Skip to content

Commit

Permalink
Use existing track names from midi file instead of overwriting them w…
Browse files Browse the repository at this point in the history
…ith "InitializedTrack" (#2903)

* - Read track names from midi file instead of naming them "InitializedTrack"
- Do not set a track name for tracks not named in midi file

* Remove double space in comment

* Use parameter position in function addMetaEvent and make it optional

* Allow overwriting track name from meta event and replace existing meta event.
Only use existing track name from meta event, if the name parameter is an empty string

* Fix whitespace and statement position violations reported by hound bot

---------

Co-authored-by: Matthias Bauer <[email protected]>
  • Loading branch information
techfreak01 and Matthias Bauer committed Apr 24, 2024
1 parent 3abac9a commit df84034
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ open class AppleSequencer: NSObject {
MusicSequenceGetIndTrack(existingSequence, UInt32(i), &musicTrack)
}
if let existingMusicTrack = musicTrack {
tracks.append(MusicTrackManager(musicTrack: existingMusicTrack, name: "InitializedTrack"))
tracks.append(MusicTrackManager(musicTrack: existingMusicTrack, name: ""))
}
}

Expand Down
102 changes: 94 additions & 8 deletions Sources/AudioKit/Sequencing/Apple Sequencer/MusicTrack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,47 @@ open class MusicTrackManager {
///
/// - parameter musicTrack: An Apple Music Track
/// - parameter name: Name for the track
/// - if name is an empty string, the name is read from track name meta event.
/// - if name is not empty, that name is used and a track name meta event is added or replaced.
///
public init(musicTrack: MusicTrack, name: String = "Unnamed") {
self.name = name
internalMusicTrack = musicTrack
trackPointer = UnsafeMutablePointer(musicTrack)

let data = [MIDIByte](name.utf8)

let metaEventPtr = MIDIMetaEvent.allocate(metaEventType: 3, data: data)
defer { metaEventPtr.deallocate() }

let result = MusicTrackNewMetaEvent(musicTrack, MusicTimeStamp(0), metaEventPtr)
if result != 0 {
Log("Unable to name Track")
if name == "" {
// Use track name from meta event (or empty name if no meta event found)
self.name = tryReadTrackNameFromMetaEvent() ?? ""
} else {
// Clear track name meta event if exists
clearMetaEvent(3)
// Add meta event with new track name
let data = [MIDIByte](name.utf8)
addMetaEvent(metaEventType: 3, data: data)
}

initSequence()
}

/// Try to read existing track name from meta event
///
/// - returns: the found track name or nil
///
func tryReadTrackNameFromMetaEvent() -> String? {
var trackName: String?

eventData?.forEach({ event in
if event.type == kMusicEventType_Meta {
let metaEventPointer = UnsafeMIDIMetaEventPointer(event.data)
let metaEvent = metaEventPointer!.event.pointee
if metaEvent.metaEventType == 0x03 {
trackName = String(decoding: metaEventPointer!.payload, as: UTF8.self)
}
}
})
return trackName
}

/// Initialize with a music track and the NoteEventSequence
///
/// - parameter musicTrack: An Apple Music Track
Expand Down Expand Up @@ -360,6 +382,48 @@ open class MusicTrackManager {
DisposeMusicEventIterator(iterator)
}

/// Clear a specific meta event
public func clearMetaEvent(_ metaEventType: MIDIByte) {
guard let track = internalMusicTrack else {
Log("internalMusicTrack does not exist")
return
}
var tempIterator: MusicEventIterator?
NewMusicEventIterator(track, &tempIterator)
guard let iterator = tempIterator else {
Log("Unable to create iterator in clearNote")
return
}
var eventTime = MusicTimeStamp(0)
var eventType = MusicEventType()
var eventData: UnsafeRawPointer?
var eventDataSize: UInt32 = 0
var hasNextEvent: DarwinBoolean = false
var isReadyForNextEvent: Bool

MusicEventIteratorHasCurrentEvent(iterator, &hasNextEvent)
while hasNextEvent.boolValue {
isReadyForNextEvent = true
MusicEventIteratorGetEventInfo(iterator,
&eventTime,
&eventType,
&eventData,
&eventDataSize)
if eventType == kMusicEventType_Meta {
if let convertedData = eventData?.load(as: MIDIMetaEvent.self) {
if convertedData.metaEventType == metaEventType {
MusicEventIteratorDeleteEvent(iterator)
isReadyForNextEvent = false
}
}
}

if isReadyForNextEvent { MusicEventIteratorNextEvent(iterator) }
MusicEventIteratorHasCurrentEvent(iterator, &hasNextEvent)
}
DisposeMusicEventIterator(iterator)
}

/// Determine if the sequence is empty
open var isEmpty: Bool {
guard let track = internalMusicTrack else {
Expand Down Expand Up @@ -559,6 +623,28 @@ open class MusicTrackManager {
}
}

/// Add MetaEvent to sequence
///
/// - Parameters:
/// - data: The MIDI data byte array - standard bytes containing the length of the data are added automatically
/// - position: Where in the sequence to start the note (expressed in beats)
///
public func addMetaEvent(metaEventType: MIDIByte,
data: [MIDIByte],
position: Duration = Duration(beats: 0)) {
guard let track = internalMusicTrack else {
Log("internalMusicTrack does not exist")
return
}
let metaEventPtr = MIDIMetaEvent.allocate(metaEventType: metaEventType, data: data)
defer { metaEventPtr.deallocate() }

let result = MusicTrackNewMetaEvent(track, position.musicTimeStamp, metaEventPtr)
if result != 0 {
Log("Unable to write meta event")
}
}

/// Add Pitch Bend change to sequence
///
/// - Parameters:
Expand Down

0 comments on commit df84034

Please sign in to comment.