From cb61e8486826b10c1c5b7939a6aae4664d8d6dd9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 20 Jan 2020 12:12:23 -0500 Subject: [PATCH] Starts building out higher-level `run_until` functionality. Specifically: you can now run until the next set of speaker samples has been delivered. --- .../Implementation/MultiSpeaker.cpp | 2 +- Machines/CRTMachine.hpp | 46 +++++++++++++++++-- .../Speaker/Implementation/LowpassSpeaker.hpp | 4 +- Outputs/Speaker/Speaker.hpp | 7 +++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp index 26ec1d4de..c5a8fa724 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp @@ -53,7 +53,7 @@ void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vec std::lock_guard lock_guard(front_speaker_mutex_); if(speaker != front_speaker_) return; } - delegate_->speaker_did_complete_samples(this, buffer); + did_complete_samples(this, buffer); } void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) { diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 8d6d6a99c..ff6aa7800 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -45,17 +45,57 @@ class Machine { virtual std::string debug_type() { return ""; } /// Runs the machine for @c duration seconds. - void run_for(Time::Seconds duration) { + virtual void run_for(Time::Seconds duration) { const double cycles = (duration * clock_rate_) + clock_conversion_error_; clock_conversion_error_ = std::fmod(cycles, 1.0); run_for(Cycles(static_cast(cycles))); } - /// Runs for the machine for at least @c duration seconds, and then until @c condition is true. - void run_until(Time::Seconds minimum_duration, std::function condition) { + /*! + Runs for the machine for at least @c duration seconds, and then until @c condition is true. + + @returns The amount of time run for. + */ + Time::Seconds run_until(Time::Seconds minimum_duration, std::function condition) { + Time::Seconds total_runtime = minimum_duration; run_for(minimum_duration); while(!condition()) { + // Advance in increments of one 500th of a second until the condition + // is true; that's 1/10th of a 50Hz frame, but more like 1/8.33 of a + // 60Hz frame. Though most machines aren't exactly 50Hz or 60Hz, and some + // are arbitrary other refresh rates. So those observations are merely + // for scale. run_for(0.002); + total_runtime += 0.002; + } + return total_runtime; + } + + enum class MachineEvent { + /// At least one new packet of audio has been delivered to the spaker's delegate. + NewSpeakerSamplesGenerated + }; + + /*! + Runs for at least @c duration seconds, and then until @c event has occurred at least once since this + call to @c run_until_event. + + @returns The amount of time run for. + */ + Time::Seconds run_until(Time::Seconds minimum_duration, MachineEvent event) { + switch(event) { + case MachineEvent::NewSpeakerSamplesGenerated: { + const auto speaker = get_speaker(); + if(!speaker) { + run_for(minimum_duration); + return minimum_duration; + } + + const int sample_sets = speaker->completed_sample_sets(); + return run_until(minimum_duration, [sample_sets, speaker]() { + return speaker->completed_sample_sets() != sample_sets; + }); + } break; } } diff --git a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp index 38c9fed23..ced763a56 100644 --- a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp +++ b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp @@ -134,7 +134,7 @@ template class LowpassSpeaker: public Speaker { // announce to delegate if full if(output_buffer_pointer_ == output_buffer_.size()) { output_buffer_pointer_ = 0; - delegate_->speaker_did_complete_samples(this, output_buffer_); + did_complete_samples(this, output_buffer_); } cycles_remaining -= cycles_to_read; @@ -159,7 +159,7 @@ template class LowpassSpeaker: public Speaker { // Announce to delegate if full. if(output_buffer_pointer_ == output_buffer_.size()) { output_buffer_pointer_ = 0; - delegate_->speaker_did_complete_samples(this, output_buffer_); + did_complete_samples(this, output_buffer_); } // If the next loop around is going to reuse some of the samples just collected, use a memmove to diff --git a/Outputs/Speaker/Speaker.hpp b/Outputs/Speaker/Speaker.hpp index 11b06bfde..7c5a31437 100644 --- a/Outputs/Speaker/Speaker.hpp +++ b/Outputs/Speaker/Speaker.hpp @@ -26,6 +26,8 @@ class Speaker { virtual float get_ideal_clock_rate_in_range(float minimum, float maximum) = 0; virtual void set_output_rate(float cycles_per_second, int buffer_size) = 0; + int completed_sample_sets() { return completed_sample_sets_; } + struct Delegate { virtual void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) = 0; virtual void speaker_did_change_input_clock(Speaker *speaker) {} @@ -35,7 +37,12 @@ class Speaker { } protected: + void did_complete_samples(Speaker *speaker, const std::vector &buffer) { + ++completed_sample_sets_; + delegate_->speaker_did_complete_samples(this, buffer); + } Delegate *delegate_ = nullptr; + int completed_sample_sets_ = 0; }; }