From 4758bc861589fe1f99b8cf01bad0cf810cd492fd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 30 Apr 2021 21:37:41 -0400 Subject: [PATCH 1/8] Attempts to support insertion of states into existing windows. --- .../Documents/MachineDocument.swift | 31 ++++++++++++++++--- .../Machine/StaticAnalyser/CSStaticAnalyser.h | 2 ++ .../StaticAnalyser/CSStaticAnalyser.mm | 4 +++ .../Clock Signal/ScanTarget/CSScanTarget.h | 2 ++ .../Clock Signal/ScanTarget/CSScanTarget.mm | 4 +++ .../Mac/Clock Signal/Views/CSScanTargetView.h | 6 ++++ .../Mac/Clock Signal/Views/CSScanTargetView.m | 4 +++ 7 files changed, 48 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 855c5c23e..122bbe61a 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -137,6 +137,9 @@ class MachineDocument: func configureAs(_ analysis: CSStaticAnalyser) { self.machineDescription = analysis + actionLock.lock() + drawLock.lock() + let missingROMs = NSMutableArray() if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) { self.machine = machine @@ -147,9 +150,11 @@ class MachineDocument: // Store the selected machine and list of missing ROMs, and // show the missing ROMs dialogue. self.missingROMs = missingROMs.map({$0 as! CSMissingROM}) - requestRoms() } + + actionLock.unlock() + drawLock.unlock() } enum InteractionMode { @@ -280,8 +285,7 @@ class MachineDocument: /// Delegate message to receive drag and drop files. final func scanTargetView(_ view: CSScanTargetView, didReceiveFileAt URL: URL) { - let mediaSet = CSMediaSet(fileAt: URL) - mediaSet.apply(to: self.machine) + insertFile(URL) } /// Action for the insert menu command; displays an NSOpenPanel and then segues into the same process @@ -292,13 +296,30 @@ class MachineDocument: openPanel.beginSheetModal(for: self.windowControllers[0].window!) { (response) in if response == .OK { for url in openPanel.urls { - let mediaSet = CSMediaSet(fileAt: url) - mediaSet.apply(to: self.machine) + self.insertFile(url) } } } } + private func insertFile(_ URL: URL) { + // Try to insert media. + let mediaSet = CSMediaSet(fileAt: URL) + if !mediaSet.empty { + mediaSet.apply(to: self.machine) + return + } + + // Failing that see whether a new machine is required. + // TODO. + if let newMachine = CSStaticAnalyser(fileAt: URL) { + machine?.stop() + self.interactionMode = .notStarted + self.scanTargetView.willChangeScanTargetOwner() + configureAs(newMachine) + } + } + // MARK: - Input Management. /// Upon a resign key, immediately releases all ongoing input mechanisms — any currently pressed keys, diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index 62df779b9..e82adb720 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -114,6 +114,8 @@ typedef int Kilobytes; - (instancetype)initWithFileAtURL:(NSURL *)url; - (void)applyToMachine:(CSMachine *)machine; +@property(nonatomic, readonly) BOOL empty; + @end NS_ASSUME_NONNULL_END diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 8f4c693b4..acc8dd852 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -309,4 +309,8 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K [machine applyMedia:_media]; } +- (BOOL)empty { + return _media.empty(); +} + @end diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.h b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.h index 993e1fd04..2e0998c4c 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.h +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.h @@ -22,4 +22,6 @@ - (nonnull NSBitmapImageRep *)imageRepresentation; +- (void)willChangeOwner; + @end diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm index 75d5c66fd..2c304da1d 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm @@ -1143,6 +1143,10 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; return &_scanTarget; } +- (void)willChangeOwner { + _scanTarget.ScanTarget::will_change_owner(); +} + - (NSBitmapImageRep *)imageRepresentation { // Create an NSBitmapRep as somewhere to copy pixel data to. NSBitmapImageRep *const result = diff --git a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h index 58917f785..330e24b99 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.h @@ -162,4 +162,10 @@ */ @property(nonatomic, readonly, nonnull) CSScanTarget *scanTarget; +/*! + Indicates that the enclosed scan target is about to be handed off to a new owner; + exactly identical to calling scanTarget.will_change_owner(). +*/ +- (void)willChangeScanTargetOwner; + @end diff --git a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m index 65ed41fb1..1fe1ebbb6 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSScanTargetView.m @@ -129,6 +129,10 @@ static CVReturn DisplayLinkCallback(__unused CVDisplayLinkRef displayLink, const return _scanTarget; } +- (void)willChangeScanTargetOwner { + [_scanTarget willChangeOwner]; +} + - (void)updateBacking { [_scanTarget updateFrameBuffer]; } From cecdf8584a39a51993db43894a9209d67af64fa7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 30 Apr 2021 22:51:26 -0400 Subject: [PATCH 2/8] Ensures proper propagation of will_change_owner. --- OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm index 2c304da1d..42548e778 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm @@ -1144,7 +1144,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; } - (void)willChangeOwner { - _scanTarget.ScanTarget::will_change_owner(); + self.scanTarget->will_change_owner(); } - (NSBitmapImageRep *)imageRepresentation { From 6a052e19004094d17e36596cf142726334931c95 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 30 Apr 2021 22:56:13 -0400 Subject: [PATCH 3/8] Starts working on SDL drag-and-drop support for snapshots. --- .../xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme | 6 +++--- OSBindings/SDL/main.cpp | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 0d593f776..02b9d9828 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -74,7 +74,7 @@ + isEnabled = "YES"> + isEnabled = "YES"> + isEnabled = "NO"> media_target()->insert_media(media); + const Analyser::Static::Media media = Analyser::Static::GetMedia(event.drop.file); + if(!media.empty()) { + machine->media_target()->insert_media(media); + break; + } } break; case SDL_TEXTINPUT: From 8ae38991b06627a2122503f7ff58c70fd5af69fe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 May 2021 13:13:43 -0400 Subject: [PATCH 4/8] Factor out machine wiring. --- OSBindings/SDL/main.cpp | 168 +++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 78 deletions(-) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 9a3e85597..208f49861 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -479,6 +479,44 @@ class DynamicWindowTitler { const std::string file_name_; }; +/*! + Provides a wrapper for SDL_Joystick pointers that can keep track + of historic hat values. +*/ +class SDLJoystick { + public: + SDLJoystick(SDL_Joystick *joystick) : joystick_(joystick) { + hat_values_.resize(SDL_JoystickNumHats(joystick)); + } + + ~SDLJoystick() { + SDL_JoystickClose(joystick_); + } + + /// @returns The underlying SDL_Joystick. + SDL_Joystick *get() { + return joystick_; + } + + /// @returns A reference to the storage for the previous state of hat @c c. + Uint8 &last_hat_value(int c) { + return hat_values_[c]; + } + + /// @returns The logic OR of all stored hat states. + Uint8 hat_values() { + Uint8 value = 0; + for(const auto hat_value: hat_values_) { + value |= hat_value; + } + return value; + } + + private: + SDL_Joystick *joystick_; + std::vector hat_values_; +}; + } int main(int argc, char *argv[]) { @@ -844,91 +882,66 @@ int main(int argc, char *argv[]) { // Setup output, assuming a CRT machine for now, and prepare a best-effort updater. Outputs::Display::OpenGL::ScanTarget scan_target(target_framebuffer); - machine->scan_producer()->set_scan_target(&scan_target); + std::unique_ptr activity_observer; + bool uses_mouse; + std::vector joysticks; - // For now, lie about audio output intentions. - auto speaker = machine->audio_producer()->get_speaker(); - if(speaker) { - // Create an audio pipe. - SDL_AudioSpec desired_audio_spec; - SDL_AudioSpec obtained_audio_spec; + const auto setup_machine_input_output = [&scan_target, &machine, &speaker_delegate, &activity_observer, &joysticks, &uses_mouse] { + machine->scan_producer()->set_scan_target(&scan_target); - SDL_zero(desired_audio_spec); - desired_audio_spec.freq = 48000; // TODO: how can I get SDL to reveal the output rate of this machine? - desired_audio_spec.format = AUDIO_S16; - desired_audio_spec.channels = 1 + int(speaker->get_is_stereo()); - desired_audio_spec.samples = Uint16(SpeakerDelegate::buffered_samples); - desired_audio_spec.callback = SpeakerDelegate::SDL_audio_callback; - desired_audio_spec.userdata = &speaker_delegate; + // For now, lie about audio output intentions. + auto speaker = machine->audio_producer()->get_speaker(); + if(speaker) { + // Create an audio pipe. + SDL_AudioSpec desired_audio_spec; + SDL_AudioSpec obtained_audio_spec; - speaker_delegate.audio_device = SDL_OpenAudioDevice(nullptr, 0, &desired_audio_spec, &obtained_audio_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + SDL_zero(desired_audio_spec); + desired_audio_spec.freq = 48000; // TODO: how can I get SDL to reveal the output rate of this machine? + desired_audio_spec.format = AUDIO_S16; + desired_audio_spec.channels = 1 + int(speaker->get_is_stereo()); + desired_audio_spec.samples = Uint16(SpeakerDelegate::buffered_samples); + desired_audio_spec.callback = SpeakerDelegate::SDL_audio_callback; + desired_audio_spec.userdata = &speaker_delegate; - speaker->set_output_rate(obtained_audio_spec.freq, desired_audio_spec.samples, obtained_audio_spec.channels == 2); - speaker_delegate.is_stereo = obtained_audio_spec.channels == 2; - speaker->set_delegate(&speaker_delegate); - SDL_PauseAudioDevice(speaker_delegate.audio_device, 0); - } + speaker_delegate.audio_device = SDL_OpenAudioDevice(nullptr, 0, &desired_audio_spec, &obtained_audio_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + + speaker->set_output_rate(obtained_audio_spec.freq, desired_audio_spec.samples, obtained_audio_spec.channels == 2); + speaker_delegate.is_stereo = obtained_audio_spec.channels == 2; + speaker->set_delegate(&speaker_delegate); + SDL_PauseAudioDevice(speaker_delegate.audio_device, 0); + } + + /* + If the machine offers anything for activity observation, + create and register an activity observer. + */ + Activity::Source *const activity_source = machine->activity_source(); + if(activity_source) { + activity_observer = std::make_unique(activity_source, 4.0f / 3.0f); + } else { + activity_observer = nullptr; + } + + // If this is a joystick machine, check for and open attached joysticks. + const auto joystick_machine = machine->joystick_machine(); + if(joystick_machine) { + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + for(int c = 0; c < SDL_NumJoysticks(); ++c) { + joysticks.emplace_back(SDL_JoystickOpen(c)); + } + } else { + joysticks.clear(); + } + + // Keep a record of whether mouse events can be forwarded. + uses_mouse = !!machine->mouse_machine(); + }; + setup_machine_input_output(); int window_width, window_height; SDL_GetWindowSize(window, &window_width, &window_height); - // If this is a joystick machine, check for and open attached joysticks. - /*! - Provides a wrapper for SDL_Joystick pointers that can keep track - of historic hat values. - */ - class SDLJoystick { - public: - SDLJoystick(SDL_Joystick *joystick) : joystick_(joystick) { - hat_values_.resize(SDL_JoystickNumHats(joystick)); - } - - ~SDLJoystick() { - SDL_JoystickClose(joystick_); - } - - /// @returns The underlying SDL_Joystick. - SDL_Joystick *get() { - return joystick_; - } - - /// @returns A reference to the storage for the previous state of hat @c c. - Uint8 &last_hat_value(int c) { - return hat_values_[c]; - } - - /// @returns The logic OR of all stored hat states. - Uint8 hat_values() { - Uint8 value = 0; - for(const auto hat_value: hat_values_) { - value |= hat_value; - } - return value; - } - - private: - SDL_Joystick *joystick_; - std::vector hat_values_; - }; - std::vector joysticks; - const auto joystick_machine = machine->joystick_machine(); - if(joystick_machine) { - SDL_InitSubSystem(SDL_INIT_JOYSTICK); - for(int c = 0; c < SDL_NumJoysticks(); ++c) { - joysticks.emplace_back(SDL_JoystickOpen(c)); - } - } - - /* - If the machine offers anything for activity observation, - create and register an activity observer. - */ - std::unique_ptr activity_observer; - Activity::Source *const activity_source = machine->activity_source(); - if(activity_source) { - activity_observer = std::make_unique(activity_source, 4.0f / 3.0f); - } - // SDL 2.x delivers key up/down events and text inputs separately even when they're correlated; // this struct and map is used to correlate them by time. struct KeyPress { @@ -945,7 +958,6 @@ int main(int argc, char *argv[]) { std::vector keypresses; // Run the main event loop until the OS tells us to quit. - const bool uses_mouse = !!machine->mouse_machine(); bool should_quit = false; Uint32 fullscreen_mode = 0; machine_runner.start(); From 1290a8e32b9591c9dfe10924bea9833f07d1ce3b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 May 2021 13:30:07 -0400 Subject: [PATCH 5/8] SDL: Ensures joysticks, mouse, LEDs, etc, all update to a dragged state snapshot. --- OSBindings/SDL/main.cpp | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 208f49861..2a565adf0 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -470,13 +470,18 @@ class DynamicWindowTitler { update_window_title(); } + void set_file_name(const std::string &name) { + file_name_ = name; + update_window_title(); + } + private: void update_window_title() { SDL_SetWindowTitle(window_, window_title().c_str()); } bool mouse_is_captured_ = false; SDL_Window *window_ = nullptr; - const std::string file_name_; + std::string file_name_; }; /*! @@ -761,7 +766,7 @@ int main(int argc, char *argv[]) { if(!rom.descriptive_name.empty()) { std::cerr << rom.descriptive_name << "; "; } - std::cerr << "accepted crc32s: "; + std::cerr << "usual crc32s: "; bool is_first = true; for(const auto crc32: rom.crc32s) { if(!is_first) std::cerr << ", "; @@ -829,10 +834,6 @@ int main(int argc, char *argv[]) { SDL_StartTextInput(); } - // Wire up the best-effort updater, its delegate, and the speaker delegate. - machine_runner.machine = machine.get(); - machine_runner.machine_mutex = &machine_mutex; - // Ensure all media is inserted, if this machine accepts it. { auto media_target = machine->media_target(); @@ -886,7 +887,11 @@ int main(int argc, char *argv[]) { bool uses_mouse; std::vector joysticks; - const auto setup_machine_input_output = [&scan_target, &machine, &speaker_delegate, &activity_observer, &joysticks, &uses_mouse] { + machine_runner.machine_mutex = &machine_mutex; + const auto setup_machine_input_output = [&scan_target, &machine, &speaker_delegate, &activity_observer, &joysticks, &uses_mouse, &machine_runner] { + // Wire up the best-effort updater, its delegate, and the speaker delegate. + machine_runner.machine = machine.get(); + machine->scan_producer()->set_scan_target(&scan_target); // For now, lie about audio output intentions. @@ -1000,10 +1005,25 @@ int main(int argc, char *argv[]) { case SDL_DROPFILE: { const Analyser::Static::Media media = Analyser::Static::GetMedia(event.drop.file); + + // If the new file is only media, insert it; if it is a state snapshot then + // tear down the entire machine and replace it. if(!media.empty()) { machine->media_target()->insert_media(media); break; } + + targets = Analyser::Static::GetTargets(event.drop.file); + if(targets.empty()) break; + + ::Machine::Error error; + std::unique_ptr<::Machine::DynamicMachine> new_machine(::Machine::MachineForTargets(targets, rom_fetcher, error)); + if(error != Machine::Error::None) break; + + machine = std::move(new_machine); + static_cast(&scan_target)->will_change_owner(); + setup_machine_input_output(); + window_titler.set_file_name(final_path_component(event.drop.file)); } break; case SDL_TEXTINPUT: From 4db792591ad7752a39ab8286fb270bfa402ac44d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 May 2021 14:35:57 -0400 Subject: [PATCH 6/8] macOS: ensure activity and options panels change upon a drag-and-drop state. --- .../Documents/MachineDocument.swift | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 122bbe61a..3a3093655 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -87,6 +87,14 @@ class MachineDocument: } } + private func dismissPanels() { + activityPanel?.setIsVisible(false) + activityPanel = nil + + optionsPanel?.setIsVisible(false) + optionsPanel = nil + } + override func close() { // Close any dangling sheets. // @@ -105,11 +113,7 @@ class MachineDocument: machine?.stop() // Dismiss panels. - activityPanel?.setIsVisible(false) - activityPanel = nil - - optionsPanel?.setIsVisible(false) - optionsPanel = nil + dismissPanels() // End the update cycle. actionLock.lock() @@ -143,7 +147,6 @@ class MachineDocument: let missingROMs = NSMutableArray() if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) { self.machine = machine - setupActivityDisplay() machine.setVolume(userDefaultsVolume()) setupMachineOutput() } else { @@ -205,6 +208,9 @@ class MachineDocument: let aspectRatio = self.aspectRatio() machine.setView(scanTargetView, aspectRatio: Float(aspectRatio.width / aspectRatio.height)) + // Get rid of all existing accessory panels. + dismissPanels() + // Attach an options panel if one is available. if let optionsPanelNibName = self.machineDescription?.optionsPanelNibName { Bundle.main.loadNibNamed(optionsPanelNibName, owner: self, topLevelObjects: nil) @@ -213,6 +219,9 @@ class MachineDocument: showOptions(self) } + // Create and populate an activity display if required. + setupActivityDisplay() + machine.delegate = self // Callbacks from the OpenGL may come on a different thread, immediately following the .delegate set; From c2ff64c1e046e47f430ba437a0e0bd631484d0f0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 May 2021 14:42:43 -0400 Subject: [PATCH 7/8] Removes dangling OpenGL reference, attempts to ensure audio handover upon a machine change. --- .../Mac/Clock Signal/Documents/MachineDocument.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 3a3093655..3b16883b1 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -223,10 +223,6 @@ class MachineDocument: setupActivityDisplay() machine.delegate = self - - // Callbacks from the OpenGL may come on a different thread, immediately following the .delegate set; - // hence the full setup of the best-effort updater prior to setting self as a delegate. -// scanTargetView.delegate = self scanTargetView.responderDelegate = self // If this machine has a mouse, enable mouse capture; also indicate whether usurption @@ -270,9 +266,9 @@ class MachineDocument: self.machine.audioQueue = nil self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo) self.audioQueue.delegate = self - self.machine.audioQueue = self.audioQueue - self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo) } + self.machine.audioQueue = self.audioQueue + self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo) } } From 8268e8ee4c16609ba0ef8a15bc081fa929472fe6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 May 2021 20:46:10 -0400 Subject: [PATCH 8/8] Ensures music survives a machine switch. --- OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 3b16883b1..726d6196b 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -262,13 +262,13 @@ class MachineDocument: let isStereo = self.machine.isStereo if selectedSamplingRate > 0 { // [Re]create the audio queue only if necessary. - if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate { + if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue { self.machine.audioQueue = nil self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo) self.audioQueue.delegate = self + self.machine.audioQueue = self.audioQueue + self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo) } - self.machine.audioQueue = self.audioQueue - self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo) } }