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)
0 commit comments