()
| 62 | } |
| 63 | let lastTime = Date.now() |
| 64 | function* generate() { |
| 65 | for (const midiTrack of format.tracks) { |
| 66 | for (const [channel, midiEvents] of midiTrack.controlEvents) { |
| 67 | console.debug(`Importing ${midiEvents.length} events of channel #${channel}.`) |
| 68 | if (midiEvents.length === 0) {continue} |
| 69 | if (midiEvents.every(event => event.type !== ControlType.NOTE_ON && event.type !== ControlType.NOTE_OFF)) {continue} |
| 70 | const map = new Map<byte, { position: ppqn, note: byte, velocity: unitValue }> |
| 71 | const notes: Array<{ position: ppqn, duration: ppqn, pitch: byte, velocity: unitValue }> = [] |
| 72 | let duration = 0 | 0 |
| 73 | for (const midiEvent of midiEvents) { |
| 74 | const index = midiEvents.indexOf(midiEvent) |
| 75 | const position = PPQN.fromSignature(midiEvent.ticks / format.timeDivision, 4) | 0 |
| 76 | midiEvent.accept({ |
| 77 | noteOn: (note: byte, velocity: number) => map.set(note, {position, note, velocity}), |
| 78 | noteOff: (note: byte) => { |
| 79 | const data = map.get(note) |
| 80 | map.delete(note) |
| 81 | if (!isDefined(data)) {return} |
| 82 | notes.push({ |
| 83 | position: data.position, |
| 84 | duration: position - data.position, |
| 85 | pitch: data.note, |
| 86 | velocity: data.velocity |
| 87 | }) |
| 88 | duration = Math.max(duration, position) |
| 89 | } |
| 90 | }) |
| 91 | progress.setValue(index / midiEvents.length) |
| 92 | if (Date.now() - lastTime > 16.0) { |
| 93 | lastTime = Date.now() |
| 94 | yield |
| 95 | } |
| 96 | } |
| 97 | duration = quantizeCeil(duration, PPQN.Bar) |
| 98 | if (duration === 0) { |
| 99 | console.warn(`Channel #${channel}: no playable notes, skipping region.`) |
| 100 | continue |
| 101 | } |
| 102 | let trackBox: TrackBox |
| 103 | if (isDefined(reuseTrackBox)) { |
| 104 | trackBox = reuseTrackBox |
| 105 | reuseTrackBox = null |
| 106 | trackIndex++ |
| 107 | } else { |
| 108 | trackBox = TrackBox.create(boxGraph, UUID.generate(), box => { |
| 109 | box.type.setValue(TrackType.Notes) |
| 110 | box.tracks.refer(audioUnitBoxAdapter.box.tracks) |
| 111 | box.index.setValue(trackIndex++) |
| 112 | box.target.refer(audioUnitBoxAdapter.box) |
| 113 | }) |
| 114 | } |
| 115 | const collection = NoteEventCollectionBox.create(boxGraph, UUID.generate()) |
| 116 | notes.forEach(({position, duration: noteDuration, pitch, velocity}) => { |
| 117 | NoteEventBox.create(boxGraph, UUID.generate(), box => { |
| 118 | box.position.setValue(position) |
| 119 | box.duration.setValue(noteDuration) |
| 120 | box.pitch.setValue(pitch) |
| 121 | box.velocity.setValue(velocity) |
no test coverage detected