@@ -68,6 +68,12 @@ class MidiService {
6868 outputConnected : false
6969 } ;
7070
71+ // Flags to track intentional disconnections (prevent auto-reconnect until next server connection)
72+ private intentionalDisconnectFlags = {
73+ input : false ,
74+ output : false
75+ } ;
76+
7177 async initialize ( ) : Promise < boolean > {
7278 try {
7379 this . jzz = await JZZ ( ) ;
@@ -222,6 +228,9 @@ class MidiService {
222228 }
223229 } ) ;
224230
231+ // Clear intentional disconnect flag since user manually connected
232+ this . intentionalDisconnectFlags . input = false ;
233+
225234 console . log ( `Connected to MIDI input: ${ this . connectionState . inputDeviceName } ` ) ;
226235 return true ;
227236 } catch ( error ) {
@@ -254,6 +263,9 @@ class MidiService {
254263 lastOutputDeviceId : deviceId
255264 }
256265 } ) ;
266+
267+ // Clear intentional disconnect flag since user manually connected
268+ this . intentionalDisconnectFlags . output = false ;
257269
258270 console . log ( `Connected to virtual MIDI synthesizer: ${ virtualMidiService . getPortName ( ) } ` ) ;
259271 return true ;
@@ -284,6 +296,9 @@ class MidiService {
284296 }
285297 } ) ;
286298
299+ // Clear intentional disconnect flag since user manually connected
300+ this . intentionalDisconnectFlags . output = false ;
301+
287302 console . log ( `Connected to MIDI output: ${ this . connectionState . outputDeviceName } ` ) ;
288303 return true ;
289304 } catch ( error ) {
@@ -319,6 +334,44 @@ class MidiService {
319334 }
320335 }
321336
337+ // Disconnect specific device type
338+ disconnectDevice ( deviceType : 'input' | 'output' ) : void {
339+ if ( deviceType === 'input' ) {
340+ if ( this . inputDevice ) {
341+ this . inputDevice . close ?.( ) ;
342+ this . inputDevice = null ;
343+ }
344+ this . inputCallback = null ;
345+ this . connectionState . inputConnected = false ;
346+ this . connectionState . inputDeviceId = undefined ;
347+ this . connectionState . inputDeviceName = undefined ;
348+ console . log ( "Disconnected input device" ) ;
349+ } else if ( deviceType === 'output' ) {
350+ if ( this . outputDevice ) {
351+ this . outputDevice . close ?.( ) ;
352+ this . outputDevice = null ;
353+ }
354+ this . connectionState . outputConnected = false ;
355+ this . connectionState . outputDeviceId = undefined ;
356+ this . connectionState . outputDeviceName = undefined ;
357+ console . log ( "Disconnected output device" ) ;
358+ }
359+ }
360+
361+ // Disconnect with intentional flag setting
362+ disconnectWithIntent ( deviceType : 'input' | 'output' | 'both' ) : void {
363+ this . setIntentionalDisconnect ( deviceType ) ;
364+
365+ if ( deviceType === 'input' ) {
366+ this . disconnectDevice ( 'input' ) ;
367+ } else if ( deviceType === 'output' ) {
368+ this . disconnectDevice ( 'output' ) ;
369+ } else {
370+ // 'both'
371+ this . disconnect ( ) ;
372+ }
373+ }
374+
322375 disconnect ( ) : void {
323376 if ( this . inputDevice ) {
324377 this . inputDevice . close ?.( ) ;
@@ -359,6 +412,11 @@ class MidiService {
359412 return { ...this . connectionState } ;
360413 }
361414
415+ // Get intentional disconnect flags
416+ get intentionalDisconnectStatus ( ) {
417+ return { ...this . intentionalDisconnectFlags } ;
418+ }
419+
362420 // Device change monitoring
363421 onDeviceChange ( callback : DeviceChangeCallback ) : ( ) => void {
364422 this . deviceChangeCallbacks . add ( callback ) ;
@@ -447,36 +505,42 @@ class MidiService {
447505
448506 // For input devices, we need a callback, so we'll defer this until someone actually tries to connect
449507 // Just log what we would try to reconnect to
450- if ( preferences . lastInputDeviceId ) {
508+ if ( preferences . lastInputDeviceId && ! this . intentionalDisconnectFlags . input ) {
451509 const inputDevices = this . getInputDevices ( ) ;
452510 const lastInputDevice = inputDevices . find ( d => d . id === preferences . lastInputDeviceId ) ;
453511 if ( lastInputDevice ) {
454512 console . log ( `Input device available for auto-reconnect: ${ lastInputDevice . name } ` ) ;
455513 }
514+ } else if ( preferences . lastInputDeviceId && this . intentionalDisconnectFlags . input ) {
515+ console . log ( `Skipping input auto-reconnect due to intentional disconnect` ) ;
456516 }
457517
458518 // For output devices, we can attempt reconnection immediately
459- if ( preferences . lastOutputDeviceId ) {
519+ if ( preferences . lastOutputDeviceId && ! this . intentionalDisconnectFlags . output ) {
460520 const outputDevices = this . getOutputDevices ( ) ;
461521 const lastOutputDevice = outputDevices . find ( d => d . id === preferences . lastOutputDeviceId ) ;
462522 if ( lastOutputDevice ) {
463523 console . log ( `Attempting to auto-reconnect to output device: ${ lastOutputDevice . name } ` ) ;
464524 await this . connectOutputDevice ( preferences . lastOutputDeviceId ) ;
465525 }
526+ } else if ( preferences . lastOutputDeviceId && this . intentionalDisconnectFlags . output ) {
527+ console . log ( `Skipping output auto-reconnect due to intentional disconnect` ) ;
466528 }
467529 }
468530
469531 // Public method to attempt auto-reconnection when callback is available
470532 async attemptAutoReconnectInput ( callback : MidiInputCallback ) : Promise < boolean > {
471533 const preferences = preferencesStore . getState ( ) . midi ;
472534
473- if ( preferences . lastInputDeviceId ) {
535+ if ( preferences . lastInputDeviceId && ! this . intentionalDisconnectFlags . input ) {
474536 const inputDevices = this . getInputDevices ( ) ;
475537 const lastInputDevice = inputDevices . find ( d => d . id === preferences . lastInputDeviceId ) ;
476538 if ( lastInputDevice ) {
477539 console . log ( `Attempting to auto-reconnect to input device: ${ lastInputDevice . name } ` ) ;
478540 return await this . connectInputDevice ( preferences . lastInputDeviceId , callback ) ;
479541 }
542+ } else if ( preferences . lastInputDeviceId && this . intentionalDisconnectFlags . input ) {
543+ console . log ( `Skipping input auto-reconnect due to intentional disconnect` ) ;
480544 }
481545
482546 return false ;
@@ -498,6 +562,25 @@ class MidiService {
498562 return false ;
499563 }
500564
565+ // Set intentional disconnect flag for a device type
566+ setIntentionalDisconnect ( deviceType : 'input' | 'output' | 'both' ) : void {
567+ if ( deviceType === 'input' || deviceType === 'both' ) {
568+ this . intentionalDisconnectFlags . input = true ;
569+ }
570+ if ( deviceType === 'output' || deviceType === 'both' ) {
571+ this . intentionalDisconnectFlags . output = true ;
572+ }
573+ console . log ( `Set intentional disconnect flag for: ${ deviceType } ` ) ;
574+ }
575+
576+ // Reset all intentional disconnect flags (called when server reconnects)
577+ resetIntentionalDisconnectFlags ( ) : void {
578+ this . intentionalDisconnectFlags . input = false ;
579+ this . intentionalDisconnectFlags . output = false ;
580+ console . log ( "Reset intentional disconnect flags" ) ;
581+ }
582+
583+
501584 // Shutdown and cleanup
502585 shutdown ( ) : void {
503586 this . disconnect ( ) ;
0 commit comments