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:
parent
df65ff8a1e
commit
dec9ed066d
2 changed files with 34 additions and 16 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue