From 6dabdaca45c3990f7588d2cc77f458b1f7108d8c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 9 Jul 2022 13:33:46 -0400 Subject: [PATCH] Switch to `int`; attempt to do a better job of initial audio filling. --- Machines/Amiga/Amiga.cpp | 2 +- Machines/AmstradCPC/AmstradCPC.cpp | 4 ++-- Machines/Apple/AppleII/AppleII.cpp | 6 +++--- Machines/Apple/AppleIIgs/AppleIIgs.cpp | 6 +++--- Machines/Apple/Macintosh/Macintosh.cpp | 2 +- Machines/Atari/2600/Atari2600.cpp | 2 +- Machines/Atari/ST/AtariST.cpp | 6 +++--- Machines/ColecoVision/ColecoVision.cpp | 6 +++--- Machines/Commodore/Vic-20/Vic20.cpp | 6 +++--- Machines/Electron/Electron.cpp | 6 +++--- Machines/Enterprise/Enterprise.cpp | 6 +++--- Machines/MSX/MSX.cpp | 6 +++--- Machines/MasterSystem/MasterSystem.cpp | 6 +++--- Machines/Oric/Oric.cpp | 6 +++--- Machines/Sinclair/ZX8081/ZX8081.cpp | 6 +++--- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 6 +++--- Machines/TimedMachine.hpp | 10 +++++----- .../Mac/Clock Signal/Audio/CSAudioQueue.h | 5 +++++ .../Mac/Clock Signal/Audio/CSAudioQueue.m | 17 +++++++++++------ .../Mac/Clock Signal/Machine/CSMachine.mm | 12 ++++++++++-- 20 files changed, 72 insertions(+), 54 deletions(-) diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index 4e214c21a..370217d7c 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -215,7 +215,7 @@ class ConcreteMachine: mc68000_.run_for(cycles); } - void flush_output(Output) final { + void flush_output(int) final { chipset_.flush(); } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 18d672657..db4c88391 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1049,9 +1049,9 @@ template class ConcreteMachine: } /// Fields requests to pump all output. - void flush_output(Output output) final { + void flush_output(int outputs) final { // Just flush the AY. - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { ay_.update(); ay_.flush(); } diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 450ad4452..706e4cb6c 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -810,13 +810,13 @@ template class ConcreteMachine: return Cycles(1); } - void flush_output(Output output) final { + void flush_output(int outputs) final { update_just_in_time_cards(); - if(int(output) & int(Output::Video)) { + if(outputs & Output::Video) { update_video(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index bebde58bf..546eeb68a 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -326,14 +326,14 @@ class ConcreteMachine: m65816_.run_for(cycles); } - void flush_output(Output output) final { + void flush_output(int outputs) final { iwm_.flush(); adb_glu_.flush(); - if(int(output) & int(Output::Video)) { + if(outputs & Output::Video) { video_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { AudioUpdater updater(this); audio_queue_.perform(); } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 09eed25e3..02378af24 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -365,7 +365,7 @@ template class ConcreteMachin return delay; } - void flush_output(Output) { + void flush_output(int) { // Flush the video before the audio queue; in a Mac the // video is responsible for providing part of the // audio signal, so the two aren't as distinct as in diff --git a/Machines/Atari/2600/Atari2600.cpp b/Machines/Atari/2600/Atari2600.cpp index 9fb138353..72ff213d6 100644 --- a/Machines/Atari/2600/Atari2600.cpp +++ b/Machines/Atari/2600/Atari2600.cpp @@ -174,7 +174,7 @@ class ConcreteMachine: bus_->apply_confidence(confidence_counter_); } - void flush_output(Output) final { + void flush_output(int) final { bus_->flush(); } diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 4464f6da7..2721de88c 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -413,16 +413,16 @@ class ConcreteMachine: return HalfCycles(0); } - void flush_output(Output output) final { + void flush_output(int outputs) final { dma_.flush(); mfp_.flush(); keyboard_acia_.flush(); midi_acia_.flush(); - if(int(output) & int(Output::Video)) { + if(outputs & Output::Video) { video_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 61f710907..5123abdb5 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -342,11 +342,11 @@ class ConcreteMachine: return penalty; } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { vdp_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index b63796eec..03d8292e4 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -620,11 +620,11 @@ class ConcreteMachine: return Cycles(1); } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { update_video(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { mos6560_.flush(); } } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 0eef67e3d..b0f3591e3 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -501,11 +501,11 @@ template class ConcreteMachine: return Cycles(int(cycles)); } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { video_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 1c54c7b88..1481d8e13 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -539,11 +539,11 @@ template class ConcreteMachine: return penalty; } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { nick_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 7934b36e8..71c598c9f 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -615,11 +615,11 @@ class ConcreteMachine: return addition; } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { vdp_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 51db116c4..3e5fab9c9 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -210,11 +210,11 @@ class ConcreteMachine: z80_.run_for(cycles); } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { vdp_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 4cc079904..808c80725 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -578,11 +578,11 @@ template class ConcreteMachine: return HalfCycles(0); } - void flush_output(Output output) final { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) final { + if(outputs & Output::Video) { video_.flush(); } if constexpr (is_zx81) { - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index 06a5d8631..c01671947 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -263,12 +263,12 @@ template class ConcreteMachine: } } - void flush_output(Output output) override { - if(int(output) & int(Output::Video)) { + void flush_output(int outputs) override { + if(outputs & Output::Video) { video_.flush(); } - if(int(output) & int(Output::Audio)) { + if(outputs & Output::Audio) { update_audio(); audio_queue_.perform(); } diff --git a/Machines/TimedMachine.hpp b/Machines/TimedMachine.hpp index c741d9016..f97a5ac66 100644 --- a/Machines/TimedMachine.hpp +++ b/Machines/TimedMachine.hpp @@ -61,13 +61,13 @@ class TimedMachine { virtual float get_confidence() { return 0.5f; } virtual std::string debug_type() { return ""; } - enum class Output { - Video = 1 << 0, - Audio = 1 << 1 + struct Output { + static constexpr int Video = 1 << 0; + static constexpr int Audio = 1 << 1; }; /// Ensures all locally-buffered output is posted onward for the types of output indicated - /// by the bitfield argument. - virtual void flush_output(Output) {} + /// by the bitfield argument, which is comprised of flags from the namespace @c Output. + virtual void flush_output(int) {} protected: /// Runs the machine for @c cycles. diff --git a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h index 370bea589..8a8721dc9 100644 --- a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h +++ b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h @@ -58,4 +58,9 @@ */ @property (nonatomic, readonly) NSUInteger preferredBufferSize; +/*! + @returns @C YES if this queue is running low or is completely exhausted of new audio buffers. +*/ +@property (atomic, readonly) BOOL isRunningDry; + @end diff --git a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m index f65432cc6..1c1a5f7e5 100644 --- a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m +++ b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m @@ -8,6 +8,7 @@ #import "CSAudioQueue.h" @import AudioToolbox; +#include #define AudioQueueBufferMaxLength 8192 #define NumberOfStoredAudioQueueBuffer 16 @@ -29,7 +30,7 @@ static NSLock *CSAudioQueueDeallocLock; AudioQueueRef _audioQueue; NSLock *_storedBuffersLock; CSWeakAudioQueuePointer *_weakPointer; - int _enqueuedBuffers; + atomic_int _enqueuedBuffers; } #pragma mark - AudioQueue callbacks @@ -39,14 +40,14 @@ static NSLock *CSAudioQueueDeallocLock; */ - (BOOL)audioQueue:(AudioQueueRef)theAudioQueue didCallbackWithBuffer:(AudioQueueBufferRef)buffer { [_storedBuffersLock lock]; - --_enqueuedBuffers; + const int buffers = atomic_fetch_add(&_enqueuedBuffers, -1); // If that leaves nothing in the queue, re-enqueue whatever just came back in order to keep the // queue going. AudioQueues seem to stop playing and never restart no matter how much encouragement // if exhausted. - if(!_enqueuedBuffers) { + if(!buffers) { AudioQueueEnqueueBuffer(theAudioQueue, buffer, 0, NULL); - ++_enqueuedBuffers; + atomic_fetch_add(&_enqueuedBuffers, 1); } else { AudioQueueFreeBuffer(_audioQueue, buffer); } @@ -71,6 +72,10 @@ static void audioOutputCallback( } } +- (BOOL)isRunningDry { + return atomic_load_explicit(&_enqueuedBuffers, memory_order_relaxed) < 2; +} + #pragma mark - Standard object lifecycle - (instancetype)initWithSamplingRate:(Float64)samplingRate isStereo:(BOOL)isStereo { @@ -158,11 +163,11 @@ static void audioOutputCallback( [_storedBuffersLock lock]; // Don't enqueue more than 4 buffers ahead of now, to ensure not too much latency accrues. - if(_enqueuedBuffers > 4) { + if(atomic_load_explicit(&_enqueuedBuffers, memory_order_relaxed) > 4) { [_storedBuffersLock unlock]; return; } - ++_enqueuedBuffers; + atomic_fetch_add(&_enqueuedBuffers, 1); AudioQueueBufferRef newBuffer; AudioQueueAllocateBuffer(_audioQueue, (UInt32)bufferBytes * 2, &newBuffer); diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 579a66451..45c93b0b4 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -678,13 +678,21 @@ struct ActivityObserver: public Activity::Observer { - (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue { updater.update([self] { - updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Audio); + updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Audio);// | MachineTypes::TimedMachine::Output::Video); +// dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ +// [self.view updateBacking]; +// }); }); } - (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime { updater.update([self] { - updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Video); + auto outputs = MachineTypes::TimedMachine::Output::Video; + if(_audioQueue.isRunningDry) { + outputs |= MachineTypes::TimedMachine::Output::Audio; + } + updater.performer.machine->flush_output(outputs); + dispatch_async(dispatch_get_main_queue(), ^{ [self.view updateBacking]; [self.view draw];