Skip to content

Commit 39b6959

Browse files
committed
yet more sounds improvements
1 parent 14f39c6 commit 39b6959

File tree

3 files changed

+44
-42
lines changed

3 files changed

+44
-42
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, uint64 expectedPeriod);
19+
void process(uint64 expectedPeriod);
2020
};
2121

2222
struct CAGE_ENGINE_API SpeakerCreateConfig

sources/libengine/sound/speaker.cpp

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -128,43 +128,23 @@ namespace cage
128128
lock_free_audio_ring_buffer<float> ring;
129129
Delegate<void(const SoundCallbackData &)> callback;
130130
std::vector<float> buffer;
131-
uint64 lastTime = m;
131+
uint64 playbackTime = applicationTime();
132132
const uint32 channels = 0;
133133
const uint32 sampleRate = 0;
134134

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

137-
void process(uint64 currentTime, uint64 expectedPeriod)
137+
void process(uint64 expectedPeriod)
138138
{
139-
if (currentTime <= lastTime)
140-
{
141-
lastTime = currentTime;
142-
return;
143-
}
144-
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-
}
139+
// maintain consistent delay
140+
const uint32 assumedOccupied = ring.capacity() - ring.available_write();
141+
const uint32 intendedOccupied = expectedPeriod * 2 * sampleRate / 1'000'000;
142+
const uint32 request = intendedOccupied > assumedOccupied ? intendedOccupied - assumedOccupied : 0;
163143

164144
SoundCallbackData data;
165145
data.frames = min(request, numeric_cast<uint32>(ring.available_write()));
166-
data.time = lastTime; // it must correspond to the beginning of the buffer, otherwise varying number of frames would skew it
167-
lastTime += request * 1'000'000 / sampleRate;
146+
data.time = playbackTime; // it must correspond to the beginning of the buffer, otherwise varying number of frames would skew it
147+
playbackTime += request * 1'000'000 / sampleRate;
168148
if (request > data.frames)
169149
{
170150
// sound buffer overflow
@@ -315,10 +295,10 @@ namespace cage
315295
cageCheckCubebError(cubeb_stream_stop(stream));
316296
}
317297

318-
void process(uint64 currentTime, uint64 expectedPeriod)
298+
void process(uint64 expectedPeriod)
319299
{
320300
if (ringBuffer)
321-
ringBuffer->process(currentTime, expectedPeriod);
301+
ringBuffer->process(expectedPeriod);
322302
}
323303

324304
void dataCallback(float *output_buffer, uint32 nframes)
@@ -391,10 +371,10 @@ namespace cage
391371
return impl->started;
392372
}
393373

394-
void Speaker::process(uint64 timeStamp, uint64 expectedPeriod)
374+
void Speaker::process(uint64 expectedPeriod)
395375
{
396376
SpeakerImpl *impl = (SpeakerImpl *)this;
397-
impl->process(timeStamp, expectedPeriod);
377+
impl->process(expectedPeriod);
398378
}
399379

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

sources/libsimple/sound.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,32 @@ namespace cage
5151
{
5252
Holder<VoicesMixer> mixer;
5353
Holder<Voice> chaining; // register this mixer in the engine scene mixer
54+
ankerl::unordered_dense::map<uintPtr, Holder<Voice>> voicesMapping; // must be destroyed before the mixer
55+
sint64 lastTime = 0;
56+
sint64 dispatchTime = 0;
5457

55-
ankerl::unordered_dense::map<uintPtr, Holder<Voice>> voicesMapping;
58+
void process(const SoundCallbackData &data)
59+
{
60+
CAGE_ASSERT(mixer);
61+
// correction for time drift
62+
const sint64 period = controlThread().updatePeriod();
63+
const sint64 duration = sint64(data.frames) * 1'000'000 / data.sampleRate;
64+
if (abs(lastTime - dispatchTime) > period)
65+
lastTime = dispatchTime; // reset time
66+
SoundCallbackData clb = data;
67+
clb.time = lastTime;
68+
lastTime += duration;
69+
dispatchTime += duration;
70+
mixer->process(clb);
71+
}
5672
};
5773

5874
struct SoundPrepareImpl
5975
{
6076
Holder<Mutex> mut = newMutex();
6177
std::vector<EmitListener> listeners;
6278
std::vector<EmitSound> sounds;
63-
ankerl::unordered_dense::map<uintPtr, PrepareListener> listenersMapping;
79+
ankerl::unordered_dense::map<uintPtr, Holder<PrepareListener>> listenersMapping; // requires stable pointers
6480

6581
explicit SoundPrepareImpl(const EngineCreateConfig &config) {}
6682

@@ -138,20 +154,21 @@ namespace cage
138154
v->loop = e.sound.loop;
139155
}
140156

141-
void prepare(PrepareListener &l, const EmitListener &e)
157+
void prepare(PrepareListener &l, const EmitListener &e, uint64 dispatchTime)
142158
{
143159
{ // listener
144160
if (!l.mixer)
145161
{
146162
l.mixer = newVoicesMixer();
147163
l.chaining = engineSceneMixer()->newVoice();
148-
l.chaining->callback.bind<VoicesMixer, &VoicesMixer::process>(+l.mixer);
164+
l.chaining->callback.bind<PrepareListener, &PrepareListener::process>(&l);
149165
}
150166
l.mixer->orientation = e.transform.orientation;
151167
l.mixer->position = e.transform.position;
152168
l.mixer->maxActiveSounds = e.listener.maxSounds;
153169
l.mixer->maxGainThreshold = e.listener.maxGainThreshold;
154170
l.mixer->gain = e.listener.gain;
171+
l.dispatchTime = dispatchTime;
155172
}
156173

157174
{ // remove obsolete
@@ -168,7 +185,7 @@ namespace cage
168185
prepare(l, l.voicesMapping[s.id], s);
169186
}
170187

171-
void dispatch(uint64 time)
188+
void dispatch(uint64 dispatchTime)
172189
{
173190
auto lock = ScopeLock(mut);
174191

@@ -181,11 +198,16 @@ namespace cage
181198
}
182199

183200
for (const EmitListener &e : listeners)
184-
prepare(listenersMapping[e.id], e);
201+
{
202+
Holder<PrepareListener> &h = listenersMapping[e.id];
203+
if (!h)
204+
h = systemMemory().createHolder<PrepareListener>();
205+
prepare(*h, e, dispatchTime);
206+
}
185207

186208
{
187209
ProfilingScope profiling("speaker process");
188-
engineSpeaker()->process(time, controlThread().updatePeriod());
210+
engineSpeaker()->process(controlThread().updatePeriod());
189211
}
190212
}
191213
};
@@ -198,9 +220,9 @@ namespace cage
198220
soundPrepare->emit();
199221
}
200222

201-
void soundDispatch(uint64 time)
223+
void soundDispatch(uint64 dispatchTime)
202224
{
203-
soundPrepare->dispatch(time);
225+
soundPrepare->dispatch(dispatchTime);
204226
}
205227

206228
void soundFinalize()

0 commit comments

Comments
 (0)