@@ -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).
262266void 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.
287288void 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