1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 23:38:12 +00:00

LibAudio: Avoid reading past the end of sample data

Prior code in `WavLoader::get_more_samples()` would attempt to
read the requested number of samples without actually checking
whether that many samples were remaining in the stream.
This was the cause of an audible pop at the end of a track, due
to reading non-audio data that is sometimes at the end of a Wave file.

Now we only attempt to read up to the end of sample data, but no
further.

Also, added comments to clarify the meaning of "sample", and how it
should be independent of the number of channels.
This commit is contained in:
Nick Miller 2021-06-10 10:59:00 -07:00 committed by Ali Mohammad Pur
parent df65ff8a1e
commit dec9ed066d
2 changed files with 34 additions and 16 deletions

View file

@ -54,15 +54,23 @@ RefPtr<Buffer> 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<int>(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<int>(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<Buffer> 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;