Skip to content

Commit fe666f0

Browse files
committed
rework sounds
1 parent 1644a4e commit fe666f0

File tree

5 files changed

+109
-126
lines changed

5 files changed

+109
-126
lines changed

sources/include/cage-engine/speaker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace cage
1616
void stop();
1717
bool running() const;
1818

19-
void process(uint64 timeStamp);
19+
void process(uint64 timeStamp, uint64 expectedPeriod);
2020
};
2121

2222
struct CAGE_ENGINE_API SpeakerCreateConfig

sources/libengine/sound/speaker.cpp

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <Objbase.h>
1717
#endif // CAGE_SYSTEM_WINDOWS
1818

19+
//#define GCHL_RINGBUFFER_LOGGING
1920
//#define GCHL_CUBEB_LOGGING
2021
#ifdef GCHL_CUBEB_LOGGING
2122
#include <cstdarg>
@@ -127,29 +128,49 @@ namespace cage
127128
lock_free_audio_ring_buffer<float> ring;
128129
Delegate<void(const SoundCallbackData &)> callback;
129130
std::vector<float> buffer;
130-
uint64 lastTime = 0;
131+
uint64 lastTime = m;
131132
const uint32 channels = 0;
132133
const uint32 sampleRate = 0;
133134

134135
RingBuffer(uint32 channels, uint32 sampleRate, Delegate<void(const SoundCallbackData &)> callback) : ring(channels, sampleRate / 2), callback(callback), channels(channels), sampleRate(sampleRate) {}
135136

136-
void process(uint64 currentTime)
137+
void process(uint64 currentTime, uint64 expectedPeriod)
137138
{
138-
if (lastTime == 0 || currentTime <= lastTime)
139+
if (currentTime <= lastTime)
139140
{
140141
lastTime = currentTime;
141142
return;
142143
}
143144

144-
const uint64 request = sampleRate * (currentTime - lastTime) / 1000000;
145+
uint32 request = numeric_cast<uint32>((currentTime - lastTime) * sampleRate / 1'000'000);
146+
147+
{ // maintain more consistent delay
148+
const uint32 assumedOccupied = ring.capacity() - ring.available_write();
149+
const uint32 intendedOccupied = expectedPeriod * 2 * sampleRate / 1'000'000;
150+
const uint32 toFill = intendedOccupied > assumedOccupied ? intendedOccupied - assumedOccupied : 0;
151+
const uint32 threshold = expectedPeriod * 2 / 3 * sampleRate / 1'000'000;
152+
const uint32 diff = request > toFill ? request - toFill : toFill - request;
153+
if (diff > threshold)
154+
{
155+
// reset timing
156+
#ifdef GCHL_RINGBUFFER_LOGGING
157+
CAGE_LOG(SeverityEnum::Warning, "speaker", "sound ringbuffer resets timing");
158+
#endif // GCHL_RINGBUFFER_LOGGING
159+
request = toFill;
160+
lastTime = currentTime - request * 1'000'000 / sampleRate;
161+
}
162+
}
163+
145164
SoundCallbackData data;
146-
data.frames = numeric_cast<uint32>(min(request, numeric_cast<uint64>(ring.available_write())));
165+
data.frames = min(request, numeric_cast<uint32>(ring.available_write()));
147166
data.time = lastTime; // it must correspond to the beginning of the buffer, otherwise varying number of frames would skew it
148-
lastTime += request * 1000000 / sampleRate;
167+
lastTime += request * 1'000'000 / sampleRate;
149168
if (request > data.frames)
150169
{
151170
// sound buffer overflow
152-
// nothing to do
171+
#ifdef GCHL_RINGBUFFER_LOGGING
172+
CAGE_LOG(SeverityEnum::Warning, "speaker", "sound ringbuffer overflow");
173+
#endif // GCHL_RINGBUFFER_LOGGING
153174
}
154175
if (data.frames == 0)
155176
return;
@@ -159,7 +180,8 @@ namespace cage
159180
data.channels = channels;
160181
data.sampleRate = sampleRate;
161182
callback(data);
162-
ring.enqueue(buffer.data(), data.frames);
183+
const auto wrt = ring.enqueue(buffer.data(), data.frames);
184+
CAGE_ASSERT(wrt == data.frames);
163185
}
164186

165187
void speaker(const SoundCallbackData &data)
@@ -178,6 +200,9 @@ namespace cage
178200
float *buff = data.buffer.data() + r * channels;
179201
const uint32 remaining = data.frames - r;
180202
detail::memset(buff, 0, sizeof(float) * remaining * channels);
203+
#ifdef GCHL_RINGBUFFER_LOGGING
204+
CAGE_LOG(SeverityEnum::Warning, "speaker", "sound ringbuffer underflow");
205+
#endif // GCHL_RINGBUFFER_LOGGING
181206
}
182207
};
183208

@@ -290,10 +315,10 @@ namespace cage
290315
cageCheckCubebError(cubeb_stream_stop(stream));
291316
}
292317

293-
void process(uint64 currentTime)
318+
void process(uint64 currentTime, uint64 expectedPeriod)
294319
{
295320
if (ringBuffer)
296-
ringBuffer->process(currentTime);
321+
ringBuffer->process(currentTime, expectedPeriod);
297322
}
298323

299324
void dataCallback(float *output_buffer, uint32 nframes)
@@ -366,10 +391,10 @@ namespace cage
366391
return impl->started;
367392
}
368393

369-
void Speaker::process(uint64 timeStamp)
394+
void Speaker::process(uint64 timeStamp, uint64 expectedPeriod)
370395
{
371396
SpeakerImpl *impl = (SpeakerImpl *)this;
372-
impl->process(timeStamp);
397+
impl->process(timeStamp, expectedPeriod);
373398
}
374399

375400
Holder<Speaker> newSpeaker(const SpeakerCreateConfig &config)

sources/libsimple/engine.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ namespace cage
2020
void soundCreate(const EngineCreateConfig &config);
2121
void soundDestroy();
2222
void soundFinalize();
23-
void soundEmit(uint64 time);
24-
void soundTick(uint64 time);
25-
void soundDispatch();
23+
void soundEmit(); // control thread
24+
void soundDispatch(uint64 time); // sound thread
2625

2726
extern EntityComponent *transformHistoryComponent;
2827
}

sources/libsimple/gameloop.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,9 @@ namespace cage
295295
ProfilingScope profiling("sound callback");
296296
soundThread().sound.dispatch();
297297
}
298-
{
299-
ProfilingScope profiling("sound run");
300-
soundTick(soundUpdateSchedule->time());
301-
}
302298
{
303299
ProfilingScope profiling("sound dispatch");
304-
soundDispatch();
300+
soundDispatch(soundUpdateSchedule->time());
305301
}
306302
}
307303

@@ -378,7 +374,7 @@ namespace cage
378374
}
379375
{
380376
ProfilingScope profiling("sound emit");
381-
soundEmit(controlTime);
377+
soundEmit();
382378
}
383379
{
384380
ProfilingScope profiling("graphics emit");

0 commit comments

Comments
 (0)