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:
parent
290db67f09
commit
3aa2c297a2
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user