MIDI2Transport
Transport ModuleMIDI I/O abstraction layer. Production CoreMIDI implementation and MockTransport for testing.
MIDITransport Protocol
The core protocol that abstracts MIDI I/O operations. Both production and test implementations conform to this protocol.
public protocol MIDITransport: Sendable
Required Methods
| Method | Description |
|---|---|
send(_:to:) | Send MIDI data to a destination |
connect(to:) | Connect to a MIDI source |
disconnect(from:) | Disconnect from a source |
connectToAllSources() | Connect to all available sources |
findMatchingDestination(for:) | Find destination for a source (same entity) |
shutdown() | Shut down and finish all streams |
Required Properties
| Property | Type | Description |
|---|---|---|
received | AsyncStream<MIDIReceivedData> | Stream of incoming MIDI data |
sources | [MIDISourceInfo] | Available MIDI sources |
destinations | [MIDIDestinationInfo] | Available MIDI destinations |
setupChanged | AsyncStream<Void> | Setup change notifications |
CoreMIDITransport
Production implementation using Apple's CoreMIDI framework.
public class CoreMIDITransport: MIDITransport
// Create transport
let transport = try CoreMIDITransport(clientName: "MyApp")
// Connect to all sources
try await transport.connectToAllSources()
// Send data
let data: [UInt8] = [0xF0, 0x7E, 0x7F, ...]
try await transport.send(data, to: destinationID)
// Receive data
for await received in transport.received {
print("Received \(received.data.count) bytes from \(received.sourceID)")
}
// Handle setup changes (device connected/disconnected)
for await _ in transport.setupChanged {
print("MIDI setup changed")
// Refresh device list
}
⚠️ Important: MIDISourceID and MIDIDestinationID are session-scoped handles, not persistent IDs. They may change across reboots or device reconnections. For persistent identification, use uniqueID from endpoint info.
MockMIDITransport
Mock implementation for unit testing without real MIDI hardware.
public actor MockMIDITransport: MIDITransport
// Create mock transport
let mockTransport = MockMIDITransport()
// Add mock endpoints
await mockTransport.addMockSource(MIDISourceInfo(
sourceID: MIDISourceID(1),
name: "Test Device",
uniqueID: 12345
))
await mockTransport.addMockDestination(MIDIDestinationInfo(
destinationID: MIDIDestinationID(1),
name: "Test Device",
uniqueID: 12345
))
// Simulate receiving data
await mockTransport.simulateReceive(
[0xF0, 0x7E, 0x7F, ...],
from: MIDISourceID(1)
)
// Check sent messages
let sent = await mockTransport.sentMessages
XCTAssertEqual(sent.count, 1)
XCTAssertEqual(sent[0].data, expectedData)
Test Helpers
| Method | Description |
|---|---|
simulateReceive(_:from:) | Simulate incoming MIDI data |
addMockSource(_:) | Add a mock MIDI source |
addMockDestination(_:) | Add a mock MIDI destination |
sentMessages | Array of all sent messages for verification |
clearSentMessages() | Clear the sent messages array |
Endpoint Types
MIDISourceID / MIDIDestinationID
Type-safe wrappers for CoreMIDI endpoint references.
public struct MIDISourceID: Sendable, Hashable {
public let value: UInt32
}
public struct MIDIDestinationID: Sendable, Hashable {
public let value: UInt32
}
MIDISourceInfo / MIDIDestinationInfo
Endpoint metadata including name, manufacturer, and persistent ID.
| Property | Type | Description |
|---|---|---|
sourceID / destinationID | MIDISourceID / MIDIDestinationID | Session-scoped handle |
name | String | Endpoint display name |
manufacturer | String? | Manufacturer name |
isOnline | Bool | True if currently available |
uniqueID | Int32? | Persistent ID across sessions |
MIDIReceivedData
Incoming MIDI data with source information.
public struct MIDIReceivedData: Sendable {
public let data: [UInt8] // Raw MIDI bytes
public let sourceID: MIDISourceID? // Source endpoint
public let timestamp: UInt64 // CoreMIDI timestamp
}
Error Types
public enum MIDITransportError: Error, Sendable
| Case | Description |
|---|---|
notInitialized | Transport not initialized |
clientCreationFailed(Int32) | Failed to create MIDI client |
portCreationFailed(Int32) | Failed to create MIDI port |
sendFailed(Int32) | Failed to send MIDI data |
connectionFailed(Int32) | Failed to connect to source |
destinationNotFound(UInt32) | Destination endpoint not found |
sourceNotFound(UInt32) | Source endpoint not found |