1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Adds feedback to the best-effort updater; enables the Cocoa port for audio event requests.

This commit is contained in:
Thomas Harte 2020-01-20 17:38:25 -05:00
parent 290db67f09
commit 3aa2c297a2
8 changed files with 39 additions and 26 deletions

View File

@ -36,7 +36,7 @@ void BestEffortUpdater::update(int flags) {
has_skipped_ = update_requested_;
update_requested_ = true;
flags_ |= flags;
target_time_ = std::chrono::high_resolution_clock::now();
target_time_ = std::chrono::high_resolution_clock::now().time_since_epoch().count();
}
update_condition_.notify_one();
}
@ -71,21 +71,17 @@ void BestEffortUpdater::update_loop() {
flags_ = 0;
lock.unlock();
// Calculate period from previous time to now.
const auto elapsed = target_time - previous_time_point_;
previous_time_point_ = target_time;
// Invoke the delegate, if supplied, in order to run.
const int64_t integer_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
if(integer_duration > 0) {
const auto delegate = delegate_.load();
if(delegate) {
// Cap running at 1/5th of a second, to avoid doing a huge amount of work after any
// brief system interruption.
const double duration = std::min(double(integer_duration) / 1e9, 0.2);
delegate->update(this, duration, has_skipped_, flags);
has_skipped_ = false;
}
const int64_t integer_duration = std::max(target_time - previous_time_point_, int64_t(0));
const auto delegate = delegate_.load();
if(delegate) {
// Cap running at 1/5th of a second, to avoid doing a huge amount of work after any
// brief system interruption.
const double duration = std::min(double(integer_duration) / 1e9, 0.2);
const double elapsed_duraation = delegate->update(this, duration, has_skipped_, flags);
previous_time_point_ += int64_t(elapsed_duraation * 1e9);
has_skipped_ = false;
}
}
}

View File

@ -33,7 +33,13 @@ class BestEffortUpdater {
/// A delegate receives timing cues.
struct Delegate {
virtual void update(BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) = 0;
/*!
Instructs the delegate to run for at least @c duration, providing hints as to whether multiple updates were requested before the previous had completed
(as @c did_skip_previous_update) and providing the union of any flags supplied to @c update.
@returns The amount of time actually run for.
*/
virtual Time::Seconds update(BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) = 0;
};
/// Sets the current delegate.
@ -52,13 +58,13 @@ class BestEffortUpdater {
std::atomic<bool> should_quit_;
std::atomic<bool> is_updating_;
std::chrono::time_point<std::chrono::high_resolution_clock> target_time_;
int64_t target_time_;
int flags_ = 0;
bool update_requested_;
std::mutex update_mutex_;
std::condition_variable update_condition_;
std::chrono::time_point<std::chrono::high_resolution_clock> previous_time_point_;
decltype(target_time_) previous_time_point_;
bool has_previous_time_point_ = false;
std::atomic<bool> has_skipped_ = false;

View File

@ -243,7 +243,7 @@ class MachineDocument:
/// Responds to the CSAudioQueueDelegate dry-queue warning message by requesting a machine update.
final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) {
bestEffortLock.lock()
bestEffortUpdater?.update()
bestEffortUpdater?.update(with: .audioNeeded)
bestEffortLock.unlock()
}

View File

@ -57,7 +57,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
*/
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray<CSMissingROM *> *)missingROMs NS_DESIGNATED_INITIALIZER;
- (void)runForInterval:(NSTimeInterval)interval untilEvent:(int)events;
- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events;
- (float)idealSamplingRateFromRange:(NSRange)range;
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize;

View File

@ -259,7 +259,7 @@ struct ActivityObserver: public Activity::Observer {
}
}
- (void)runForInterval:(NSTimeInterval)interval untilEvent:(int)events {
- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events {
@synchronized(self) {
if(_joystickMachine && _joystickManager) {
[_joystickManager update];
@ -309,7 +309,7 @@ struct ActivityObserver: public Activity::Observer {
}
}
}
_machine->crt_machine()->run_until(interval, events);
return _machine->crt_machine()->run_until(interval, events);
}
}

View File

@ -11,9 +11,16 @@
#import "CSMachine.h"
// The following is coupled to the definitions in CRTMachine.hpp, but exposed here
// for the benefit of Swift.
typedef NS_ENUM(NSInteger, CSBestEffortUpdaterEvent) {
CSBestEffortUpdaterEventAudioNeeded = 1 << 0
};
@interface CSBestEffortUpdater : NSObject
- (void)update;
- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event;
- (void)flush;
- (void)setMachine:(CSMachine *)machine;

View File

@ -13,8 +13,8 @@
struct UpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate {
__weak CSMachine *machine;
void update(Concurrency::BestEffortUpdater *updater, Time::Seconds seconds, bool did_skip_previous_update, int flags) {
[machine runForInterval:seconds untilEvent:flags];
Time::Seconds update(Concurrency::BestEffortUpdater *updater, Time::Seconds seconds, bool did_skip_previous_update, int flags) final {
return [machine runForInterval:seconds untilEvent:flags];
}
};
@ -35,6 +35,10 @@ struct UpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate {
_updater.update();
}
- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event {
_updater.update((int)event);
}
- (void)flush {
_updater.flush();
}

View File

@ -34,8 +34,8 @@
namespace {
struct BestEffortUpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate {
void update(Concurrency::BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) override {
machine->crt_machine()->run_until(duration, flags);
Time::Seconds update(Concurrency::BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) override {
return machine->crt_machine()->run_until(duration, flags);
}
Machine::DynamicMachine *machine;