diff --git a/Userland/Libraries/LibAudio/WavWriter.cpp b/Userland/Libraries/LibAudio/WavWriter.cpp index 7930c6a9ca1..a771d89b6e7 100644 --- a/Userland/Libraries/LibAudio/WavWriter.cpp +++ b/Userland/Libraries/LibAudio/WavWriter.cpp @@ -5,21 +5,23 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include namespace Audio { -ErrorOr> WavWriter::create_from_file(StringView path, int sample_rate, u16 num_channels, u16 bits_per_sample) +ErrorOr> WavWriter::create_from_file(StringView path, int sample_rate, u16 num_channels, PcmSampleFormat sample_format) { - auto wav_writer = TRY(adopt_nonnull_own_or_enomem(new (nothrow) WavWriter(sample_rate, num_channels, bits_per_sample))); + auto wav_writer = TRY(adopt_nonnull_own_or_enomem(new (nothrow) WavWriter(sample_rate, num_channels, sample_format))); TRY(wav_writer->set_file(path)); return wav_writer; } -WavWriter::WavWriter(int sample_rate, u16 num_channels, u16 bits_per_sample) +WavWriter::WavWriter(int sample_rate, u16 num_channels, PcmSampleFormat sample_format) : m_sample_rate(sample_rate) , m_num_channels(num_channels) - , m_bits_per_sample(bits_per_sample) + , m_sample_format(sample_format) { } @@ -39,15 +41,32 @@ ErrorOr WavWriter::set_file(StringView path) ErrorOr WavWriter::write_samples(Span samples) { - m_data_sz += samples.size() * sizeof(Sample); - - for (auto const& sample : samples) { - // FIXME: This only really works for 16-bit samples. - u16 left = static_cast(sample.left * static_cast(1 << m_bits_per_sample)); - u16 right = static_cast(sample.right * static_cast(1 << m_bits_per_sample)); - // FIXME: This ignores endianness. - TRY(m_file->write_value(left)); - TRY(m_file->write_value(right)); + switch (m_sample_format) { + // FIXME: For non-float formats, we don't add good quantization noise, leading to possibly unpleasant quantization artifacts. + case PcmSampleFormat::Uint8: { + constexpr float scale = static_cast(NumericLimits::max()) * .5f; + for (auto const& sample : samples) { + u8 left = static_cast((sample.left + 1) * scale); + u8 right = static_cast((sample.right + 1) * scale); + TRY(m_file->write_value(left)); + TRY(m_file->write_value(right)); + } + m_data_sz += samples.size() * 2 * sizeof(u8); + break; + } + case PcmSampleFormat::Int16: { + constexpr float scale = static_cast(NumericLimits::max()); + for (auto const& sample : samples) { + u16 left = AK::convert_between_host_and_little_endian(static_cast(sample.left * scale)); + u16 right = AK::convert_between_host_and_little_endian(static_cast(sample.right * scale)); + TRY(m_file->write_value(left)); + TRY(m_file->write_value(right)); + } + m_data_sz += samples.size() * 2 * sizeof(u16); + break; + } + default: + VERIFY_NOT_REACHED(); } return {}; @@ -93,21 +112,22 @@ ErrorOr WavWriter::write_header() static u32 fmt_size = 16; TRY(m_file->write_value(fmt_size)); - // 1 for PCM - static u16 audio_format = 1; + static u16 audio_format = to_underlying(RIFF::WaveFormat::Pcm); TRY(m_file->write_value(audio_format)); TRY(m_file->write_value(m_num_channels)); TRY(m_file->write_value(m_sample_rate)); - u32 byte_rate = m_sample_rate * m_num_channels * (m_bits_per_sample / 8); + VERIFY(m_sample_format == PcmSampleFormat::Int16 || m_sample_format == PcmSampleFormat::Uint8); + u16 bits_per_sample = pcm_bits_per_sample(m_sample_format); + u32 byte_rate = m_sample_rate * m_num_channels * (bits_per_sample / 8); TRY(m_file->write_value(byte_rate)); - u16 block_align = m_num_channels * (m_bits_per_sample / 8); + u16 block_align = m_num_channels * (bits_per_sample / 8); TRY(m_file->write_value(block_align)); - TRY(m_file->write_value(m_bits_per_sample)); + TRY(m_file->write_value(bits_per_sample)); // "data" static u32 chunk_id = 0x61746164; diff --git a/Userland/Libraries/LibAudio/WavWriter.h b/Userland/Libraries/LibAudio/WavWriter.h index 8548d2f777e..a1ce01a9eb1 100644 --- a/Userland/Libraries/LibAudio/WavWriter.h +++ b/Userland/Libraries/LibAudio/WavWriter.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,8 +22,8 @@ class WavWriter { AK_MAKE_NONMOVABLE(WavWriter); public: - static ErrorOr> create_from_file(StringView path, int sample_rate = 44100, u16 num_channels = 2, u16 bits_per_sample = 16); - WavWriter(int sample_rate = 44100, u16 num_channels = 2, u16 bits_per_sample = 16); + static ErrorOr> create_from_file(StringView path, int sample_rate = 44100, u16 num_channels = 2, PcmSampleFormat sample_format = PcmSampleFormat::Int16); + WavWriter(int sample_rate = 44100, u16 num_channels = 2, PcmSampleFormat sample_format = PcmSampleFormat::Int16); ~WavWriter(); ErrorOr write_samples(Span samples); @@ -30,13 +31,13 @@ public: u32 sample_rate() const { return m_sample_rate; } u16 num_channels() const { return m_num_channels; } - u16 bits_per_sample() const { return m_bits_per_sample; } + PcmSampleFormat sample_format() const { return m_sample_format; } Core::File& file() const { return *m_file; } ErrorOr set_file(StringView path); void set_num_channels(int num_channels) { m_num_channels = num_channels; } void set_sample_rate(int sample_rate) { m_sample_rate = sample_rate; } - void set_bits_per_sample(int bits_per_sample) { m_bits_per_sample = bits_per_sample; } + void set_sample_format(PcmSampleFormat sample_format) { m_sample_format = sample_format; } private: ErrorOr write_header(); @@ -45,7 +46,7 @@ private: u32 m_sample_rate; u16 m_num_channels; - u16 m_bits_per_sample; + PcmSampleFormat m_sample_format; u32 m_data_sz { 0 }; };