1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

Adds new hook for watching audio output rate changes.

This commit is contained in:
Thomas Harte 2018-03-22 09:23:01 -04:00
parent da3d65c18f
commit 682c3d8079
6 changed files with 42 additions and 12 deletions

View File

@ -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);
}
}

View File

@ -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_;

View File

@ -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;

View File

@ -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)

View File

@ -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() {

View File

@ -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;