| // 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_ |