From 682c3d8079df54db023b804ba7f0ee12920b44ec Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 22 Mar 2018 09:23:01 -0400 Subject: [PATCH] Adds new hook for watching audio output rate changes. --- .../MultiMachine/Implementation/MultiSpeaker.cpp | 10 ++++++++++ .../MultiMachine/Implementation/MultiSpeaker.hpp | 9 +++++---- OSBindings/Mac/Clock Signal/Machine/CSMachine.h | 3 --- OSBindings/Mac/Clock Signal/Machine/CSMachine.mm | 16 +++++++++++----- .../Speaker/Implementation/LowpassSpeaker.hpp | 15 +++++++++++++++ Outputs/Speaker/Speaker.hpp | 1 + 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp index 47f92e305..0e5d246c0 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp @@ -54,7 +54,17 @@ void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vec } } +void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) { + std::lock_guard lock_guard(front_speaker_mutex_); + if(delegate_ && speaker == front_speaker_) { + delegate_->speaker_did_change_input_clock(this); + } +} + void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) { std::lock_guard lock_guard(front_speaker_mutex_); front_speaker_ = machine->crt_machine()->get_speaker(); + if(delegate_) { + delegate_->speaker_did_change_input_clock(this); + } } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp index cba1acca0..1b73a9f31 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp @@ -38,12 +38,13 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker: void set_new_front_machine(::Machine::DynamicMachine *machine); // Below is the standard Outputs::Speaker::Speaker interface; see there for documentation. - float get_ideal_clock_rate_in_range(float minimum, float maximum); - void set_output_rate(float cycles_per_second, int buffer_size); - void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate); + float get_ideal_clock_rate_in_range(float minimum, float maximum) override; + void set_output_rate(float cycles_per_second, int buffer_size) override; + void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) override; private: - void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer); + void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) override; + void speaker_did_change_input_clock(Speaker *speaker) override; MultiSpeaker(const std::vector &speakers); std::vector speakers_; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index e5c7fd4db..72f2edf62 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -43,9 +43,6 @@ @property (nonatomic, strong) CSAudioQueue *audioQueue; @property (nonatomic, readonly) CSOpenGLView *view; -@property (nonatomic, readonly) double clockRate; -@property (nonatomic, readonly) BOOL clockIsUnlimited; - @property (nonatomic, readonly) NSString *userDefaultsPrefix; - (void)paste:(NSString *)string; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 1e6ddcad1..0b5be154e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -25,6 +25,7 @@ @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; +- (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; @end struct LockProtectedDelegate { @@ -35,11 +36,16 @@ struct LockProtectedDelegate { }; struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockProtectedDelegate { - void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector &buffer) { + void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector &buffer) override { [machineAccessLock lock]; [machine speaker:speaker didCompleteSamples:buffer.data() length:(int)buffer.size()]; [machineAccessLock unlock]; } + void speaker_did_change_input_clock(Outputs::Speaker::Speaker *speaker) override { + [machineAccessLock lock]; + [machine speakerDidChangeInputClock:speaker]; + [machineAccessLock unlock]; + } }; @implementation CSMachine { @@ -71,6 +77,10 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP [self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length]; } +- (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker { + // TODO: consider changing output audio queue rate. +} + - (void)dealloc { // The two delegate's references to this machine are nilled out here because close_output may result // in a data flush, which might cause an audio callback, which could cause the audio queue to decide @@ -142,10 +152,6 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } -- (double)clockRate { - return _machine->crt_machine()->get_clock_rate(); -} - - (void)paste:(NSString *)paste { KeyboardMachine::Machine *keyboardMachine = _machine->keyboard_machine(); if(keyboardMachine) diff --git a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp index b0b361c38..8f71a8bc6 100644 --- a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp +++ b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp @@ -15,6 +15,7 @@ #include "../../../ClockReceiver/ClockReceiver.hpp" #include "../../../Concurrency/AsyncTaskQueue.hpp" +#include #include namespace Outputs { @@ -34,6 +35,8 @@ template class LowpassSpeaker: public Speaker { // Implemented as per Speaker. float get_ideal_clock_rate_in_range(float minimum, float maximum) { + std::lock_guard lock_guard(filter_parameters_mutex_); + // return twice the cut off, if applicable if( filter_parameters_.high_frequency_cutoff > 0.0f && filter_parameters_.input_cycles_per_second >= filter_parameters_.high_frequency_cutoff * 3.0f && @@ -55,6 +58,7 @@ template class LowpassSpeaker: public Speaker { // Implemented as per Speaker. void set_output_rate(float cycles_per_second, int buffer_size) { + std::lock_guard lock_guard(filter_parameters_mutex_); filter_parameters_.output_cycles_per_second = cycles_per_second; filter_parameters_.parameters_are_dirty = true; output_buffer_.resize(static_cast(buffer_size)); @@ -64,8 +68,10 @@ template class LowpassSpeaker: public Speaker { Sets the clock rate of the input audio. */ void set_input_rate(float cycles_per_second) { + std::lock_guard lock_guard(filter_parameters_mutex_); filter_parameters_.input_cycles_per_second = cycles_per_second; filter_parameters_.parameters_are_dirty = true; + filter_parameters_.input_rate_changed = true; } /*! @@ -75,6 +81,7 @@ template class LowpassSpeaker: public Speaker { path to be explicit about its effect, and get that simulation for free. */ void set_high_frequency_cutoff(float high_frequency) { + std::lock_guard lock_guard(filter_parameters_mutex_); filter_parameters_.high_frequency_cutoff = high_frequency; filter_parameters_.parameters_are_dirty = true; } @@ -88,7 +95,13 @@ template class LowpassSpeaker: public Speaker { std::size_t cycles_remaining = static_cast(cycles.as_int()); if(!cycles_remaining) return; + + std::lock_guard lock_guard(filter_parameters_mutex_); if(filter_parameters_.parameters_are_dirty) update_filter_coefficients(); + if(filter_parameters_.input_rate_changed) { + delegate_->speaker_did_change_input_clock(this); + filter_parameters_.input_rate_changed = false; + } // If input and output rates exactly match, and no additional cut-off has been specified, // just accumulate results and pass on. @@ -175,12 +188,14 @@ template class LowpassSpeaker: public Speaker { std::unique_ptr stepper_; std::unique_ptr filter_; + std::recursive_mutex filter_parameters_mutex_; struct FilterParameters { float input_cycles_per_second = 0.0f; float output_cycles_per_second = 0.0f; float high_frequency_cutoff = -1.0; bool parameters_are_dirty = true; + bool input_rate_changed = false; } filter_parameters_; void update_filter_coefficients() { diff --git a/Outputs/Speaker/Speaker.hpp b/Outputs/Speaker/Speaker.hpp index 209667076..e3c95a5b6 100644 --- a/Outputs/Speaker/Speaker.hpp +++ b/Outputs/Speaker/Speaker.hpp @@ -28,6 +28,7 @@ class Speaker { struct Delegate { virtual void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) = 0; + virtual void speaker_did_change_input_clock(Speaker *speaker) {} }; virtual void set_delegate(Delegate *delegate) { delegate_ = delegate;