Skip to content

Commit 8e5c6e2

Browse files
Reduce audio buffer capacity and add active latency recovery
1 parent ae1ec48 commit 8e5c6e2

3 files changed

Lines changed: 40 additions & 2 deletions

File tree

src/apple2/SoundCore.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ struct sample_buffer {
2626
last_value = 0;
2727
}
2828

29+
auto skip(size_t len) -> void {
30+
size_t filled = get_filled();
31+
size_t num = (len < filled) ? len : filled;
32+
if (num == 0) {
33+
return;
34+
}
35+
size_t r = read_index.load(std::memory_order_relaxed);
36+
read_index.store((r + num) % buffer.size(), std::memory_order_release);
37+
}
38+
2939
auto get_filled() const -> size_t {
3040
size_t r = read_index.load(std::memory_order_relaxed);
3141
size_t w = write_index.load(std::memory_order_relaxed);
@@ -137,7 +147,7 @@ static sample_buffer* g_mockMixBuffer = nullptr;
137147
} // namespace
138148

139149
void SoundCore_Initialize() {
140-
constexpr size_t buffer_size = 65536;
150+
constexpr size_t buffer_size = 16384;
141151
if (g_spkrMixBuffer == nullptr) {
142152
g_spkrMixBuffer = new sample_buffer(buffer_size);
143153
}
@@ -155,6 +165,15 @@ void SoundCore_Destroy() {
155165
g_mockMixBuffer = nullptr;
156166
}
157167

168+
void SoundCore_ClearBuffers() {
169+
if (g_spkrMixBuffer != nullptr) {
170+
g_spkrMixBuffer->reinit();
171+
}
172+
if (g_mockMixBuffer != nullptr) {
173+
g_mockMixBuffer->reinit();
174+
}
175+
}
176+
158177
void SoundCore_UploadSpeakerSamples(const int16_t* buffer,
159178
uint32_t num_samples) {
160179
if (g_spkrMixBuffer != nullptr) {
@@ -175,6 +194,22 @@ void SoundCore_GetSamples(int16_t* out, size_t num_samples) {
175194
return;
176195
}
177196

197+
// Active Latency Recovery: skip oldest samples if the buffer backlog is too
198+
// large. We allow a cushion of 1024 elements (~23 ms) above the requested
199+
// block size to avoid underruns due to thread scheduling jitter.
200+
const size_t target_backlog =
201+
std::min(num_samples + 1024, static_cast<size_t>(16384 - 2));
202+
203+
size_t spkr_filled = g_spkrMixBuffer->get_filled();
204+
if (spkr_filled > target_backlog) {
205+
g_spkrMixBuffer->skip(spkr_filled - target_backlog);
206+
}
207+
208+
size_t mock_filled = g_mockMixBuffer->get_filled();
209+
if (mock_filled > target_backlog) {
210+
g_mockMixBuffer->skip(mock_filled - target_backlog);
211+
}
212+
178213
if (g_spkrMixBuffer->get_filled() == 0 &&
179214
g_mockMixBuffer->get_filled() == 0) {
180215
memset(out, 0, num_samples * sizeof(int16_t));

src/apple2/SoundCore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ enum { FADE_OUT = 0, FADE_IN = 1 };
1212

1313
void SoundCore_Initialize();
1414
void SoundCore_Destroy();
15+
void SoundCore_ClearBuffers();
1516

1617
/**
1718
* @brief Uploads samples for the built-in Speaker channel.

src/core/LinAppleCore.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ static auto ShouldRunFullSpeed() -> bool {
101101
} else if (!shouldTurbo && s_wasTurbo) {
102102
uint32_t elapsed = Linapple_GetTicks() - s_turboStartMs;
103103
Logger::Perf("Full-speed disk mode disengaged after %ums\n", elapsed);
104+
SoundCore_ClearBuffers();
104105
}
105106

106107
s_wasTurbo = shouldTurbo;
@@ -243,7 +244,8 @@ auto Linapple_RunFrame(uint32_t cycles) -> uint32_t {
243244
}
244245

245246
void Linapple_SetKeyState(uint8_t apple_code, bool down) {
246-
KeyboardEvent_t ev = {apple_code, (uint8_t)(down ? 1 : 0), 0, 0, 0, 0, {0, 0, 0}};
247+
KeyboardEvent_t ev = {apple_code, (uint8_t)(down ? 1 : 0), 0, 0, 0, 0,
248+
{0, 0, 0}};
247249
Peripheral_Command(0, keyboard_cmd_event, &ev, sizeof(ev));
248250
}
249251

0 commit comments

Comments
 (0)