Skip to content

Commit 7c18de6

Browse files
authored
Merge pull request #71 from orchetect/dev
Fixes and unit tests updates
2 parents 92b8d16 + 17e0980 commit 7c18de6

21 files changed

+657
-256
lines changed

Examples/MIDIEventLogger/MIDIEventLogger/AppDelegate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2323
logger.default(error)
2424
}
2525

26-
// #warning("> TODO: remove this")
27-
// newManager.preferredAPI = .legacyCoreMIDI
26+
// uncomment this to test different API versions or limit to MIDI 1.0 protocol
27+
//newManager.preferredAPI = .legacyCoreMIDI
2828

2929
return newManager
3030
}()

Examples/MIDIEventLogger/MIDIEventLogger/ContentView SubViews.swift

Lines changed: 192 additions & 92 deletions
Large diffs are not rendered by default.

Sources/MIDIKit/Events/Event/Event description.swift

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//
55

66
import Foundation
7+
@_implementationOnly import SwiftRadix
78

89
extension MIDI.Event: CustomStringConvertible, CustomDebugStringConvertible {
910

@@ -24,7 +25,10 @@ extension MIDI.Event: CustomStringConvertible, CustomDebugStringConvertible {
2425
attrStr = "\(event.attribute), "
2526
}
2627

27-
return "noteOn(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(event.channel), group: \(event.group))"
28+
let channelString = event.channel.value.hex.stringValue(prefix: true)
29+
let groupString = event.group.value.hex.stringValue(prefix: true)
30+
31+
return "noteOn(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(channelString), group: \(groupString))"
2832

2933
case .noteOff(let event):
3034
let attrStr: String
@@ -35,134 +39,150 @@ extension MIDI.Event: CustomStringConvertible, CustomDebugStringConvertible {
3539
attrStr = "\(event.attribute), "
3640
}
3741

38-
return "noteOff(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(event.channel), group: \(event.group))"
42+
let channelString = event.channel.value.hex.stringValue(prefix: true)
43+
let groupString = event.group.value.hex.stringValue(prefix: true)
44+
45+
return "noteOff(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(channelString), group: \(groupString))"
3946

4047
case .noteCC(let event):
48+
let channelString = event.channel.value.hex.stringValue(prefix: true)
49+
let groupString = event.group.value.hex.stringValue(prefix: true)
4150

42-
return "noteCC(note: \(event.note), controller: \(event.controller), val: \(event.value), chan: \(event.channel), group: \(event.group))"
51+
return "noteCC(note: \(event.note), controller: \(event.controller), val: \(event.value), chan: \(channelString), group: \(groupString))"
4352

4453
case .notePitchBend(let event):
54+
let channelString = event.channel.value.hex.stringValue(prefix: true)
55+
let groupString = event.group.value.hex.stringValue(prefix: true)
4556

46-
return "notePitchBend(note: \(event.note), value: \(event.value), chan: \(event.channel), group: \(event.group))"
57+
return "notePitchBend(note: \(event.note), value: \(event.value), chan: \(channelString), group: \(groupString))"
4758

4859
case .notePressure(let event):
60+
let channelString = event.channel.value.hex.stringValue(prefix: true)
61+
let groupString = event.group.value.hex.stringValue(prefix: true)
4962

50-
return "notePressure(note:\(event.note), amount: \(event.amount), chan: \(event.channel), group: \(event.group))"
63+
return "notePressure(note:\(event.note), amount: \(event.amount), chan: \(channelString), group: \(groupString))"
5164

5265
case .noteManagement(let event):
66+
let channelString = event.channel.value.hex.stringValue(prefix: true)
67+
let groupString = event.group.value.hex.stringValue(prefix: true)
5368

54-
return "noteManagement(options: \(event.optionFlags), chan: \(event.channel), group: \(event.group))"
69+
return "noteManagement(options: \(event.optionFlags), chan: \(channelString), group: \(groupString))"
5570

5671
case .cc(let event):
72+
let channelString = event.channel.value.hex.stringValue(prefix: true)
73+
let groupString = event.group.value.hex.stringValue(prefix: true)
5774

58-
return "cc(\(event.controller.number), val: \(event.value), chan: \(event.channel), group: \(event.group))"
75+
return "cc(\(event.controller.number), val: \(event.value), chan: \(channelString), group: \(groupString))"
5976

6077
case .programChange(let event):
78+
let channelString = event.channel.value.hex.stringValue(prefix: true)
79+
let groupString = event.group.value.hex.stringValue(prefix: true)
6180

6281
switch event.bank {
6382
case .noBankSelect:
64-
return "prgChange(\(event.program), chan: \(event.channel), group: \(event.group))"
83+
return "prgChange(\(event.program), chan: \(channelString), group: \(groupString))"
6584
case .bankSelect(let bank):
66-
return "prgChange(\(event.program), bank: \(bank), chan: \(event.channel), group: \(event.group))"
85+
return "prgChange(\(event.program), bank: \(bank), chan: \(channelString), group: \(groupString))"
6786
}
6887

6988
case .pressure(let event):
89+
let channelString = event.channel.value.hex.stringValue(prefix: true)
90+
let groupString = event.group.value.hex.stringValue(prefix: true)
7091

71-
return "pressure(amount: \(event.amount), chan: \(event.channel), group: \(event.group))"
92+
return "pressure(amount: \(event.amount), chan: \(channelString), group: \(groupString))"
7293

7394
case .pitchBend(let event):
95+
let channelString = event.channel.value.hex.stringValue(prefix: true)
96+
let groupString = event.group.value.hex.stringValue(prefix: true)
7497

75-
return "pitchBend(\(event.value), chan: \(event.channel), group: \(event.group))"
98+
return "pitchBend(\(event.value), chan: \(channelString), group: \(groupString))"
7699

77100

78101
// ----------------------
79102
// MARK: System Exclusive
80103
// ----------------------
81104

82105
case .sysEx7(let event):
106+
let dataString = event.data.hex.stringValue(padTo: 2, prefix: true)
107+
let groupString = event.group.value.hex.stringValue(prefix: true)
83108

84-
let dataString = event.data
85-
.hex.stringValue(padTo: 2, prefix: true)
86-
87-
return "sysEx7(mfr: \(event.manufacturer), data: [\(dataString)], group: \(event.group))"
109+
return "sysEx7(mfr: \(event.manufacturer), data: [\(dataString)], group: \(groupString))"
88110

89111
case .universalSysEx7(let event):
112+
let dataString = event.data.hex.stringValue(padTo: 2, prefix: true)
113+
let groupString = event.group.value.hex.stringValue(prefix: true)
90114

91-
let dataString = event.data
92-
.hex.stringValue(padTo: 2, prefix: true)
93-
94-
return "universalSysEx7(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(event.group))"
115+
return "universalSysEx7(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(groupString))"
95116

96117
case .sysEx8(let event):
118+
let dataString = event.data.hex.stringValue(padTo: 2, prefix: true)
119+
let groupString = event.group.value.hex.stringValue(prefix: true)
97120

98-
let dataString = event.data
99-
.hex.stringValue(padTo: 2, prefix: true)
100-
101-
return "sysEx8(mfr: \(event.manufacturer), data: [\(dataString)], group: \(event.group), streamID: \(event.streamID))"
121+
return "sysEx8(mfr: \(event.manufacturer), data: [\(dataString)], group: \(groupString), streamID: \(event.streamID))"
102122

103123
case .universalSysEx8(let event):
124+
let dataString = event.data.hex.stringValue(padTo: 2, prefix: true)
125+
let groupString = event.group.value.hex.stringValue(prefix: true)
104126

105-
let dataString = event.data
106-
.hex.stringValue(padTo: 2, prefix: true)
107-
108-
return "universalSysEx8(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(event.group), streamID: \(event.streamID))"
127+
return "universalSysEx8(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(groupString), streamID: \(event.streamID))"
109128

110129

111130
// -------------------
112131
// MARK: System Common
113132
// -------------------
114133

115134
case .timecodeQuarterFrame(let event):
116-
117135
let dataByteString = event.dataByte.uInt8Value
118136
.binary.stringValue(padTo: 8, splitEvery: 8, prefix: true)
119137

120-
return "timecodeQF(\(dataByteString), group: \(event.group))"
138+
let groupString = event.group.value.hex.stringValue(prefix: true)
121139

122-
case .songPositionPointer(let event):
140+
return "timecodeQF(\(dataByteString), group: \(groupString))"
123141

124-
return "songPositionPointer(beat: \(event.midiBeat), group: \(event.group))"
142+
case .songPositionPointer(let event):
143+
let groupString = event.group.value.hex.stringValue(prefix: true)
144+
return "songPositionPointer(beat: \(event.midiBeat), group: \(groupString))"
125145

126146
case .songSelect(let event):
127-
128-
return "songSelect(number: \(event.number), group: \(event.group))"
147+
let groupString = event.group.value.hex.stringValue(prefix: true)
148+
return "songSelect(number: \(event.number), group: \(groupString))"
129149

130150
case .unofficialBusSelect(let event):
131-
132-
return "unofficialBusSelect(bus: \(event.bus), group: \(event.group))"
151+
let groupString = event.group.value.hex.stringValue(prefix: true)
152+
return "unofficialBusSelect(bus: \(event.bus), group: \(groupString))"
133153

134154
case .tuneRequest(let event):
135-
136-
return "tuneRequest(group: \(event.group))"
155+
let groupString = event.group.value.hex.stringValue(prefix: true)
156+
return "tuneRequest(group: \(groupString))"
137157

138158

139159
// ----------------------
140160
// MARK: System Real Time
141161
// ----------------------
142162

143163
case .timingClock(let event):
144-
145-
return "timingClock(group: \(event.group))"
164+
let groupString = event.group.value.hex.stringValue(prefix: true)
165+
return "timingClock(group: \(groupString))"
146166

147167
case .start(let event):
148-
149-
return "start(group: \(event.group))"
168+
let groupString = event.group.value.hex.stringValue(prefix: true)
169+
return "start(group: \(groupString))"
150170

151171
case .continue(let event):
152-
153-
return "continue(group: \(event.group))"
172+
let groupString = event.group.value.hex.stringValue(prefix: true)
173+
return "continue(group: \(groupString))"
154174

155175
case .stop(let event):
156-
157-
return "stop(group: \(event.group))"
176+
let groupString = event.group.value.hex.stringValue(prefix: true)
177+
return "stop(group: \(groupString))"
158178

159179
case .activeSensing(let event):
160-
161-
return "activeSensing(group: \(event.group))"
180+
let groupString = event.group.value.hex.stringValue(prefix: true)
181+
return "activeSensing(group: \(groupString))"
162182

163183
case .systemReset(let event):
164-
165-
return "systemReset(group: \(event.group))"
184+
let groupString = event.group.value.hex.stringValue(prefix: true)
185+
return "systemReset(group: \(groupString))"
166186

167187
}
168188

Sources/MIDIKit/Events/Event/System Exclusive/SysEx Parser.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ extension MIDI.Event {
1212
///
1313
/// - Throws: `MIDI.Event.ParseError` if message is malformed.
1414
@inline(__always)
15-
public static func sysEx7(
16-
rawBytes: [MIDI.Byte],
15+
public init(
16+
sysEx7RawBytes rawBytes: [MIDI.Byte],
1717
group: MIDI.UInt4 = 0
18-
) throws -> Self {
18+
) throws {
1919

2020
var readPos = rawBytes.startIndex
2121

@@ -93,14 +93,15 @@ extension MIDI.Event {
9393
try readPosAdvance(by: 1)
9494
let data = try readData()
9595

96-
return .universalSysEx7(
96+
self = .universalSysEx7(
9797
.init(universalType: universalType,
9898
deviceID: deviceID,
9999
subID1: subID1,
100100
subID2: subID2,
101101
data: data,
102102
group: group)
103103
)
104+
return
104105

105106
case 0x00...0x7D:
106107
var readManufacturer: SysExManufacturer?
@@ -139,11 +140,12 @@ extension MIDI.Event {
139140
data = try readData()
140141
}
141142

142-
return .sysEx7(
143+
self = .sysEx7(
143144
.init(manufacturer: manufacturer,
144145
data: data,
145146
group: group)
146147
)
148+
return
147149

148150
default:
149151
// malformed
@@ -158,10 +160,10 @@ extension MIDI.Event {
158160
///
159161
/// - Throws: `MIDI.Event.ParseError` if message is malformed.
160162
@inline(__always)
161-
public static func sysEx8(
162-
rawBytes: [MIDI.Byte],
163+
public init(
164+
sysEx8RawBytes rawBytes: [MIDI.Byte],
163165
group: MIDI.UInt4 = 0
164-
) throws -> Self {
166+
) throws {
165167

166168
var readPos = rawBytes.startIndex
167169

@@ -220,7 +222,7 @@ extension MIDI.Event {
220222
}
221223
}()
222224

223-
return .universalSysEx8(
225+
self = .universalSysEx8(
224226
.init(universalType: universalType,
225227
deviceID: deviceID,
226228
subID1: subID1,
@@ -229,6 +231,7 @@ extension MIDI.Event {
229231
streamID: streamID,
230232
group: group)
231233
)
234+
return
232235

233236
case .manufacturer(let mfr):
234237
var data: [MIDI.Byte] = []
@@ -240,12 +243,13 @@ extension MIDI.Event {
240243
}
241244
}
242245

243-
return .sysEx8(
246+
self = .sysEx8(
244247
.init(manufacturer: mfr,
245248
data: data,
246249
streamID: streamID,
247250
group: group)
248251
)
252+
return
249253

250254
default:
251255
// malformed

Sources/MIDIKit/Events/Event/System Exclusive/SysExID.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,35 @@ extension MIDI.Event.SysExID {
7272
}
7373

7474
}
75+
76+
extension MIDI.Event.SysExID {
77+
78+
/// Returns the Manufacturer byte(s) formatted for MIDI 1.0 SysEx7, as one byte (7-bit) or three bytes (21-bit).
79+
@inline(__always)
80+
public func sysEx7RawBytes() -> [MIDI.Byte] {
81+
82+
switch self {
83+
case .manufacturer(let mfr):
84+
return mfr.sysEx7RawBytes()
85+
86+
case .universal(let uSysEx):
87+
return [uSysEx.rawValue.uInt8Value]
88+
}
89+
90+
}
91+
92+
/// Returns the Manufacturer byte(s) formatted for MIDI 2.0 SysEx8, as two bytes (16-bit).
93+
@inline(__always)
94+
public func sysEx8RawBytes() -> [MIDI.Byte] {
95+
96+
switch self {
97+
case .manufacturer(let mfr):
98+
return mfr.sysEx8RawBytes()
99+
100+
case .universal(let uSysEx):
101+
return [0x00, uSysEx.rawValue.uInt8Value]
102+
}
103+
104+
}
105+
106+
}

Sources/MIDIKit/Events/Event/System Exclusive/SysExManufacturer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ extension MIDI.Event.SysExManufacturer {
153153
return (0x01...0x7D).contains(byte)
154154

155155
case .threeByte(byte2: let byte2, byte3: let byte3):
156-
return
157-
(0x00...0x7F).contains(byte2) &&
158-
(0x00...0x7F).contains(byte3)
156+
// both can't be 0x00, at least one has to be non-zero.
157+
// all other scenarios are valid
158+
return !(byte2 == 0x00 && byte3 == 0x00)
159159
}
160160

161161
}

Sources/MIDIKit/IO/API/APIVersion.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ extension MIDI.IO.APIVersion {
6767
switch self {
6868
case .legacyCoreMIDI:
6969
#if os(macOS)
70+
// Apple has deprecated legacy API but not yet obsoleted.
71+
// We'll guess that it may become obsoleted as of macOS 13.0
7072
if #available(macOS 13, *) { return false }
7173
return true
7274
#elseif os(iOS)
75+
// Apple has deprecated legacy API but not yet obsoleted.
76+
// We'll guess that it may become obsoleted as of iOS 16.0
7377
if #available(iOS 16, *) { return false }
7478
return true
7579
#elseif os(tvOS) || os(watchOS)

0 commit comments

Comments
 (0)