Skip to content

Commit a5ae3c9

Browse files
authored
Merge pull request #79 from orchetect/dev
Incremental updates
2 parents 00b62c0 + cad7b0b commit a5ae3c9

File tree

5 files changed

+160
-47
lines changed

5 files changed

+160
-47
lines changed

Sources/MIDIKit/Events/Event/Channel Voice/CC/CC Controller/CC Categories/CC NRPN.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ extension MIDI.Event.CC.Controller.NRPN {
2828
public func events(channel: MIDI.UInt4,
2929
group: MIDI.UInt4 = 0) -> [MIDI.Event] {
3030

31-
#warning("> TODO: not sure if this is correct")
32-
3331
var nrpnEvents: [MIDI.Event] = [
3432
.cc(.nrpnMSB,
3533
value: .midi1(parameter.msb),
@@ -50,7 +48,7 @@ extension MIDI.Event.CC.Controller.NRPN {
5048
}
5149

5250
if let dataEntryLSB = dataEntryLSB {
53-
nrpnEvents.append(.cc(.dataEntry,
51+
nrpnEvents.append(.cc(.lsb(for: .dataEntry),
5452
value: .midi1(dataEntryLSB),
5553
channel: channel,
5654
group: group))

Sources/MIDIKit/Events/Event/Channel Voice/CC/CC Controller/CC Categories/CC RPN.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ extension MIDI.Event.CC.Controller.RPN {
174174
public func events(channel: MIDI.UInt4,
175175
group: MIDI.UInt4 = 0) -> [MIDI.Event] {
176176

177-
#warning("> TODO: not sure RPN messages are correct")
178-
179177
var rpnEvents: [MIDI.Event] = [
180178
.cc(.rpnMSB,
181179
value: .midi1(parameter.msb),
@@ -198,7 +196,7 @@ extension MIDI.Event.CC.Controller.RPN {
198196
}
199197

200198
if let dataEntryLSB = dataEntryBytes.lsb {
201-
rpnEvents.append(.cc(.dataEntry,
199+
rpnEvents.append(.cc(.lsb(for: .dataEntry),
202200
value: .midi1(dataEntryLSB),
203201
channel: channel,
204202
group: group))

Sources/MIDIKit/IO/Core MIDI/MIDIEventList/MIDIEventList Utilities.swift

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -47,48 +47,47 @@ extension MIDIEventPacket {
4747

4848
}
4949

50-
// MARK: - This method "works" but MIDIEventPacket.Builder appears to have a heap overflow bug, so we shouldn't use this code for the meantime. A radar has been filed with Apple.
50+
extension MIDIEventPacket {
51+
52+
// Note: this init isn't used but it works.
53+
// It implements Apple's built-in Core MIDI event packet builder.
54+
55+
/// Internal:
56+
/// Assembles a Core MIDI `MIDIEventPacket` (Universal MIDI Packet) from a `UInt32` word array.
57+
@available(macOS 11, iOS 14, macCatalyst 14, tvOS 14, watchOS 7, *)
58+
@inline(__always)
59+
internal init(
60+
wordsUsingBuilder words: [MIDI.UMPWord]
61+
) throws {
5162

52-
//extension MIDIEventPacket {
53-
//
54-
// /// Internal:
55-
// /// Assembles a Core MIDI `MIDIEventPacket` (Universal MIDI Packet) from a `UInt32` word array.
56-
// @available(macOS 11, iOS 14, macCatalyst 14, tvOS 14, watchOS 7, *)
57-
// @inline(__always)
58-
// internal init(
59-
// words: [MIDI.UMPWord]
60-
// ) throws {
61-
//
62-
// guard words.count > 0 else {
63-
// throw MIDI.IO.MIDIError.malformed(
64-
// "A Universal MIDI Packet cannot contain zero UInt32 words."
65-
// )
66-
// }
67-
//
68-
// guard words.count <= 64 else {
69-
// throw MIDI.IO.MIDIError.malformed(
70-
// "A Universal MIDI Packet cannot contain more than 64 UInt32 words."
71-
// )
72-
// }
73-
//
74-
// let packetBuilder = MIDIEventPacket
75-
// .Builder(maximumNumberMIDIWords: words.count)
76-
//
77-
// words.forEach { packetBuilder.append($0) }
78-
//
79-
// let packet = try packetBuilder
80-
// .withUnsafePointer { unsafePtr -> Result<MIDIEventPacket, Error> in
81-
// // this "works", but when ASAN is enabled accessing pointee throws a heap overflow error
82-
// // I filed a radar with Apple on Sep 18, 2021: FB9637098
83-
// .success(unsafePtr.pointee)
84-
// }
85-
// .get()
86-
//
87-
// self = packet
88-
//
89-
// }
90-
//
91-
//}
63+
guard words.count > 0 else {
64+
throw MIDI.IO.MIDIError.malformed(
65+
"A Universal MIDI Packet cannot contain zero UInt32 words."
66+
)
67+
}
68+
69+
guard words.count <= 64 else {
70+
throw MIDI.IO.MIDIError.malformed(
71+
"A Universal MIDI Packet cannot contain more than 64 UInt32 words."
72+
)
73+
}
74+
75+
let packetBuilder = MIDIEventPacket
76+
.Builder(maximumNumberMIDIWords: 64) // must be 64 or we get crashes!
77+
78+
words.forEach { packetBuilder.append($0) }
79+
80+
let packet = try packetBuilder
81+
.withUnsafePointer { unsafePtr -> Result<MIDIEventPacket, Error> in
82+
.success(unsafePtr.pointee)
83+
}
84+
.get()
85+
86+
self = packet
87+
88+
}
89+
90+
}
9291

9392
extension MIDIEventList {
9493

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// CC NRPN Tests.swift
3+
// MIDIKit • https://github.com/orchetect/MIDIKit
4+
//
5+
6+
#if shouldTestCurrentPlatform
7+
8+
import XCTest
9+
import MIDIKit
10+
11+
final class MIDIEventCCNRPN_Tests: XCTestCase {
12+
13+
typealias NRPN = MIDI.Event.CC.Controller.NRPN
14+
15+
func testNRPN_MIDI1_NoDataEntry() {
16+
17+
let nrpn: [MIDI.Event] = MIDI.Event.ccNRPN(parameter: .init(msb: 66, lsb: 103),
18+
dataEntryMSB: nil,
19+
dataEntryLSB: nil,
20+
channel: 0x9)
21+
22+
XCTAssertEqual(nrpn.flatMap(\.midi1RawBytes),
23+
[0xB9, 0x63, 66,
24+
0xB9, 0x62, 103])
25+
26+
}
27+
28+
func testNRPN_MIDI1_DataEntryMSB() {
29+
30+
let nrpn: [MIDI.Event] = MIDI.Event.ccNRPN(parameter: .init(msb: 66, lsb: 103),
31+
dataEntryMSB: 127,
32+
dataEntryLSB: nil,
33+
channel: 0x9)
34+
35+
XCTAssertEqual(nrpn.flatMap(\.midi1RawBytes),
36+
[0xB9, 0x63, 66,
37+
0xB9, 0x62, 103,
38+
0xB9, 0x06, 127])
39+
40+
}
41+
42+
func testNRPN_MIDI1_testRPN_MIDI1_DataEntryMSBandLSB() {
43+
44+
let nrpn: [MIDI.Event] = MIDI.Event.ccNRPN(parameter: .init(msb: 66, lsb: 103),
45+
dataEntryMSB: 127,
46+
dataEntryLSB: 2,
47+
channel: 0x9)
48+
49+
XCTAssertEqual(nrpn.flatMap(\.midi1RawBytes),
50+
[0xB9, 0x63, 66,
51+
0xB9, 0x62, 103,
52+
0xB9, 0x06, 127,
53+
0xB9, 0x26, 2])
54+
55+
}
56+
57+
}
58+
59+
#endif
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// CC RPN Tests.swift
3+
// MIDIKit • https://github.com/orchetect/MIDIKit
4+
//
5+
6+
#if shouldTestCurrentPlatform
7+
8+
import XCTest
9+
import MIDIKit
10+
11+
final class MIDIEventCCRPN_Tests: XCTestCase {
12+
13+
typealias RPN = MIDI.Event.CC.Controller.RPN
14+
15+
func testRPN_MIDI1_NoDataEntry() {
16+
17+
let rpn: [MIDI.Event] = MIDI.Event.ccRPN(.raw(parameter: .init(msb: 66, lsb: 103),
18+
dataEntryMSB: nil,
19+
dataEntryLSB: nil),
20+
channel: 0x9)
21+
22+
XCTAssertEqual(rpn.flatMap(\.midi1RawBytes),
23+
[0xB9, 0x65, 66,
24+
0xB9, 0x64, 103])
25+
26+
}
27+
28+
func testRPN_MIDI1_DataEntryMSB() {
29+
30+
let rpn: [MIDI.Event] = MIDI.Event.ccRPN(.raw(parameter: .init(msb: 66, lsb: 103),
31+
dataEntryMSB: 127,
32+
dataEntryLSB: nil),
33+
channel: 0x9)
34+
35+
XCTAssertEqual(rpn.flatMap(\.midi1RawBytes),
36+
[0xB9, 0x65, 66,
37+
0xB9, 0x64, 103,
38+
0xB9, 0x06, 127])
39+
40+
}
41+
42+
func testRPN_MIDI1_testRPN_MIDI1_DataEntryMSBandLSB() {
43+
44+
let rpn: [MIDI.Event] = MIDI.Event.ccRPN(.raw(parameter: .init(msb: 66, lsb: 103),
45+
dataEntryMSB: 127,
46+
dataEntryLSB: 2),
47+
channel: 0x9)
48+
49+
XCTAssertEqual(rpn.flatMap(\.midi1RawBytes),
50+
[0xB9, 0x65, 66,
51+
0xB9, 0x64, 103,
52+
0xB9, 0x06, 127,
53+
0xB9, 0x26, 2])
54+
55+
}
56+
57+
}
58+
59+
#endif

0 commit comments

Comments
 (0)