Skip to content

Commit 0aefbb0

Browse files
committed
GUI - audio scope: event-driven SHM connect
1 parent d1599d4 commit 0aefbb0

2 files changed

Lines changed: 66 additions & 70 deletions

File tree

‎app/api/include/api/audio/audio_processor.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ class AudioProcessor
6666
std::unique_ptr<server_shared_memory_client> m_shmClient;
6767
shm_scope_buffer_reader m_shmReader;
6868

69-
unsigned int m_emptyFrames = 0;
69+
// Previous validity for transition-only logging in Run().
70+
bool m_shmReaderLastValid = false;
71+
7072
int m_scSynthPort = 0;
7173

7274
std::atomic<bool> m_calculateFFT = { false };

‎app/api/src/audio/audio_processor.cpp‎

Lines changed: 63 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -259,124 +259,118 @@ shm_audio_buffer* AudioProcessor::GetAudioBufferSlot(unsigned int slot)
259259
return m_shmClient->get_audio_buffer(slot);
260260
}
261261

262+
// Re-opens the shm segment and re-binds the scope reader. The segment
263+
// is owned by supersonic and survives cold swaps; re-opening by name
264+
// covers both in-place cold swap and supersonic-restart. Validity
265+
// transitions are logged from Run() (see m_shmReaderLastValid).
262266
void AudioProcessor::ResetConnection()
263267
{
264268
try
265269
{
266270
m_shmClient.reset(new server_shared_memory_client(m_scSynthPort));
267271
m_shmReader = m_shmClient->get_scope_buffer_reader(0);
268-
269-
if (m_shmReader.valid())
270-
{
271-
LOG(DBG, "Connected to shared audio memory");
272-
SetConsumed(true);
273-
}
274-
else
275-
{
276-
LOG(ERR, "Failed to connect to shared audio memory");
277-
}
278272
}
279-
catch (const std::exception& e)
273+
catch (const std::exception&)
280274
{
281-
LOG(ERR, "Shared memory connection failed: " << e.what());
275+
// Segment not yet created by supersonic — expected at boot races.
282276
m_shmClient.reset();
283277
m_shmReader = shm_scope_buffer_reader();
284278
}
279+
280+
// Clear any stale consumed=false left from a prior torn-down session.
281+
SetConsumed(true);
285282
}
286283

284+
// Consumer-side loop. ResetConnection is called externally from the GUI
285+
// lifecycle (onSpiderReady / onSupersonicSetup); pull() returns 0 frames
286+
// gracefully whether the scope buffer is mid-reallocation or supersonic
287+
// is simply silent.
287288
void AudioProcessor::Run()
288289
{
289290
for (;;)
290291
{
291-
// We are done
292-
if (m_quit.load())
293-
{
294-
break;
295-
}
292+
if (m_quit.load()) break;
296293

297294
auto startTime = std::chrono::high_resolution_clock::now();
298295
auto nextTime = startTime + std::chrono::milliseconds(int(1000.0f / AudioProcessorRefreshRate));
299296

297+
// Log validity transitions once per change. The buffer the
298+
// reader points at transitions free → initialized asynchronously
299+
// when spider's Phase 5 runs. Above the m_running gate so the
300+
// status is logged whether or not the scope window is open.
301+
const bool nowValid = m_shmReader.valid();
302+
if (nowValid != m_shmReaderLastValid)
303+
{
304+
if (nowValid)
305+
{
306+
LOG(INFO, "Scope reader attached (scope buffer ready)");
307+
}
308+
else
309+
{
310+
LOG(INFO, "Scope reader unavailable "
311+
"(scope buffer not initialised — studio "
312+
"booting, mid-cold-swap, or wedged)");
313+
}
314+
m_shmReaderLastValid = nowValid;
315+
}
316+
300317
if (!m_running.load())
301318
{
302-
// Sleep for a second and try again
303319
std::this_thread::sleep_for(std::chrono::seconds(1));
304320
continue;
305321
}
306322

307-
if (!m_shmReader.valid())
323+
if (!nowValid)
308324
{
309-
ResetConnection();
310-
if (!m_shmReader.valid())
311-
{
312-
// Not getting a connection, sleep for a second before trying again
313-
std::this_thread::sleep_for(std::chrono::seconds(1));
314-
}
325+
std::this_thread::sleep_until(nextTime);
315326
continue;
316327
}
317328

318-
// If the GUI hasn't processed the last of our outputs, then yield our threads remaining slice.
319-
// We want to try again pretty soon, but we don't want to spin while the UI is doing its thing
329+
// If the GUI hasn't consumed the previous frame yet, yield the
330+
// rest of this slice. Avoids spinning while the UI is busy.
320331
if (!m_consumed.load())
321332
{
322333
std::this_thread::sleep_until(nextTime);
323334
continue;
324335
}
325336

337+
unsigned int frames;
338+
if (m_shmReader.pull(frames))
326339
{
327-
unsigned int frames;
328-
if (m_shmReader.pull(frames))
340+
float* data = m_shmReader.data();
341+
for (unsigned int j = 0; j < 2; ++j)
329342
{
343+
unsigned int offset = m_shmReader.max_frames() * j;
344+
for (unsigned int i = 0; i < FrameSamples - frames; ++i)
330345
{
331-
m_emptyFrames = 0;
332-
float* data = m_shmReader.data();
333-
for (unsigned int j = 0; j < 2; ++j)
346+
m_processedAudio.m_samples[j][i] = m_processedAudio.m_samples[j][i + frames];
347+
if (j == 0)
334348
{
335-
unsigned int offset = m_shmReader.max_frames() * j;
336-
for (unsigned int i = 0; i < FrameSamples - frames; ++i)
337-
{
338-
m_processedAudio.m_samples[j][i] = m_processedAudio.m_samples[j][i + frames];
339-
if (j == 0)
340-
{
341-
m_processedAudio.m_monoSamples[i] = m_processedAudio.m_monoSamples[i + frames];
342-
}
343-
}
344-
345-
for (unsigned int i = 0; i < frames; ++i)
346-
{
347-
m_processedAudio.m_samples[j][FrameSamples - frames + i] = data[i + offset];
348-
auto d = data[i + offset] + 1.0;
349-
if (j == 0)
350-
{
351-
m_processedAudio.m_monoSamples[FrameSamples - frames + i] = float(d * d);
352-
}
353-
else
354-
{
355-
m_processedAudio.m_monoSamples[FrameSamples - frames + i] += float(d * d);
356-
m_processedAudio.m_monoSamples[FrameSamples - frames + i] /= 2.0f;
357-
m_processedAudio.m_monoSamples[FrameSamples - frames + i] = sqrt(m_processedAudio.m_monoSamples[FrameSamples - frames + i]) - 1.0f;
358-
}
359-
}
349+
m_processedAudio.m_monoSamples[i] = m_processedAudio.m_monoSamples[i + frames];
360350
}
361351
}
362352

363-
CalculateFFT(m_processedAudio);
364-
365-
// Tell the UI to update
366-
m_pClient->AudioDataAvailable(m_processedAudio);
367-
}
368-
else
369-
{
370-
++m_emptyFrames;
371-
if (m_emptyFrames > 10)
353+
for (unsigned int i = 0; i < frames; ++i)
372354
{
373-
ResetConnection();
374-
m_emptyFrames = 0;
355+
m_processedAudio.m_samples[j][FrameSamples - frames + i] = data[i + offset];
356+
auto d = data[i + offset] + 1.0;
357+
if (j == 0)
358+
{
359+
m_processedAudio.m_monoSamples[FrameSamples - frames + i] = float(d * d);
360+
}
361+
else
362+
{
363+
m_processedAudio.m_monoSamples[FrameSamples - frames + i] += float(d * d);
364+
m_processedAudio.m_monoSamples[FrameSamples - frames + i] /= 2.0f;
365+
m_processedAudio.m_monoSamples[FrameSamples - frames + i] = sqrt(m_processedAudio.m_monoSamples[FrameSamples - frames + i]) - 1.0f;
366+
}
375367
}
376368
}
369+
370+
CalculateFFT(m_processedAudio);
371+
m_pClient->AudioDataAvailable(m_processedAudio);
377372
}
378373

379-
// Sleep until means we will still use the same frequency of update, regardless of how much time we took to do the processing
380374
std::this_thread::sleep_until(nextTime);
381375
}
382376

0 commit comments

Comments
 (0)