summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibAudio/ABuffer.h47
-rw-r--r--Libraries/LibAudio/AClientConnection.cpp4
-rw-r--r--Libraries/LibAudio/AWavLoader.cpp83
-rw-r--r--Libraries/LibAudio/AWavLoader.h2
-rw-r--r--Servers/AudioServer/ASClientConnection.cpp2
-rw-r--r--Servers/AudioServer/AudioServer.ipc2
6 files changed, 78 insertions, 62 deletions
diff --git a/Libraries/LibAudio/ABuffer.h b/Libraries/LibAudio/ABuffer.h
index 287c0a738f..237ec2b209 100644
--- a/Libraries/LibAudio/ABuffer.h
+++ b/Libraries/LibAudio/ABuffer.h
@@ -1,6 +1,5 @@
#pragma once
-#include <AK/RefCounted.h>
#include <AK/ByteBuffer.h>
#include <AK/Types.h>
#include <AK/Vector.h>
@@ -12,19 +11,22 @@ struct ASample {
ASample()
: left(0)
, right(0)
- {}
+ {
+ }
// For mono
ASample(float left)
: left(left)
, right(left)
- {}
+ {
+ }
// For stereo
ASample(float left, float right)
: left(left)
, right(right)
- {}
+ {
+ }
void clip()
{
@@ -57,38 +59,57 @@ struct ASample {
float right;
};
+// Small helper to resample from one playback rate to another
+// This isn't really "smart", in that we just insert (or drop) samples.
+// Should do better...
+class AResampleHelper {
+public:
+ AResampleHelper(float source, float target);
+
+ void process_sample(float sample_l, float sample_r);
+ bool read_sample(float& next_l, float& next_r);
+
+private:
+ const float m_ratio;
+ float m_current_ratio { 0 };
+ float m_last_sample_l { 0 };
+ float m_last_sample_r { 0 };
+};
+
// A buffer of audio samples, normalized to 44100hz.
class ABuffer : public RefCounted<ABuffer> {
public:
- static RefPtr<ABuffer> from_pcm_data(ByteBuffer& data, int num_channels, int bits_per_sample, int source_rate);
+ static RefPtr<ABuffer> from_pcm_data(ByteBuffer& data, AResampleHelper& resampler, int num_channels, int bits_per_sample);
static NonnullRefPtr<ABuffer> create_with_samples(Vector<ASample>&& samples)
{
return adopt(*new ABuffer(move(samples)));
}
- static NonnullRefPtr<ABuffer> create_with_shared_buffer(NonnullRefPtr<SharedBuffer>&& buffer)
+ static NonnullRefPtr<ABuffer> create_with_shared_buffer(NonnullRefPtr<SharedBuffer>&& buffer, int sample_count)
{
- return adopt(*new ABuffer(move(buffer)));
+ return adopt(*new ABuffer(move(buffer), sample_count));
}
const ASample* samples() const { return (const ASample*)data(); }
- int sample_count() const { return m_buffer->size() / (int)sizeof(ASample); }
+ int sample_count() const { return m_sample_count; }
const void* data() const { return m_buffer->data(); }
- int size_in_bytes() const { return m_buffer->size(); }
+ int size_in_bytes() const { return m_sample_count * (int)sizeof(ASample); }
int shared_buffer_id() const { return m_buffer->shared_buffer_id(); }
SharedBuffer& shared_buffer() { return *m_buffer; }
private:
explicit ABuffer(Vector<ASample>&& samples)
- : m_buffer(*SharedBuffer::create_with_size(samples.size() * sizeof(ASample)))
+ : m_buffer(*SharedBuffer::create_with_size(samples.size() * sizeof(ASample))),
+ m_sample_count(samples.size())
{
memcpy(m_buffer->data(), samples.data(), samples.size() * sizeof(ASample));
}
- explicit ABuffer(NonnullRefPtr<SharedBuffer>&& buffer)
- : m_buffer(move(buffer))
+ explicit ABuffer(NonnullRefPtr<SharedBuffer>&& buffer, int sample_count)
+ : m_buffer(move(buffer)),
+ m_sample_count(sample_count)
{
}
NonnullRefPtr<SharedBuffer> m_buffer;
- int m_sample_count { 0 };
+ const int m_sample_count;
};
diff --git a/Libraries/LibAudio/AClientConnection.cpp b/Libraries/LibAudio/AClientConnection.cpp
index 3d9c46405d..0ff43ec198 100644
--- a/Libraries/LibAudio/AClientConnection.cpp
+++ b/Libraries/LibAudio/AClientConnection.cpp
@@ -18,7 +18,7 @@ void AClientConnection::enqueue(const ABuffer& buffer)
{
for (;;) {
const_cast<ABuffer&>(buffer).shared_buffer().share_with(server_pid());
- auto response = send_sync<AudioServer::EnqueueBuffer>(buffer.shared_buffer_id());
+ auto response = send_sync<AudioServer::EnqueueBuffer>(buffer.shared_buffer_id(), buffer.sample_count());
if (response->success())
break;
sleep(1);
@@ -28,7 +28,7 @@ void AClientConnection::enqueue(const ABuffer& buffer)
bool AClientConnection::try_enqueue(const ABuffer& buffer)
{
const_cast<ABuffer&>(buffer).shared_buffer().share_with(server_pid());
- auto response = send_sync<AudioServer::EnqueueBuffer>(buffer.shared_buffer_id());
+ auto response = send_sync<AudioServer::EnqueueBuffer>(buffer.shared_buffer_id(), buffer.sample_count());
return response->success();
}
diff --git a/Libraries/LibAudio/AWavLoader.cpp b/Libraries/LibAudio/AWavLoader.cpp
index 6315a8c3c2..d64a65ddc6 100644
--- a/Libraries/LibAudio/AWavLoader.cpp
+++ b/Libraries/LibAudio/AWavLoader.cpp
@@ -1,5 +1,4 @@
#include <AK/BufferStream.h>
-#include <LibAudio/ABuffer.h>
#include <LibAudio/AWavLoader.h>
#include <LibCore/CFile.h>
#include <LibCore/CIODeviceStreamReader.h>
@@ -14,6 +13,7 @@ AWavLoader::AWavLoader(const StringView& path)
}
parse_header();
+ m_resampler = make<AResampleHelper>(m_sample_rate, 44100);
}
RefPtr<ABuffer> AWavLoader::get_more_samples(size_t max_bytes_to_read_from_input)
@@ -26,7 +26,7 @@ RefPtr<ABuffer> AWavLoader::get_more_samples(size_t max_bytes_to_read_from_input
if (raw_samples.is_empty())
return nullptr;
- auto buffer = ABuffer::from_pcm_data(raw_samples, m_num_channels, m_bits_per_sample, m_sample_rate);
+ auto buffer = ABuffer::from_pcm_data(raw_samples, *m_resampler, m_num_channels, m_bits_per_sample);
m_loaded_samples += buffer->sample_count();
return buffer;
}
@@ -80,7 +80,7 @@ bool AWavLoader::parse_header()
u16 audio_format;
stream >> audio_format;
- CHECK_OK("Audio format"); // incomplete read check
+ CHECK_OK("Audio format"); // incomplete read check
ok = ok && audio_format == 1; // WAVE_FORMAT_PCM
ASSERT(audio_format == 1);
CHECK_OK("Audio format"); // value check
@@ -137,64 +137,62 @@ bool AWavLoader::parse_header()
return true;
}
-// Small helper to resample from one playback rate to another
-// This isn't really "smart", in that we just insert (or drop) samples.
-// Should do better...
-class AResampleHelper {
-public:
- AResampleHelper(float source, float target);
- bool read_sample();
- void prepare();
-
-private:
- const float m_ratio;
- float m_current_ratio { 0 };
-};
-
AResampleHelper::AResampleHelper(float source, float target)
: m_ratio(source / target)
{
}
-void AResampleHelper::prepare()
+void AResampleHelper::process_sample(float sample_l, float sample_r)
{
- m_current_ratio += m_ratio;
+ m_last_sample_l = sample_l;
+ m_last_sample_r = sample_r;
+ m_current_ratio += 1;
}
-bool AResampleHelper::read_sample()
+bool AResampleHelper::read_sample(float& next_l, float& next_r)
{
- if (m_current_ratio > 1) {
- m_current_ratio--;
+ if (m_current_ratio > 0) {
+ m_current_ratio -= m_ratio;
+ next_l = m_last_sample_l;
+ next_r = m_last_sample_r;
return true;
}
return false;
}
-template<typename SampleReader>
-static void read_samples_from_stream(BufferStream& stream, SampleReader read_sample, Vector<ASample>& samples, int num_channels, int source_rate)
+template <typename SampleReader>
+static void read_samples_from_stream(BufferStream& stream, SampleReader read_sample, Vector<ASample>& samples, AResampleHelper& resampler, int num_channels)
{
- AResampleHelper resampler(source_rate, 44100);
float norm_l = 0;
float norm_r = 0;
+
switch (num_channels) {
case 1:
- while (!stream.handle_read_failure()) {
- resampler.prepare();
- while (resampler.read_sample()) {
- norm_l = read_sample(stream);
+ for(;;) {
+ while (resampler.read_sample(norm_l, norm_r)) {
+ samples.append(ASample(norm_l));
+ }
+ norm_l = read_sample(stream);
+
+ if (stream.handle_read_failure()) {
+ break;
}
- samples.append(ASample(norm_l));
+ resampler.process_sample(norm_l, norm_r);
}
break;
case 2:
- while (!stream.handle_read_failure()) {
- resampler.prepare();
- while (resampler.read_sample()) {
- norm_l = read_sample(stream);
- norm_r = read_sample(stream);
+ for(;;) {
+ while (resampler.read_sample(norm_l, norm_r)) {
+ samples.append(ASample(norm_l, norm_r));
+ }
+ norm_l = read_sample(stream);
+ norm_r = read_sample(stream);
+
+ if (stream.handle_read_failure()) {
+ break;
}
- samples.append(ASample(norm_l, norm_r));
+ resampler.process_sample(norm_l, norm_r);
}
break;
default:
@@ -236,7 +234,7 @@ static float read_norm_sample_8(BufferStream& stream)
// ### can't const this because BufferStream is non-const
// perhaps we need a reading class separate from the writing one, that can be
// entirely consted.
-RefPtr<ABuffer> ABuffer::from_pcm_data(ByteBuffer& data, int num_channels, int bits_per_sample, int source_rate)
+RefPtr<ABuffer> ABuffer::from_pcm_data(ByteBuffer& data, AResampleHelper& resampler, int num_channels, int bits_per_sample)
{
BufferStream stream(data);
Vector<ASample> fdata;
@@ -248,13 +246,13 @@ RefPtr<ABuffer> ABuffer::from_pcm_data(ByteBuffer& data, int num_channels, int b
switch (bits_per_sample) {
case 8:
- read_samples_from_stream(stream, read_norm_sample_8, fdata, num_channels, source_rate);
+ read_samples_from_stream(stream, read_norm_sample_8, fdata, resampler, num_channels);
break;
case 16:
- read_samples_from_stream(stream, read_norm_sample_16, fdata, num_channels, source_rate);
+ read_samples_from_stream(stream, read_norm_sample_16, fdata, resampler, num_channels);
break;
case 24:
- read_samples_from_stream(stream, read_norm_sample_24, fdata, num_channels, source_rate);
+ read_samples_from_stream(stream, read_norm_sample_24, fdata, resampler, num_channels);
break;
default:
ASSERT_NOT_REACHED();
@@ -265,10 +263,5 @@ RefPtr<ABuffer> ABuffer::from_pcm_data(ByteBuffer& data, int num_channels, int b
// don't belong.
ASSERT(!stream.handle_read_failure());
- // HACK: This is a total hack to remove an unnecessary sample at the end of the buffer.
- // FIXME: Don't generate the extra sample... :^)
- for (int i = 0; i < 1; ++i)
- fdata.take_last();
-
return ABuffer::create_with_samples(move(fdata));
}
diff --git a/Libraries/LibAudio/AWavLoader.h b/Libraries/LibAudio/AWavLoader.h
index 494649b7a6..34fb0a0305 100644
--- a/Libraries/LibAudio/AWavLoader.h
+++ b/Libraries/LibAudio/AWavLoader.h
@@ -4,6 +4,7 @@
#include <AK/RefPtr.h>
#include <AK/StringView.h>
#include <LibCore/CFile.h>
+#include <LibAudio/ABuffer.h>
class ABuffer;
@@ -31,6 +32,7 @@ private:
bool parse_header();
RefPtr<CFile> m_file;
String m_error_string;
+ OwnPtr<AResampleHelper> m_resampler;
u32 m_sample_rate { 0 };
u16 m_num_channels { 0 };
diff --git a/Servers/AudioServer/ASClientConnection.cpp b/Servers/AudioServer/ASClientConnection.cpp
index 227de826ef..0ec2b0d13c 100644
--- a/Servers/AudioServer/ASClientConnection.cpp
+++ b/Servers/AudioServer/ASClientConnection.cpp
@@ -68,6 +68,6 @@ OwnPtr<AudioServer::EnqueueBufferResponse> ASClientConnection::handle(const Audi
if (m_queue->is_full())
return make<AudioServer::EnqueueBufferResponse>(false);
- m_queue->enqueue(ABuffer::create_with_shared_buffer(*shared_buffer));
+ m_queue->enqueue(ABuffer::create_with_shared_buffer(*shared_buffer, message.sample_count()));
return make<AudioServer::EnqueueBufferResponse>(true);
}
diff --git a/Servers/AudioServer/AudioServer.ipc b/Servers/AudioServer/AudioServer.ipc
index c2078afeb9..dfad1dd7fa 100644
--- a/Servers/AudioServer/AudioServer.ipc
+++ b/Servers/AudioServer/AudioServer.ipc
@@ -8,5 +8,5 @@ endpoint AudioServer
SetMainMixVolume(i32 volume) => ()
// Buffer playback
- EnqueueBuffer(i32 buffer_id) => (bool success)
+ EnqueueBuffer(i32 buffer_id, int sample_count) => (bool success)
}