Skip to content

Commit

Permalink
Work in frames instead of samples
Browse files Browse the repository at this point in the history
  • Loading branch information
jcmoyer committed May 13, 2024
1 parent e4f40be commit cf0d60d
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 47 deletions.
23 changes: 10 additions & 13 deletions src/main_frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,37 +134,34 @@ void FE_ReceiveSample(void* userdata, int *sample)
{
fe_emu_instance_t& fe = *(fe_emu_instance_t*)userdata;
sample[0] >>= 15;
if (sample[0] > INT16_MAX)
sample[0] = INT16_MAX;
else if (sample[0] < INT16_MIN)
sample[0] = INT16_MIN;
sample[1] >>= 15;
if (sample[1] > INT16_MAX)
sample[1] = INT16_MAX;
else if (sample[1] < INT16_MIN)
sample[1] = INT16_MIN;
RB_Write(fe.sample_buffer, sample[0], sample[1]);

audio_frame_t frame;
frame.left = (int16_t)clamp<int>(sample[0], INT16_MIN, INT16_MAX);
frame.right = (int16_t)clamp<int>(sample[1], INT16_MIN, INT16_MAX);

RB_Write(fe.sample_buffer, frame);
}

void FE_AudioCallback(void* userdata, Uint8* stream, int len)
{
frontend_t& frontend = *(frontend_t*)userdata;

const size_t num_samples = len / sizeof(int16_t);
const size_t num_frames = len / sizeof(audio_frame_t);
memset(stream, 0, len);

size_t renderable_count = num_samples;
size_t renderable_count = num_frames;
for (size_t i = 0; i < frontend.instances_in_use; ++i)
{
renderable_count = min(
renderable_count,
RB_ReadableSampleCount(frontend.instances[i].sample_buffer)
RB_ReadableFrameCount(frontend.instances[i].sample_buffer)
);
}

for (size_t i = 0; i < frontend.instances_in_use; ++i)
{
RB_ReadMix(frontend.instances[i].sample_buffer, (int16_t*)stream, renderable_count);
RB_ReadMix(frontend.instances[i].sample_buffer, (audio_frame_t*)stream, renderable_count);
}
}

Expand Down
64 changes: 30 additions & 34 deletions src/ringbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,27 @@
#include <cstring>
#include "math_util.h"

const int RB_CHANNEL_COUNT = 2;
struct audio_frame_t {
int16_t left;
int16_t right;
};

struct ringbuffer_t {
int16_t* samples;
audio_frame_t* frames;
size_t frame_count;
size_t sample_count;
size_t read_head;
size_t write_head;
bool oversampling;
};

inline bool RB_Init(ringbuffer_t& rb, size_t frame_count)
{
rb.samples = (int16_t*)calloc(RB_CHANNEL_COUNT * frame_count, sizeof(int16_t));
if (!rb.samples)
rb.frames = (audio_frame_t*)calloc(frame_count, sizeof(audio_frame_t));
if (!rb.frames)
{
return false;
}
rb.frame_count = frame_count;
rb.sample_count = RB_CHANNEL_COUNT * frame_count;
rb.read_head = 0;
rb.write_head = 0;
rb.oversampling = false;
Expand All @@ -66,17 +67,13 @@ inline bool RB_Init(ringbuffer_t& rb, size_t frame_count)

inline void RB_Free(ringbuffer_t& rb)
{
free(rb.samples);
free(rb.frames);
}

inline void RB_SetOversamplingEnabled(ringbuffer_t& rb, bool enabled)
{
rb.oversampling = enabled;
if (rb.oversampling)
{
rb.write_head &= ~3;
}
else
{
rb.write_head &= ~1;
}
Expand All @@ -86,63 +83,62 @@ inline bool RB_IsFull(ringbuffer_t& rb)
{
if (rb.oversampling)
{
return ((rb.write_head + 2 * RB_CHANNEL_COUNT) % rb.sample_count) == rb.read_head;
return ((rb.write_head + 2) % rb.frame_count) == rb.read_head;
}
else
{
return ((rb.write_head + 1 * RB_CHANNEL_COUNT) % rb.sample_count) == rb.read_head;
return ((rb.write_head + 1) % rb.frame_count) == rb.read_head;
}
}

inline void RB_Write(ringbuffer_t& rb, int16_t left, int16_t right)
inline void RB_Write(ringbuffer_t& rb, const audio_frame_t& frame)
{
rb.samples[rb.write_head + 0] = left;
rb.samples[rb.write_head + 1] = right;
rb.write_head = (rb.write_head + RB_CHANNEL_COUNT) % rb.sample_count;
rb.frames[rb.write_head] = frame;
rb.write_head = (rb.write_head + 1) % rb.frame_count;
}

inline size_t RB_ReadableSampleCount(ringbuffer_t& rb)
inline size_t RB_ReadableFrameCount(ringbuffer_t& rb)
{
if (rb.read_head <= rb.write_head)
{
return rb.write_head - rb.read_head;
}
else
{
return rb.sample_count - (rb.read_head - rb.write_head);
return rb.frame_count - (rb.read_head - rb.write_head);
}
}

// Reads up to `sample_count` samples and returns the number of samples
// actually read.
inline size_t RB_Read(ringbuffer_t& rb, int16_t* dest, size_t sample_count)
// Reads up to `frame_count` frames and returns the number of frames actually
// read.
inline size_t RB_Read(ringbuffer_t& rb, audio_frame_t* dest, size_t frame_count)
{
size_t have_count = RB_ReadableSampleCount(rb);
size_t read_count = sample_count < have_count ? sample_count : have_count;
const size_t have_count = RB_ReadableFrameCount(rb);
const size_t read_count = min(have_count, frame_count);
size_t read_head = rb.read_head;
// TODO make this one or two memcpys
for (size_t i = 0; i < read_count; ++i)
{
*dest = rb.samples[read_head];
*dest = rb.frames[read_head];
++dest;
read_head = (read_head + 1) % rb.sample_count;
read_head = (read_head + 1) % rb.frame_count;
}
rb.read_head = read_head;
return read_count;
}

// Reads up to `sample_count` samples and returns the number of samples
// actually read. Mixes samples into dest by adding and clipping.
inline size_t RB_ReadMix(ringbuffer_t& rb, int16_t* dest, size_t sample_count)
// Reads up to `frame_count` frames and returns the number of frames actually
// read. Mixes samples into dest by adding and clipping.
inline size_t RB_ReadMix(ringbuffer_t& rb, audio_frame_t* dest, size_t frame_count)
{
size_t have_count = RB_ReadableSampleCount(rb);
size_t read_count = sample_count < have_count ? sample_count : have_count;
const size_t have_count = RB_ReadableFrameCount(rb);
const size_t read_count = min(have_count, frame_count);
size_t read_head = rb.read_head;
for (size_t i = 0; i < read_count; ++i)
{
*dest = saturating_add(*dest, rb.samples[read_head]);
++dest;
read_head = (read_head + 1) % rb.sample_count;
dest[i].left = saturating_add(dest[i].left, rb.frames[read_head].left);
dest[i].right = saturating_add(dest[i].right, rb.frames[read_head].right);
read_head = (read_head + 1) % rb.frame_count;
}
rb.read_head = read_head;
return read_count;
Expand Down

0 comments on commit cf0d60d

Please sign in to comment.