diff --git a/.changeset/puny-places-fall.md b/.changeset/puny-places-fall.md new file mode 100644 index 0000000..161619d --- /dev/null +++ b/.changeset/puny-places-fall.md @@ -0,0 +1,11 @@ +--- +'@arcanewizards/timecode-toolbox': patch +--- + +Fix occasional MacOS MIDI crashing + +CoreMIDI can sometimes return with response code -304 when attempting to +initialize a client to listen for changes to the MIDI devices. +Timecode Toolbox now handles this by falling-back to polling mode if this error +is encountered. It is usually temporary, and future attempts to establish +listeners (e.g. after the app is restarted) usually works. diff --git a/apps/timecode-toolbox/src/lib/midi.tsx b/apps/timecode-toolbox/src/lib/midi.tsx index 9361998..e4c840a 100644 --- a/apps/timecode-toolbox/src/lib/midi.tsx +++ b/apps/timecode-toolbox/src/lib/midi.tsx @@ -45,17 +45,32 @@ export const useMidiDeviceWatcher = ( return; } + let establishedListener = false; if (supportInfo.notifications.supported) { - m.addEventListener('endpointschanged', listener); - // Get the initial list of devices - m.getEndpoints() - .then((endpoints) => setAvailableDevices(endpoints[type])) - .catch((cause) => { - const error = new Error(`Failed to get MIDI ${type}`, { cause }); - log.error(error); - }); - } else { - // If notifications aren't supported, poll for changes every 5 seconds + try { + m.addEventListener('endpointschanged', listener); + establishedListener = true; + // Get the initial list of devices + m.getEndpoints() + .then((endpoints) => setAvailableDevices(endpoints[type])) + .catch((cause) => { + const error = new Error(`Failed to get MIDI ${type}`, { + cause, + }); + log.error(error); + }); + } catch (cause) { + const error = new Error( + `Failed to initialize MIDI endpoint change listener, falling-back to polling`, + { cause }, + ); + log.error(error); + } + } + + if (!establishedListener) { + // If notifications aren't supported, or listening failed, + // poll for changes every 5 seconds interval = setInterval(() => { m.getEndpoints() .then((endpoints) => setAvailableDevices(endpoints[type]))