mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:47:45 +00:00
AudioServer+LibAudio: Make mixing queue-based instead of buffer-based.
Each client connection now sets up an ASBufferQueue, which is basically a queue of ABuffers. This allows us to immediately start streaming the next pending buffer whenever our current buffer runs out of samples. This makes the majority of the skippiness go away for me. :^) Also get rid of the old PlayBuffer API, since we don't need it anymore.
This commit is contained in:
parent
66db6f4f92
commit
be31e2232c
7 changed files with 78 additions and 112 deletions
|
@ -16,82 +16,60 @@ ASMixer::ASMixer()
|
|||
ASMixer* mixer = (ASMixer*)context;
|
||||
mixer->mix();
|
||||
return 0;
|
||||
}, this);
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
void ASMixer::queue(ASClientConnection& client, const ABuffer& buffer)
|
||||
NonnullRefPtr<ASBufferQueue> ASMixer::create_queue(ASClientConnection& client)
|
||||
{
|
||||
ASSERT(buffer.size_in_bytes());
|
||||
CLocker lock(m_lock);
|
||||
m_pending_mixing.append(ASMixerBuffer(buffer, client));
|
||||
LOCKER(m_lock);
|
||||
auto queue = adopt(*new ASBufferQueue(client));
|
||||
m_pending_mixing.append(*queue);
|
||||
return queue;
|
||||
}
|
||||
|
||||
void ASMixer::mix()
|
||||
{
|
||||
Vector<ASMixerBuffer> active_mix_buffers;
|
||||
decltype(m_pending_mixing) active_mix_queues;
|
||||
|
||||
for (;;) {
|
||||
{
|
||||
CLocker lock(m_lock);
|
||||
active_mix_buffers.append(move(m_pending_mixing));
|
||||
LOCKER(m_lock);
|
||||
active_mix_queues.append(move(m_pending_mixing));
|
||||
}
|
||||
|
||||
// ### use a wakeup of some kind rather than this garbage
|
||||
if (active_mix_buffers.size() == 0) {
|
||||
if (active_mix_queues.size() == 0) {
|
||||
// nothing to mix yet
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
int max_size = 0;
|
||||
|
||||
for (auto& buffer : active_mix_buffers) {
|
||||
if (buffer.done)
|
||||
continue;
|
||||
ASSERT(buffer.buffer->size_in_bytes()); // zero sized buffer? how?
|
||||
max_size = max(max_size, buffer.buffer->size_in_bytes() - buffer.pos);
|
||||
}
|
||||
|
||||
// ### clear up 'done' buffers more aggressively
|
||||
if (max_size == 0) {
|
||||
active_mix_buffers.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
max_size = min(1023, max_size);
|
||||
|
||||
Vector<ASample, 1024> mixed_buffer;
|
||||
mixed_buffer.resize(max_size);
|
||||
ASample mixed_buffer[1024];
|
||||
auto mixed_buffer_length = (int)(sizeof(mixed_buffer) / sizeof(ASample));
|
||||
|
||||
// Mix the buffers together into the output
|
||||
for (auto& buffer : active_mix_buffers) {
|
||||
if (buffer.done)
|
||||
for (auto& queue : active_mix_queues) {
|
||||
if (!queue->client()) {
|
||||
queue->clear();
|
||||
continue;
|
||||
auto* samples = buffer.buffer->samples();
|
||||
auto sample_count = buffer.buffer->sample_count();
|
||||
|
||||
for (int i = 0; i < max_size && buffer.pos < sample_count; ++buffer.pos, ++i) {
|
||||
auto& mixed_sample = mixed_buffer[i];
|
||||
mixed_sample += samples[buffer.pos];
|
||||
}
|
||||
|
||||
// clear it later
|
||||
if (buffer.pos == sample_count) {
|
||||
if (buffer.m_client)
|
||||
buffer.m_client->did_finish_playing_buffer({}, buffer.buffer->shared_buffer_id());
|
||||
buffer.done = true;
|
||||
for (int i = 0; i < mixed_buffer_length; ++i) {
|
||||
auto& mixed_sample = mixed_buffer[i];
|
||||
ASample sample;
|
||||
if (!queue->get_next_sample(sample))
|
||||
break;
|
||||
mixed_sample += sample;
|
||||
}
|
||||
}
|
||||
|
||||
// output the mixed stuff to the device
|
||||
// max_size is 0 indexed, so add 1.
|
||||
const int output_buffer_byte_size = (max_size + 1) * 2 * 2;
|
||||
ASSERT(output_buffer_byte_size == 4096);
|
||||
u8 raw_buffer[4096];
|
||||
auto buffer = ByteBuffer::wrap(raw_buffer, sizeof(raw_buffer));
|
||||
BufferStream stream(buffer);
|
||||
|
||||
for (int i = 0; i < mixed_buffer.size(); ++i) {
|
||||
for (int i = 0; i < mixed_buffer_length; ++i) {
|
||||
auto& mixed_sample = mixed_buffer[i];
|
||||
mixed_sample.clip();
|
||||
|
||||
|
@ -102,20 +80,21 @@ void ASMixer::mix()
|
|||
ASSERT(!stream.at_end()); // we should have enough space for both channels in one buffer!
|
||||
out_sample = mixed_sample.right * std::numeric_limits<i16>::max();
|
||||
stream << out_sample;
|
||||
|
||||
ASSERT(!stream.at_end());
|
||||
}
|
||||
|
||||
if (stream.offset() != 0) {
|
||||
buffer.trim(stream.offset());
|
||||
m_device.write(buffer);
|
||||
mixed_buffer.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASMixer::ASMixerBuffer::ASMixerBuffer(const NonnullRefPtr<ABuffer>& buf, ASClientConnection& client)
|
||||
: buffer(buf)
|
||||
, m_client(client.make_weak_ptr())
|
||||
ASBufferQueue::ASBufferQueue(ASClientConnection& client)
|
||||
: m_client(client.make_weak_ptr())
|
||||
{
|
||||
}
|
||||
|
||||
void ASBufferQueue::enqueue(NonnullRefPtr<ABuffer>&& buffer)
|
||||
{
|
||||
m_queue.enqueue(move(buffer));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue