mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:27:43 +00:00
Audio: Make ABuffer sit on top of a SharedBuffer.
This allows us to carry the same buffer all the way from the WAV loader to the AudioServer mixer. This alleviates some of the stutter, but there's still a noticeable skip when switching buffers. We're gonna need to do better. :^)
This commit is contained in:
parent
b805f112c2
commit
5e01dde7b1
6 changed files with 38 additions and 31 deletions
|
@ -4,6 +4,7 @@
|
||||||
#include <AK/ByteBuffer.h>
|
#include <AK/ByteBuffer.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibC/SharedBuffer.h>
|
||||||
|
|
||||||
// A single sample in an audio buffer.
|
// A single sample in an audio buffer.
|
||||||
// Values are floating point, and should range from -1.0 to +1.0
|
// Values are floating point, and should range from -1.0 to +1.0
|
||||||
|
@ -57,17 +58,29 @@ public:
|
||||||
{
|
{
|
||||||
return adopt(*new ABuffer(move(samples)));
|
return adopt(*new ABuffer(move(samples)));
|
||||||
}
|
}
|
||||||
|
static NonnullRefPtr<ABuffer> create_with_shared_buffer(NonnullRefPtr<SharedBuffer>&& buffer)
|
||||||
|
{
|
||||||
|
return adopt(*new ABuffer(move(buffer)));
|
||||||
|
}
|
||||||
|
|
||||||
const Vector<ASample>& samples() const { return m_samples; }
|
const ASample* samples() const { return (const ASample*)data(); }
|
||||||
Vector<ASample>& samples() { return m_samples; }
|
int sample_count() const { return m_buffer->size() / (int)sizeof(ASample); }
|
||||||
const void* data() const { return m_samples.data(); }
|
const void* data() const { return m_buffer->data(); }
|
||||||
int size_in_bytes() const { return m_samples.size() * sizeof(ASample); }
|
int size_in_bytes() const { return m_buffer->size(); }
|
||||||
|
int shared_buffer_id() const { return m_buffer->shared_buffer_id(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ABuffer(Vector<ASample>&& samples)
|
explicit ABuffer(Vector<ASample>&& samples)
|
||||||
: m_samples(move(samples))
|
: m_buffer(*SharedBuffer::create_with_size(samples.size() * sizeof(ASample)))
|
||||||
|
{
|
||||||
|
memcpy(m_buffer->data(), samples.data(), samples.size() * sizeof(ASample));
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ABuffer(NonnullRefPtr<SharedBuffer>&& buffer)
|
||||||
|
: m_buffer(move(buffer))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<ASample> m_samples;
|
NonnullRefPtr<SharedBuffer> m_buffer;
|
||||||
|
int m_sample_count { 0 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -254,5 +254,5 @@ RefPtr<ABuffer> ABuffer::from_pcm_data(ByteBuffer& data, int num_channels, int b
|
||||||
// don't belong.
|
// don't belong.
|
||||||
ASSERT(!stream.handle_read_failure());
|
ASSERT(!stream.handle_read_failure());
|
||||||
|
|
||||||
return adopt(*new ABuffer(move(fdata)));
|
return ABuffer::create_with_samples(move(fdata));
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,16 +40,11 @@ bool ASClientConnection::handle_message(const ASAPI_ClientMessage& message, cons
|
||||||
break;
|
break;
|
||||||
case ASAPI_ClientMessage::Type::PlayBuffer: {
|
case ASAPI_ClientMessage::Type::PlayBuffer: {
|
||||||
// ### ensure that the size is that of a Vector<ASample>
|
// ### ensure that the size is that of a Vector<ASample>
|
||||||
Vector<ASample> samples;
|
|
||||||
|
|
||||||
{
|
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.play_buffer.buffer_id);
|
||||||
const auto& shared_buf = SharedBuffer::create_from_shared_buffer_id(message.play_buffer.buffer_id);
|
if (!shared_buffer) {
|
||||||
if (!shared_buf) {
|
did_misbehave();
|
||||||
did_misbehave();
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
samples.resize(shared_buf->size() / sizeof(ASample));
|
|
||||||
memcpy(samples.data(), shared_buf->data(), shared_buf->size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we no longer need the buffer, so acknowledge that it's playing
|
// we no longer need the buffer, so acknowledge that it's playing
|
||||||
|
@ -59,7 +54,7 @@ bool ASClientConnection::handle_message(const ASAPI_ClientMessage& message, cons
|
||||||
reply.playing_buffer.buffer_id = message.play_buffer.buffer_id;
|
reply.playing_buffer.buffer_id = message.play_buffer.buffer_id;
|
||||||
post_message(reply);
|
post_message(reply);
|
||||||
|
|
||||||
m_mixer.queue(*this, ABuffer::create_with_samples(move(samples)), message.play_buffer.buffer_id);
|
m_mixer.queue(*this, ABuffer::create_with_shared_buffer(*shared_buffer));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ASAPI_ClientMessage::Type::Invalid:
|
case ASAPI_ClientMessage::Type::Invalid:
|
||||||
|
|
|
@ -20,11 +20,11 @@ ASMixer::ASMixer()
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASMixer::queue(ASClientConnection& client, const ABuffer& buffer, int buffer_id)
|
void ASMixer::queue(ASClientConnection& client, const ABuffer& buffer)
|
||||||
{
|
{
|
||||||
ASSERT(buffer.size_in_bytes());
|
ASSERT(buffer.size_in_bytes());
|
||||||
CLocker lock(m_lock);
|
CLocker lock(m_lock);
|
||||||
m_pending_mixing.append(ASMixerBuffer(buffer, client, buffer_id));
|
m_pending_mixing.append(ASMixerBuffer(buffer, client));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASMixer::mix()
|
void ASMixer::mix()
|
||||||
|
@ -68,19 +68,20 @@ void ASMixer::mix()
|
||||||
for (auto& buffer : active_mix_buffers) {
|
for (auto& buffer : active_mix_buffers) {
|
||||||
if (buffer.done)
|
if (buffer.done)
|
||||||
continue;
|
continue;
|
||||||
auto& samples = buffer.buffer->samples();
|
auto* samples = buffer.buffer->samples();
|
||||||
|
auto sample_count = buffer.buffer->sample_count();
|
||||||
|
|
||||||
for (int i = 0; i < max_size && buffer.pos < samples.size(); ++buffer.pos, ++i) {
|
for (int i = 0; i < max_size && buffer.pos < sample_count; ++buffer.pos, ++i) {
|
||||||
auto& mixed_sample = mixed_buffer[i];
|
auto& mixed_sample = mixed_buffer[i];
|
||||||
mixed_sample += samples[buffer.pos];
|
mixed_sample += samples[buffer.pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear it later
|
// clear it later
|
||||||
if (buffer.pos == samples.size()) {
|
if (buffer.pos == sample_count) {
|
||||||
if (buffer.m_buffer_id && buffer.m_client) {
|
if (buffer.m_client) {
|
||||||
ASAPI_ServerMessage reply;
|
ASAPI_ServerMessage reply;
|
||||||
reply.type = ASAPI_ServerMessage::Type::FinishedPlayingBuffer;
|
reply.type = ASAPI_ServerMessage::Type::FinishedPlayingBuffer;
|
||||||
reply.playing_buffer.buffer_id = buffer.m_buffer_id;
|
reply.playing_buffer.buffer_id = buffer.buffer->shared_buffer_id();
|
||||||
buffer.m_client->post_message(reply);
|
buffer.m_client->post_message(reply);
|
||||||
}
|
}
|
||||||
buffer.done = true;
|
buffer.done = true;
|
||||||
|
@ -118,9 +119,8 @@ void ASMixer::mix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASMixer::ASMixerBuffer::ASMixerBuffer(const NonnullRefPtr<ABuffer>& buf, ASClientConnection& client, int buffer_id)
|
ASMixer::ASMixerBuffer::ASMixerBuffer(const NonnullRefPtr<ABuffer>& buf, ASClientConnection& client)
|
||||||
: buffer(buf)
|
: buffer(buf)
|
||||||
, m_client(client.make_weak_ptr())
|
, m_client(client.make_weak_ptr())
|
||||||
, m_buffer_id(buffer_id)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,15 @@ class ASMixer : public RefCounted<ASMixer> {
|
||||||
public:
|
public:
|
||||||
ASMixer();
|
ASMixer();
|
||||||
|
|
||||||
void queue(ASClientConnection&, const ABuffer&, int buffer_id);
|
void queue(ASClientConnection&, const ABuffer&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ASMixerBuffer {
|
struct ASMixerBuffer {
|
||||||
ASMixerBuffer(const NonnullRefPtr<ABuffer>&, ASClientConnection&, int buffer_id = -1);
|
ASMixerBuffer(const NonnullRefPtr<ABuffer>&, ASClientConnection&);
|
||||||
NonnullRefPtr<ABuffer> buffer;
|
NonnullRefPtr<ABuffer> buffer;
|
||||||
int pos { 0 };
|
int pos { 0 };
|
||||||
bool done { false };
|
bool done { false };
|
||||||
WeakPtr<ASClientConnection> m_client;
|
WeakPtr<ASClientConnection> m_client;
|
||||||
int m_buffer_id { -1 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<ASMixerBuffer> m_pending_mixing;
|
Vector<ASMixerBuffer> m_pending_mixing;
|
||||||
|
|
|
@ -24,7 +24,7 @@ int main(int argc, char **argv)
|
||||||
if (!samples) {
|
if (!samples) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printf("Playing %d sample(s)\n", samples->samples().size());
|
printf("Playing %d sample(s)\n", samples->sample_count());
|
||||||
a_conn.play(*samples, true);
|
a_conn.play(*samples, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue