| 380 | } |
| 381 | |
| 382 | void demonstrateRealTimeAudioPattern() |
| 383 | { |
| 384 | std::cout << "\n=== Real-Time Audio Pattern Demo ===\n"; |
| 385 | |
| 386 | // Simulate a real-time audio callback pattern |
| 387 | constexpr int sampleRate = 44100; |
| 388 | constexpr int bufferSize = 512; |
| 389 | constexpr int numChannels = 2; |
| 390 | |
| 391 | choc::fifo::SingleReaderSingleWriterFIFO<std::vector<float>> audioFIFO; |
| 392 | audioFIFO.reset (8); // 8 buffer capacity |
| 393 | |
| 394 | std::atomic<bool> isRunning{true}; |
| 395 | std::atomic<int> buffersProcessed{0}; |
| 396 | std::atomic<int> underruns{0}; |
| 397 | |
| 398 | // Audio generator thread (simulates audio callback) |
| 399 | std::thread audioThread ([&]() |
| 400 | { |
| 401 | std::random_device rd; |
| 402 | std::mt19937 gen (rd()); |
| 403 | std::uniform_real_distribution<float> dist (-0.1f, 0.1f); |
| 404 | |
| 405 | while (isRunning.load()) |
| 406 | { |
| 407 | std::vector<float> audioBuffer (bufferSize * numChannels); |
| 408 | |
| 409 | // Generate some audio data (white noise) |
| 410 | for (auto& sample : audioBuffer) |
| 411 | sample = dist (gen); |
| 412 | |
| 413 | if (audioFIFO.push (std::move (audioBuffer))) |
| 414 | { |
| 415 | buffersProcessed.fetch_add (1); |
| 416 | } |
| 417 | else |
| 418 | { |
| 419 | underruns.fetch_add (1); |
| 420 | std::cout << "Audio buffer underrun!\n"; |
| 421 | } |
| 422 | |
| 423 | // Simulate audio callback timing |
| 424 | std::this_thread::sleep_for (std::chrono::microseconds ((bufferSize * 1000000) / sampleRate)); |
| 425 | } |
| 426 | }); |
| 427 | |
| 428 | // Audio processing thread |
| 429 | std::thread processingThread ([&]() |
| 430 | { |
| 431 | std::vector<float> buffer; |
| 432 | |
| 433 | while (isRunning.load() || audioFIFO.getUsedSlots() > 0) |
| 434 | { |
| 435 | if (audioFIFO.pop (buffer)) |
| 436 | { |
| 437 | // Simulate audio processing (calculate RMS) |
| 438 | float rms = 0.0f; |
| 439 | |