Skip to content

Commit

Permalink
Merge pull request #71 from orchetect/dev
Browse files Browse the repository at this point in the history
Fixes and unit tests updates
  • Loading branch information
orchetect authored Jan 25, 2022
2 parents 92b8d16 + 17e0980 commit 7c18de6
Show file tree
Hide file tree
Showing 21 changed files with 657 additions and 256 deletions.
4 changes: 2 additions & 2 deletions Examples/MIDIEventLogger/MIDIEventLogger/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
logger.default(error)
}

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

return newManager
}()
Expand Down
284 changes: 192 additions & 92 deletions Examples/MIDIEventLogger/MIDIEventLogger/ContentView SubViews.swift

Large diffs are not rendered by default.

118 changes: 69 additions & 49 deletions Sources/MIDIKit/Events/Event/Event description.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

import Foundation
@_implementationOnly import SwiftRadix

extension MIDI.Event: CustomStringConvertible, CustomDebugStringConvertible {

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

return "noteOn(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(event.channel), group: \(event.group))"
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "noteOn(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(channelString), group: \(groupString))"

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

return "noteOff(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(event.channel), group: \(event.group))"
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "noteOff(\(event.note), vel: \(event.velocity), \(attrStr)chan: \(channelString), group: \(groupString))"

case .noteCC(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "noteCC(note: \(event.note), controller: \(event.controller), val: \(event.value), chan: \(event.channel), group: \(event.group))"
return "noteCC(note: \(event.note), controller: \(event.controller), val: \(event.value), chan: \(channelString), group: \(groupString))"

case .notePitchBend(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "notePitchBend(note: \(event.note), value: \(event.value), chan: \(event.channel), group: \(event.group))"
return "notePitchBend(note: \(event.note), value: \(event.value), chan: \(channelString), group: \(groupString))"

case .notePressure(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "notePressure(note:\(event.note), amount: \(event.amount), chan: \(event.channel), group: \(event.group))"
return "notePressure(note:\(event.note), amount: \(event.amount), chan: \(channelString), group: \(groupString))"

case .noteManagement(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "noteManagement(options: \(event.optionFlags), chan: \(event.channel), group: \(event.group))"
return "noteManagement(options: \(event.optionFlags), chan: \(channelString), group: \(groupString))"

case .cc(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "cc(\(event.controller.number), val: \(event.value), chan: \(event.channel), group: \(event.group))"
return "cc(\(event.controller.number), val: \(event.value), chan: \(channelString), group: \(groupString))"

case .programChange(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

switch event.bank {
case .noBankSelect:
return "prgChange(\(event.program), chan: \(event.channel), group: \(event.group))"
return "prgChange(\(event.program), chan: \(channelString), group: \(groupString))"
case .bankSelect(let bank):
return "prgChange(\(event.program), bank: \(bank), chan: \(event.channel), group: \(event.group))"
return "prgChange(\(event.program), bank: \(bank), chan: \(channelString), group: \(groupString))"
}

case .pressure(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "pressure(amount: \(event.amount), chan: \(event.channel), group: \(event.group))"
return "pressure(amount: \(event.amount), chan: \(channelString), group: \(groupString))"

case .pitchBend(let event):
let channelString = event.channel.value.hex.stringValue(prefix: true)
let groupString = event.group.value.hex.stringValue(prefix: true)

return "pitchBend(\(event.value), chan: \(event.channel), group: \(event.group))"
return "pitchBend(\(event.value), chan: \(channelString), group: \(groupString))"


// ----------------------
// MARK: System Exclusive
// ----------------------

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

let dataString = event.data
.hex.stringValue(padTo: 2, prefix: true)

return "sysEx7(mfr: \(event.manufacturer), data: [\(dataString)], group: \(event.group))"
return "sysEx7(mfr: \(event.manufacturer), data: [\(dataString)], group: \(groupString))"

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

let dataString = event.data
.hex.stringValue(padTo: 2, prefix: true)

return "universalSysEx7(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(event.group))"
return "universalSysEx7(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(groupString))"

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

let dataString = event.data
.hex.stringValue(padTo: 2, prefix: true)

return "sysEx8(mfr: \(event.manufacturer), data: [\(dataString)], group: \(event.group), streamID: \(event.streamID))"
return "sysEx8(mfr: \(event.manufacturer), data: [\(dataString)], group: \(groupString), streamID: \(event.streamID))"

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

let dataString = event.data
.hex.stringValue(padTo: 2, prefix: true)

return "universalSysEx8(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(event.group), streamID: \(event.streamID))"
return "universalSysEx8(\(event.universalType), deviceID: \(event.deviceID), subID1: \(event.subID1), subID2: \(event.subID2), data: [\(dataString)], group: \(groupString), streamID: \(event.streamID))"


// -------------------
// MARK: System Common
// -------------------

case .timecodeQuarterFrame(let event):

let dataByteString = event.dataByte.uInt8Value
.binary.stringValue(padTo: 8, splitEvery: 8, prefix: true)

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

case .songPositionPointer(let event):
return "timecodeQF(\(dataByteString), group: \(groupString))"

return "songPositionPointer(beat: \(event.midiBeat), group: \(event.group))"
case .songPositionPointer(let event):
let groupString = event.group.value.hex.stringValue(prefix: true)
return "songPositionPointer(beat: \(event.midiBeat), group: \(groupString))"

case .songSelect(let event):

return "songSelect(number: \(event.number), group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "songSelect(number: \(event.number), group: \(groupString))"

case .unofficialBusSelect(let event):

return "unofficialBusSelect(bus: \(event.bus), group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "unofficialBusSelect(bus: \(event.bus), group: \(groupString))"

case .tuneRequest(let event):

return "tuneRequest(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "tuneRequest(group: \(groupString))"


// ----------------------
// MARK: System Real Time
// ----------------------

case .timingClock(let event):

return "timingClock(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "timingClock(group: \(groupString))"

case .start(let event):

return "start(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "start(group: \(groupString))"

case .continue(let event):

return "continue(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "continue(group: \(groupString))"

case .stop(let event):

return "stop(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "stop(group: \(groupString))"

case .activeSensing(let event):

return "activeSensing(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "activeSensing(group: \(groupString))"

case .systemReset(let event):

return "systemReset(group: \(event.group))"
let groupString = event.group.value.hex.stringValue(prefix: true)
return "systemReset(group: \(groupString))"

}

Expand Down
24 changes: 14 additions & 10 deletions Sources/MIDIKit/Events/Event/System Exclusive/SysEx Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ extension MIDI.Event {
///
/// - Throws: `MIDI.Event.ParseError` if message is malformed.
@inline(__always)
public static func sysEx7(
rawBytes: [MIDI.Byte],
public init(
sysEx7RawBytes rawBytes: [MIDI.Byte],
group: MIDI.UInt4 = 0
) throws -> Self {
) throws {

var readPos = rawBytes.startIndex

Expand Down Expand Up @@ -93,14 +93,15 @@ extension MIDI.Event {
try readPosAdvance(by: 1)
let data = try readData()

return .universalSysEx7(
self = .universalSysEx7(
.init(universalType: universalType,
deviceID: deviceID,
subID1: subID1,
subID2: subID2,
data: data,
group: group)
)
return

case 0x00...0x7D:
var readManufacturer: SysExManufacturer?
Expand Down Expand Up @@ -139,11 +140,12 @@ extension MIDI.Event {
data = try readData()
}

return .sysEx7(
self = .sysEx7(
.init(manufacturer: manufacturer,
data: data,
group: group)
)
return

default:
// malformed
Expand All @@ -158,10 +160,10 @@ extension MIDI.Event {
///
/// - Throws: `MIDI.Event.ParseError` if message is malformed.
@inline(__always)
public static func sysEx8(
rawBytes: [MIDI.Byte],
public init(
sysEx8RawBytes rawBytes: [MIDI.Byte],
group: MIDI.UInt4 = 0
) throws -> Self {
) throws {

var readPos = rawBytes.startIndex

Expand Down Expand Up @@ -220,7 +222,7 @@ extension MIDI.Event {
}
}()

return .universalSysEx8(
self = .universalSysEx8(
.init(universalType: universalType,
deviceID: deviceID,
subID1: subID1,
Expand All @@ -229,6 +231,7 @@ extension MIDI.Event {
streamID: streamID,
group: group)
)
return

case .manufacturer(let mfr):
var data: [MIDI.Byte] = []
Expand All @@ -240,12 +243,13 @@ extension MIDI.Event {
}
}

return .sysEx8(
self = .sysEx8(
.init(manufacturer: mfr,
data: data,
streamID: streamID,
group: group)
)
return

default:
// malformed
Expand Down
32 changes: 32 additions & 0 deletions Sources/MIDIKit/Events/Event/System Exclusive/SysExID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,35 @@ extension MIDI.Event.SysExID {
}

}

extension MIDI.Event.SysExID {

/// Returns the Manufacturer byte(s) formatted for MIDI 1.0 SysEx7, as one byte (7-bit) or three bytes (21-bit).
@inline(__always)
public func sysEx7RawBytes() -> [MIDI.Byte] {

switch self {
case .manufacturer(let mfr):
return mfr.sysEx7RawBytes()

case .universal(let uSysEx):
return [uSysEx.rawValue.uInt8Value]
}

}

/// Returns the Manufacturer byte(s) formatted for MIDI 2.0 SysEx8, as two bytes (16-bit).
@inline(__always)
public func sysEx8RawBytes() -> [MIDI.Byte] {

switch self {
case .manufacturer(let mfr):
return mfr.sysEx8RawBytes()

case .universal(let uSysEx):
return [0x00, uSysEx.rawValue.uInt8Value]
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ extension MIDI.Event.SysExManufacturer {
return (0x01...0x7D).contains(byte)

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

}
Expand Down
4 changes: 4 additions & 0 deletions Sources/MIDIKit/IO/API/APIVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ extension MIDI.IO.APIVersion {
switch self {
case .legacyCoreMIDI:
#if os(macOS)
// Apple has deprecated legacy API but not yet obsoleted.
// We'll guess that it may become obsoleted as of macOS 13.0
if #available(macOS 13, *) { return false }
return true
#elseif os(iOS)
// Apple has deprecated legacy API but not yet obsoleted.
// We'll guess that it may become obsoleted as of iOS 16.0
if #available(iOS 16, *) { return false }
return true
#elseif os(tvOS) || os(watchOS)
Expand Down
Loading

0 comments on commit 7c18de6

Please sign in to comment.