mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Adds new hook for watching audio output rate changes.
This commit is contained in:
parent
da3d65c18f
commit
682c3d8079
@ -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<std::mutex> 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<std::mutex> lock_guard(front_speaker_mutex_);
|
||||
front_speaker_ = machine->crt_machine()->get_speaker();
|
||||
if(delegate_) {
|
||||
delegate_->speaker_did_change_input_clock(this);
|
||||
}
|
||||
}
|
||||
|
@ -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<int16_t> &buffer);
|
||||
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) override;
|
||||
void speaker_did_change_input_clock(Speaker *speaker) override;
|
||||
MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers);
|
||||
|
||||
std::vector<Outputs::Speaker::Speaker *> speakers_;
|
||||
|
@ -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;
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
@interface CSMachine() <CSFastLoading>
|
||||
- (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<int16_t> &buffer) {
|
||||
void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector<int16_t> &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)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
|
||||
namespace Outputs {
|
||||
@ -34,6 +35,8 @@ template <typename T> class LowpassSpeaker: public Speaker {
|
||||
|
||||
// Implemented as per Speaker.
|
||||
float get_ideal_clock_rate_in_range(float minimum, float maximum) {
|
||||
std::lock_guard<std::recursive_mutex> 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 <typename T> class LowpassSpeaker: public Speaker {
|
||||
|
||||
// Implemented as per Speaker.
|
||||
void set_output_rate(float cycles_per_second, int buffer_size) {
|
||||
std::lock_guard<std::recursive_mutex> 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<std::size_t>(buffer_size));
|
||||
@ -64,8 +68,10 @@ template <typename T> class LowpassSpeaker: public Speaker {
|
||||
Sets the clock rate of the input audio.
|
||||
*/
|
||||
void set_input_rate(float cycles_per_second) {
|
||||
std::lock_guard<std::recursive_mutex> 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 <typename T> 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<std::recursive_mutex> lock_guard(filter_parameters_mutex_);
|
||||
filter_parameters_.high_frequency_cutoff = high_frequency;
|
||||
filter_parameters_.parameters_are_dirty = true;
|
||||
}
|
||||
@ -88,7 +95,13 @@ template <typename T> class LowpassSpeaker: public Speaker {
|
||||
|
||||
std::size_t cycles_remaining = static_cast<size_t>(cycles.as_int());
|
||||
if(!cycles_remaining) return;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> 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 <typename T> class LowpassSpeaker: public Speaker {
|
||||
std::unique_ptr<SignalProcessing::Stepper> stepper_;
|
||||
std::unique_ptr<SignalProcessing::FIRFilter> 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() {
|
||||
|
@ -28,6 +28,7 @@ class Speaker {
|
||||
|
||||
struct Delegate {
|
||||
virtual void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) = 0;
|
||||
virtual void speaker_did_change_input_clock(Speaker *speaker) {}
|
||||
};
|
||||
virtual void set_delegate(Delegate *delegate) {
|
||||
delegate_ = delegate;
|
||||
|
Loading…
Reference in New Issue
Block a user