diff --git a/Userland/Libraries/LibAudio/Loader.h b/Userland/Libraries/LibAudio/Loader.h index 34e65509d4..5075b0062a 100644 --- a/Userland/Libraries/LibAudio/Loader.h +++ b/Userland/Libraries/LibAudio/Loader.h @@ -30,10 +30,18 @@ public: virtual void seek(const int sample_index) = 0; + // total_samples() and loaded_samples() should be independent + // of the number of channels. + // + // For example, with a three-second-long, stereo, 44.1KHz audio file: + // num_channels() should return 2 + // sample_rate() should return 44100 (each channel is sampled at this rate) + // total_samples() should return 132300 (sample_rate * three seconds) virtual int loaded_samples() = 0; virtual int total_samples() = 0; virtual u32 sample_rate() = 0; virtual u16 num_channels() = 0; + virtual PcmSampleFormat pcm_format() = 0; virtual RefPtr file() = 0; }; diff --git a/Userland/Libraries/LibAudio/WavLoader.cpp b/Userland/Libraries/LibAudio/WavLoader.cpp index a2179bbafc..0896b5d57c 100644 --- a/Userland/Libraries/LibAudio/WavLoader.cpp +++ b/Userland/Libraries/LibAudio/WavLoader.cpp @@ -54,15 +54,23 @@ RefPtr WavLoaderPlugin::get_more_samples(size_t max_bytes_to_read_from_i if (!m_stream) return nullptr; - size_t bytes_per_sample = (m_num_channels * (pcm_bits_per_sample(m_sample_format) / 8)); + int remaining_samples = m_total_samples - m_loaded_samples; + if (remaining_samples <= 0) { + return nullptr; + } - // Might truncate if not evenly divisible - size_t samples_to_read = static_cast(max_bytes_to_read_from_input) / bytes_per_sample; + // One "sample" contains data from all channels. + // In the Wave spec, this is also called a block. + size_t bytes_per_sample = m_num_channels * pcm_bits_per_sample(m_sample_format) / 8; + + // Might truncate if not evenly divisible by the sample size + int max_samples_to_read = static_cast(max_bytes_to_read_from_input) / bytes_per_sample; + int samples_to_read = min(max_samples_to_read, remaining_samples); size_t bytes_to_read = samples_to_read * bytes_per_sample; - dbgln_if(AWAVLOADER_DEBUG, "Read {} bytes ({} samples) WAV with num_channels {} sample rate {}, " + dbgln_if(AWAVLOADER_DEBUG, "Read {} bytes WAV with num_channels {} sample rate {}, " "bits per sample {}, sample format {}", - bytes_to_read, samples_to_read, m_num_channels, m_sample_rate, + bytes_to_read, m_num_channels, m_sample_rate, pcm_bits_per_sample(m_sample_format), sample_format_name(m_sample_format)); ByteBuffer sample_data = ByteBuffer::create_zeroed(bytes_to_read); @@ -79,7 +87,6 @@ RefPtr WavLoaderPlugin::get_more_samples(size_t max_bytes_to_read_from_i // m_loaded_samples should contain the amount of actually loaded samples m_loaded_samples += samples_to_read; - m_loaded_samples = min(m_total_samples, m_loaded_samples); return buffer; } @@ -89,15 +96,16 @@ void WavLoaderPlugin::seek(const int sample_index) if (sample_index < 0 || sample_index >= m_total_samples) return; - m_loaded_samples = sample_index; - size_t byte_position = m_byte_offset_of_data_samples + sample_index * m_num_channels * (pcm_bits_per_sample(m_sample_format) / 8); + size_t sample_offset = m_byte_offset_of_data_samples + (sample_index * m_num_channels * (pcm_bits_per_sample(m_sample_format) / 8)); - // AK::InputStream does not define seek. + // AK::InputStream does not define seek, hence the special-cases for file and stream. if (m_file) { - m_file->seek(byte_position); + m_file->seek(sample_offset); } else { - m_memory_stream->seek(byte_position); + m_memory_stream->seek(sample_offset); } + + m_loaded_samples = sample_index; } // Specification reference: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html @@ -180,11 +188,11 @@ bool WavLoaderPlugin::parse_header() read_u32(); CHECK_OK("Data rate"); - read_u16(); + u16 block_size_bytes = read_u16(); CHECK_OK("Block size"); u16 bits_per_sample = read_u16(); - CHECK_OK("Bits per sample"); // incomplete read check + CHECK_OK("Bits per sample"); if (audio_format == WAVE_FORMAT_EXTENSIBLE) { ok = ok && (fmt_size == 40); @@ -228,6 +236,9 @@ bool WavLoaderPlugin::parse_header() } } + ok = ok && (block_size_bytes == (m_num_channels * (bits_per_sample / 8))); + CHECK_OK("Block size sanity check"); + dbgln_if(AWAVLOADER_DEBUG, "WAV format {} at {} bit, {} channels, rate {}Hz ", sample_format_name(m_sample_format), pcm_bits_per_sample(m_sample_format), m_num_channels, m_sample_rate); @@ -262,12 +273,11 @@ bool WavLoaderPlugin::parse_header() ok = ok && data_sz < maximum_wav_size; CHECK_OK("Data was too large"); - int bytes_per_sample = (bits_per_sample / 8) * m_num_channels; - m_total_samples = data_sz / bytes_per_sample; + m_total_samples = data_sz / block_size_bytes; dbgln_if(AWAVLOADER_DEBUG, "WAV data size {}, bytes per sample {}, total samples {}", data_sz, - bytes_per_sample, + block_size_bytes, m_total_samples); m_byte_offset_of_data_samples = bytes_read;