blob: 3eb04cf02070c1eec5ad293c503863d5fc2d7992 [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.
#ifndef SRC_MEDIA_AUDIO_LIB_WAV_WAV_INTERNAL_H_
#define SRC_MEDIA_AUDIO_LIB_WAV_WAV_INTERNAL_H_
#include <fuchsia/media/cpp/fidl.h>
#include <stdint.h>
#include <zircon/types.h>
namespace media::audio::wav::internal {
//
// Struct and const definitions related to RIFF file format
//
// Encode a 32-bit 'fourcc' value from these 4 byte values
inline constexpr uint32_t make_fourcc(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
return (static_cast<uint32_t>(d) << 24) | (static_cast<uint32_t>(c) << 16) |
(static_cast<uint32_t>(b) << 8) | static_cast<uint32_t>(a);
}
// clang-format off
constexpr uint32_t RIFF_FOUR_CC = make_fourcc('R', 'I', 'F', 'F');
constexpr uint32_t WAVE_FOUR_CC = make_fourcc('W', 'A', 'V', 'E');
constexpr uint32_t FMT_FOUR_CC = make_fourcc('f', 'm', 't', ' ');
constexpr uint32_t DATA_FOUR_CC = make_fourcc('d', 'a', 't', 'a');
constexpr uint16_t FORMAT_LPCM = 0x0001;
constexpr uint16_t FORMAT_FLOAT = 0x0003;
// clang-format on
// The RIFF file specification (and the child specification for WAV content)
// defines the layout and contents of WAV audio files.
//
// RIFF files consist of so-called _chunks_ (self-describing sections of the
// file). These files begin with a RIFF header chunk that describes the primary
// format of the file contents, followed by the data itself (in a chunk of its
// own). Additional chunks may also be present, containing metadata and/or other
// information to support optional features. Because all chunks include a length
// field, any unknown chunks can be safely skipped by file readers.
//
// The WAV file format specifies an initial 'RIFF' chunk of type 'WAVE' (length
// 24), followed by two required Subchunks: 'fmt ' (length 24) and 'data'
// (length 8 + the size of the subsequent audio data). Audio data should
// immediately follow these first 8 bytes of the 'data' subchunk. Once the
// entirety of audio data has been written into the file, the 'length' field for
// the 'data' subchunk should be updated with the number of bytes of audio.
// Likewise, the overall length for the parent 'RIFF' chunk (which conceptually
// contains the two 'fmt ' and 'data' subchunks) must be updated at this point,
// to describe its total size (including subchunk headers and the audio data).
// Thus, although all audio data follows the file headers, we must update the
// headers once all audio has been written.
//
// ** Note, lest our RiffChunkHeader struct definition mislead the uninformed **
// These struct definitions actually conceptually relocate the final 32-bit
// value of the initial RIFF chunk into the subsequent 'fmt ' subchunk instead.
// Because the sequence of fields is maintained, this does not create a problem.
// We do this so that we can reuse our RIFF struct definition for the 'data'
// subchunk as well.
struct __PACKED RiffChunkHeader {
uint32_t four_cc;
uint32_t length = 0;
// RIFF files are stored in little-endian, regardless of host-architecture.
void FixupEndianForWriting() {
four_cc = htole32(four_cc);
length = htole32(length);
}
void FixupEndianForReading() {
four_cc = letoh32(four_cc);
length = letoh32(length);
}
};
// As mentioned above, the WAVE_FOUR_CC is actually a menber of the previous
// RIFF chunk, but we include it here so that we can manage our parent 'RIFF'
// chunk and our 'data' subchunk with common code.
struct __PACKED WavHeader {
uint32_t wave_four_cc = WAVE_FOUR_CC;
uint32_t fmt_four_cc = FMT_FOUR_CC;
uint32_t fmt_chunk_len = sizeof(WavHeader) - offsetof(WavHeader, format);
uint16_t format = 0;
uint16_t channel_count = 0;
uint32_t frame_rate = 0;
uint32_t average_byte_rate = 0;
uint16_t frame_size = 0;
uint16_t bits_per_sample = 0;
// RIFF files are stored in little-endian, regardless of host-architecture.
void FixupEndianForWriting() {
// clang-format off
wave_four_cc = htole32(wave_four_cc);
fmt_four_cc = htole32(fmt_four_cc);
fmt_chunk_len = htole32(fmt_chunk_len);
format = htole16(format);
channel_count = htole16(channel_count);
frame_rate = htole32(frame_rate);
average_byte_rate = htole32(average_byte_rate);
frame_size = htole16(frame_size);
bits_per_sample = htole16(bits_per_sample);
// clang-format on
}
void FixupEndianForReading() {
// clang-format off
wave_four_cc = letoh32(wave_four_cc);
fmt_four_cc = letoh32(fmt_four_cc);
fmt_chunk_len = letoh32(fmt_chunk_len);
format = letoh16(format);
channel_count = letoh16(channel_count);
frame_rate = letoh32(frame_rate);
average_byte_rate = letoh32(average_byte_rate);
frame_size = letoh16(frame_size);
bits_per_sample = letoh16(bits_per_sample);
// clang-format on
}
void set_format(fuchsia::media::AudioSampleFormat f) {
format = (f == fuchsia::media::AudioSampleFormat::FLOAT) ? FORMAT_FLOAT : FORMAT_LPCM;
}
fuchsia::media::AudioSampleFormat sample_format() const {
if (format == FORMAT_FLOAT) {
return fuchsia::media::AudioSampleFormat::FLOAT;
}
switch (bits_per_sample) {
case 8:
return fuchsia::media::AudioSampleFormat::UNSIGNED_8;
case 16:
return fuchsia::media::AudioSampleFormat::SIGNED_16;
case 32:
return fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32;
}
FX_CHECK(false) << "format " << format << " bits_per_sample " << bits_per_sample;
__UNREACHABLE;
}
};
} // namespace media::audio::wav::internal
#endif // SRC_MEDIA_AUDIO_LIB_WAV_WAV_INTERNAL_H_