blob: 45c5c3a8d4c86fc7ce135ca89f5dfe054abb359c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/lib/wav/wav_reader.h"
#include <endian.h>
#include <fcntl.h>
#include <lib/fdio/io.h>
#include <lib/syslog/cpp/macros.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <iomanip>
#include <limits>
#include <optional>
#include "src/media/audio/lib/wav/wav_internal.h"
namespace media::audio {
namespace {
// clang-format off
using wav::internal::RIFF_FOUR_CC;
using wav::internal::FMT_FOUR_CC;
using wav::internal::DATA_FOUR_CC;
using wav::internal::WAVE_FOUR_CC;
using wav::internal::RiffChunkHeader;
using wav::internal::WavHeader;
// clang-format on
} // namespace
// static
fit::result<std::unique_ptr<WavReader>, zx_status_t> WavReader::Open(const std::string& file_name) {
fbl::unique_fd fd(open(file_name.c_str(), O_RDONLY));
if (fd.get() < 0) {
FX_LOGS(WARNING) << "open failed for " << std::quoted(file_name) << ", returned " << fd.get()
<< ", errno " << errno;
return fit::error(ZX_ERR_NOT_FOUND);
}
// RIFF.
RiffChunkHeader riff_header;
if (read(fd.get(), &riff_header, sizeof(riff_header)) != sizeof(riff_header)) {
FX_LOGS(WARNING) << "read RIFF header failed for " << std::quoted(file_name) << ", errno "
<< errno;
return fit::error(ZX_ERR_IO);
}
riff_header.FixupEndianForReading();
if (riff_header.four_cc != RIFF_FOUR_CC) {
FX_LOGS(WARNING) << "read RIFF header failed for " << std::quoted(file_name) << ", four_cc 0x"
<< std::hex << riff_header.four_cc;
return fit::error(ZX_ERR_IO);
}
if (auto want = sizeof(WavHeader) + sizeof(RiffChunkHeader); riff_header.length < want) {
FX_LOGS(WARNING) << "read RIFF header failed for " << std::quoted(file_name) << ", length "
<< riff_header.length << ", expected at least " << want;
return fit::error(ZX_ERR_IO);
}
// WAVE + FMT.
WavHeader wav_header;
if (read(fd.get(), &wav_header, sizeof(wav_header)) != sizeof(wav_header)) {
FX_LOGS(WARNING) << "read WAV header failed for " << std::quoted(file_name) << ", errno "
<< errno;
return fit::error(ZX_ERR_IO);
}
wav_header.FixupEndianForReading();
if (wav_header.wave_four_cc != WAVE_FOUR_CC) {
FX_LOGS(WARNING) << "read WAV header failed for " << std::quoted(file_name)
<< ", wave_four_cc 0x" << std::hex << wav_header.wave_four_cc;
return fit::error(ZX_ERR_IO);
}
if (wav_header.fmt_four_cc != FMT_FOUR_CC) {
FX_LOGS(WARNING) << "read WAV header failed for " << std::quoted(file_name)
<< ", fmt_four_cc 0x" << std::hex << wav_header.fmt_four_cc;
return fit::error(ZX_ERR_IO);
}
if (wav_header.bits_per_sample != 8 && wav_header.bits_per_sample != 16 &&
wav_header.bits_per_sample != 32) {
FX_LOGS(WARNING) << "read WAV header failed for " << std::quoted(file_name)
<< ", unsupported bits_per_sample: " << wav_header.bits_per_sample;
return fit::error(ZX_ERR_IO);
}
// DATA.
RiffChunkHeader data_header;
if (read(fd.get(), &data_header, sizeof(data_header)) != sizeof(data_header)) {
FX_LOGS(WARNING) << "read DATA header failed for " << std::quoted(file_name) << ", errno "
<< errno;
return fit::error(ZX_ERR_IO);
}
data_header.FixupEndianForReading();
if (data_header.four_cc != DATA_FOUR_CC) {
FX_LOGS(WARNING) << "read DATA header failed for " << std::quoted(file_name) << ", four_cc 0x"
<< std::hex << riff_header.four_cc;
return fit::error(ZX_ERR_IO);
}
std::unique_ptr<WavReader> out(new WavReader);
out->sample_format_ = wav_header.sample_format();
out->channel_count_ = wav_header.channel_count;
out->frame_rate_ = wav_header.frame_rate;
out->bits_per_sample_ = wav_header.bits_per_sample;
out->length_ = data_header.length;
out->file_ = std::move(fd);
return fit::ok(std::move(out));
}
fit::result<size_t, int> WavReader::Read(void* buffer, size_t num_bytes) {
ssize_t n = read(file_.get(), buffer, num_bytes);
if (n < 0) {
return fit::error(static_cast<int>(errno));
}
return fit::ok(static_cast<size_t>(n));
}
} // namespace media::audio