disconnect method
Implementation
Future<void> disconnect() async {
// Create completer to wait for isolate to confirm stop
_isolateStoppedCompleter = Completer<void>();
// Signal the isolate to stop - this allows it to exit its read loop
// cleanly before we close the port
_stopPort?.send(null);
_stopPort = null;
// Wait for the isolate to confirm it has stopped (it sends null when done)
// Use a timeout to avoid hanging if something goes wrong
if (_isolate != null) {
try {
await _isolateStoppedCompleter!.future.timeout(
const Duration(milliseconds: 100),
onTimeout: () {},
);
} catch (_) {
// Ignore errors - we'll kill the isolate anyway
}
}
_isolateStoppedCompleter = null;
// Close receive ports
receivePort?.close();
receivePort = null;
errorPort?.close();
errorPort = null;
// Kill the isolate (should already be stopped, but just in case)
_isolate?.kill(priority: Isolate.immediate);
_isolate = null;
// Now it's safe to close the ALSA ports since the isolate has stopped
var status = 0;
if (inPort != null) {
if ((status = alsa.snd_rawmidi_drain(inPort!.value)) < 0) {
print(
'error: cannot drain in port $this ${stringFromNative(alsa.snd_strerror(status))}',
);
}
if ((status = alsa.snd_rawmidi_close(inPort!.value)) < 0) {
print(
'error: cannot close in port $this ${stringFromNative(alsa.snd_strerror(status))}',
);
}
inPort = null;
}
_connectedDevices.remove(hardwareId(cardId, deviceId));
_disconnectStreamCtrl.add(this);
// Close output port last
if (outPort != null) {
if ((status = alsa.snd_rawmidi_drain(outPort!.value)) < 0) {
print(
'error: cannot drain out port $this ${stringFromNative(alsa.snd_strerror(status))}',
);
}
if ((status = alsa.snd_rawmidi_close(outPort!.value)) < 0) {
print(
'error: cannot close out port $this ${stringFromNative(alsa.snd_strerror(status))}',
);
}
outPort = null;
}
connected = false;
}