mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 12:47:35 +00:00
SoundPlayer: Handle any input file sample rate
This commit addresses two issues: 1. If you play a 96 KHz Wave file, the slider position is incorrect, because it is assumed all files are 44.1 KHz. 2. For high-bitrate files, there are audio dropouts due to not buffering enough audio data. Issue 1 is addressed by scaling the number of played samples by the ratio between the source and destination sample rates. Issue 2 is addressed by buffering a certain number of milliseconds worth of audio data (instead of a fixed number of bytes). This makes the the buffer size independent of the source sample rate. Some of the code is redesigned to be simpler. The code that did the book-keeping of which buffers need to be loaded and which have been already played has been removed. Instead, we enqueue a new buffer based on a low watermark of samples remaining in the audio server queue. Other small fixes include: 1. Disable the stop button when playback is finished. 2. Remove hard-coded instances of 44100. 3. Update the GUI every 50 ms (was 100), which improves visualizations.
This commit is contained in:
parent
34a3d08e65
commit
9a2c80c791
7 changed files with 53 additions and 79 deletions
|
@ -9,7 +9,7 @@
|
|||
PlaybackManager::PlaybackManager(NonnullRefPtr<Audio::ClientConnection> connection)
|
||||
: m_connection(connection)
|
||||
{
|
||||
m_timer = Core::Timer::construct(100, [&]() {
|
||||
m_timer = Core::Timer::construct(PlaybackManager::update_rate_ms, [&]() {
|
||||
if (!m_loader)
|
||||
return;
|
||||
next_buffer();
|
||||
|
@ -27,8 +27,10 @@ void PlaybackManager::set_loader(NonnullRefPtr<Audio::Loader>&& loader)
|
|||
m_loader = loader;
|
||||
if (m_loader) {
|
||||
m_total_length = m_loader->total_samples() / static_cast<float>(m_loader->sample_rate());
|
||||
m_device_samples_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_device_sample_rate;
|
||||
u32 source_samples_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_loader->sample_rate();
|
||||
m_source_buffer_size_bytes = source_samples_per_buffer * m_loader->num_channels() * m_loader->bits_per_sample() / 8;
|
||||
m_timer->start();
|
||||
load_next_buffer();
|
||||
} else {
|
||||
m_timer->stop();
|
||||
}
|
||||
|
@ -38,11 +40,8 @@ void PlaybackManager::stop()
|
|||
{
|
||||
set_paused(true);
|
||||
m_connection->clear_buffer(true);
|
||||
m_buffers.clear();
|
||||
m_last_seek = 0;
|
||||
m_next_buffer = nullptr;
|
||||
m_current_buffer = nullptr;
|
||||
m_next_ptr = 0;
|
||||
|
||||
if (m_loader)
|
||||
m_loader->reset();
|
||||
|
@ -68,10 +67,7 @@ void PlaybackManager::seek(const int position)
|
|||
set_paused(true);
|
||||
|
||||
m_connection->clear_buffer(true);
|
||||
m_next_buffer = nullptr;
|
||||
m_current_buffer = nullptr;
|
||||
m_next_ptr = 0;
|
||||
m_buffers.clear();
|
||||
m_loader->seek(position);
|
||||
|
||||
if (!paused_state)
|
||||
|
@ -83,51 +79,8 @@ void PlaybackManager::pause()
|
|||
set_paused(true);
|
||||
}
|
||||
|
||||
void PlaybackManager::remove_dead_buffers()
|
||||
{
|
||||
int id = m_connection->get_playing_buffer();
|
||||
int current_id = -1;
|
||||
if (m_current_buffer)
|
||||
current_id = m_current_buffer->id();
|
||||
|
||||
if (id >= 0 && id != current_id) {
|
||||
while (!m_buffers.is_empty()) {
|
||||
--m_next_ptr;
|
||||
auto buffer = m_buffers.take_first();
|
||||
|
||||
if (buffer->id() == id) {
|
||||
m_current_buffer = buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackManager::load_next_buffer()
|
||||
{
|
||||
if (m_buffers.size() < 10) {
|
||||
for (int i = 0; i < 20 && m_loader->loaded_samples() < m_loader->total_samples(); i++) {
|
||||
auto buffer = m_loader->get_more_samples(PLAYBACK_MANAGER_BUFFER_SIZE);
|
||||
if (buffer) {
|
||||
m_buffers.append(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_next_ptr < m_buffers.size()) {
|
||||
m_next_buffer = m_buffers.at(m_next_ptr++);
|
||||
if (on_load_sample_buffer)
|
||||
on_load_sample_buffer(*m_next_buffer);
|
||||
} else {
|
||||
m_next_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackManager::set_paused(bool paused)
|
||||
{
|
||||
if (!m_next_buffer && m_loader)
|
||||
load_next_buffer();
|
||||
|
||||
m_paused = paused;
|
||||
m_connection->set_paused(paused);
|
||||
}
|
||||
|
@ -146,22 +99,24 @@ void PlaybackManager::next_buffer()
|
|||
{
|
||||
if (on_update)
|
||||
on_update();
|
||||
|
||||
if (m_paused)
|
||||
return;
|
||||
|
||||
remove_dead_buffers();
|
||||
if (!m_next_buffer) {
|
||||
if (!m_connection->get_remaining_samples() && !m_paused) {
|
||||
stop();
|
||||
if (on_finished_playing)
|
||||
on_finished_playing();
|
||||
}
|
||||
u32 audio_server_remaining_samples = m_connection->get_remaining_samples();
|
||||
bool all_samples_loaded = (m_loader->loaded_samples() >= m_loader->total_samples());
|
||||
bool audio_server_done = (audio_server_remaining_samples == 0);
|
||||
|
||||
if (all_samples_loaded && audio_server_done) {
|
||||
stop();
|
||||
if (on_finished_playing)
|
||||
on_finished_playing();
|
||||
return;
|
||||
}
|
||||
|
||||
bool enqueued = m_connection->try_enqueue(*m_next_buffer);
|
||||
if (!enqueued)
|
||||
return;
|
||||
|
||||
load_next_buffer();
|
||||
if (audio_server_remaining_samples < m_device_samples_per_buffer) {
|
||||
m_current_buffer = m_loader->get_more_samples(m_source_buffer_size_bytes);
|
||||
if (m_current_buffer)
|
||||
m_connection->enqueue(*m_current_buffer);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue