-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1d2c9b5
commit 369a65f
Showing
9 changed files
with
430 additions
and
349 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/*! | ||
* musicxml-player v0.8.0 | ||
* musicxml-player v0.9.0 | ||
* (c) Karim Ratib <[email protected]> (https://github.com/infojunkie) | ||
* Released under the GPL-3.0-only License. | ||
*/ | ||
|
@@ -12473,11 +12473,12 @@ const MIDI_PROGRAM_DEFAULT = 1; | |
const SCHEDULER_NOTE_LENGTH = 10; | ||
class WebAudioFontOutput { | ||
constructor(midiJson) { | ||
this.audioContext = new audioContextConstructor(); | ||
this.player = new WebAudioFontPlayer_1.WebAudioFontPlayer(); | ||
this.notes = []; | ||
this._audioContext = new audioContextConstructor(); | ||
this._player = new WebAudioFontPlayer_1.WebAudioFontPlayer(); | ||
this._notes = []; | ||
this._pitchBends = []; | ||
// Scan the MIDI for "program change" events, and load the corresponding instrument sample for each. | ||
this.channels = midiJson.tracks.reduce((channels, track) => { | ||
this._instruments = midiJson.tracks.reduce((channels, track) => { | ||
const pc = track.find((e) => 'programChange' in e) || | ||
track.reduce((pc, e) => { | ||
if ('noteOn' in e) { | ||
|
@@ -12492,10 +12493,10 @@ class WebAudioFontOutput { | |
}, null); | ||
if (pc) { | ||
if (pc.channel !== MIDI_CHANNEL_DRUMS) { | ||
const instrumentNumber = this.player.loader.findInstrument(pc.programChange.programNumber); | ||
const instrumentInfo = this.player.loader.instrumentInfo(instrumentNumber); | ||
const instrumentNumber = this._player.loader.findInstrument(pc.programChange.programNumber); | ||
const instrumentInfo = this._player.loader.instrumentInfo(instrumentNumber); | ||
channels[pc.channel] = { instrumentInfo }; | ||
this.player.loader.startLoad(this.audioContext, instrumentInfo.url, instrumentInfo.variable); | ||
this._player.loader.startLoad(this._audioContext, instrumentInfo.url, instrumentInfo.variable); | ||
} | ||
else { | ||
channels[MIDI_CHANNEL_DRUMS] = { beats: [] }; | ||
|
@@ -12504,10 +12505,10 @@ class WebAudioFontOutput { | |
.filter((e) => 'noteOn' in e) | ||
.map((e) => e.noteOn.noteNumber)), | ||
].forEach((beat) => { | ||
const drumNumber = this.player.loader.findDrum(beat); | ||
const drumInfo = this.player.loader.drumInfo(drumNumber); | ||
const drumNumber = this._player.loader.findDrum(beat); | ||
const drumInfo = this._player.loader.drumInfo(drumNumber); | ||
channels[MIDI_CHANNEL_DRUMS].beats[beat] = { drumInfo }; | ||
this.player.loader.startLoad(this.audioContext, drumInfo.url, drumInfo.variable); | ||
this._player.loader.startLoad(this._audioContext, drumInfo.url, drumInfo.variable); | ||
}); | ||
} | ||
} | ||
|
@@ -12518,7 +12519,7 @@ class WebAudioFontOutput { | |
// Then cleanup the array to keep the remaining notes. | ||
const scheduleNotes = () => { | ||
const now = performance.now(); | ||
this.notes | ||
this._notes | ||
.filter((note) => note.off !== null && note.off <= now) | ||
.forEach((note) => { | ||
// It can happen that the envelope expires before we get here, | ||
|
@@ -12531,30 +12532,49 @@ class WebAudioFontOutput { | |
} | ||
note.envelope = null; | ||
}); | ||
this.notes = this.notes.filter((note) => !!note.envelope); | ||
this._notes = this._notes.filter((note) => !!note.envelope); | ||
setTimeout$1(scheduleNotes, SCHEDULER_TIMEOUT); | ||
}; | ||
setTimeout$1(scheduleNotes, SCHEDULER_TIMEOUT); | ||
} | ||
send(data, timestamp) { | ||
const event = parseMidiEvent(data); | ||
if ('noteOn' in event) { | ||
this.noteOn(event, timestamp); | ||
this._noteOn(event, timestamp); | ||
} | ||
else if ('noteOff' in event) { | ||
this.noteOff(event, timestamp); | ||
this._noteOff(event, timestamp); | ||
} | ||
else if ('pitchBend' in event) { | ||
this._pitchBend(event, timestamp); | ||
} | ||
} | ||
noteOn(event, timestamp) { | ||
_noteOn(event, timestamp) { | ||
// Schedule the incoming notes to start at the incoming timestamp, | ||
// and add them to the current notes array waiting for their future "off" event. | ||
const instrument = event.channel === MIDI_CHANNEL_DRUMS | ||
? this.channels[event.channel].beats[event.noteOn.noteNumber].drumInfo | ||
.variable | ||
: this.channels[event.channel].instrumentInfo.variable; | ||
const when = this.audioContext.currentTime + (timestamp - performance.now()) / 1000; | ||
const envelope = this.player.queueWaveTable(this.audioContext, this.audioContext.destination, window[instrument], when, event.noteOn.noteNumber, SCHEDULER_NOTE_LENGTH, event.noteOn.velocity / 127); | ||
this.notes.push({ | ||
? this._instruments[event.channel].beats[event.noteOn.noteNumber] | ||
.drumInfo.variable | ||
: this._instruments[event.channel].instrumentInfo.variable; | ||
const when = this._timestampToAudioContext(timestamp); | ||
// Find the latest pitch bend that applies here. | ||
const pb = this._pitchBends | ||
.filter((pb) => { | ||
return event.channel === pb.channel && when >= pb.when; | ||
}) | ||
.reduce((max, pb) => { | ||
return !max || pb.when > max.when ? pb : max; | ||
}, null); | ||
// Schedule the note. | ||
const envelope = this._player.queueWaveTable(this._audioContext, this._audioContext.destination, window[instrument], when, event.noteOn.noteNumber, SCHEDULER_NOTE_LENGTH, event.noteOn.velocity / 127, pb | ||
? [ | ||
{ | ||
delta: (pb.pitchBend - 8192) / 4096, | ||
when: 0, | ||
}, | ||
] | ||
: []); | ||
this._notes.push({ | ||
channel: event.channel, | ||
pitch: event.noteOn.noteNumber, | ||
velocity: event.noteOn.velocity, | ||
|
@@ -12563,26 +12583,37 @@ class WebAudioFontOutput { | |
envelope, | ||
}); | ||
} | ||
noteOff(event, timestamp) { | ||
_noteOff(event, timestamp) { | ||
// WebAudioFont cannot schedule a future note cancellation, | ||
// so we identify the target note and set its cancellation timestamp. | ||
// Our own scheduleNotes() scheduler will take care of cancelling the note | ||
// when its timestamp occurs. | ||
const note = this.notes.find((note) => note.pitch === event.noteOff.noteNumber && | ||
const note = this._notes.find((note) => note.pitch === event.noteOff.noteNumber && | ||
note.channel === event.channel && | ||
note.off === null); | ||
if (note) { | ||
note.off = timestamp; | ||
} | ||
} | ||
_pitchBend(event, timestamp) { | ||
// Save the current pitch bend value. It will be used at the next noteOn event. | ||
this._pitchBends.push({ | ||
channel: event.channel, | ||
pitchBend: event.pitchBend, | ||
when: this._timestampToAudioContext(timestamp), | ||
}); | ||
} | ||
_timestampToAudioContext(timestamp) { | ||
return (this._audioContext.currentTime + (timestamp - performance.now()) / 1000); | ||
} | ||
clear() { | ||
this.player.cancelQueue(this.audioContext); | ||
this.notes = []; | ||
this._player.cancelQueue(this._audioContext); | ||
this._notes = []; | ||
} | ||
} | ||
|
||
var name = "musicxml-player"; | ||
var version = "0.8.0"; | ||
var version = "0.9.0"; | ||
var description = "A simple JavaScript component that loads and plays MusicXML files in the browser using Web Audio and Web MIDI."; | ||
var main = "dist/musicxml-player.esm.js"; | ||
var type = "module"; | ||
|
@@ -15388,11 +15419,11 @@ class VerovioRenderer { | |
note.setAttribute('fill', '#c00'); | ||
note.setAttribute('stroke', '#c00'); | ||
if (this._options.breaks === 'none') { | ||
note.scrollIntoView({ 'behavior': 'auto', 'inline': 'center' }); | ||
note.scrollIntoView({ behavior: 'auto', inline: 'center' }); | ||
} | ||
else { | ||
const system = note.closest('.system'); | ||
system === null || system === void 0 ? void 0 : system.scrollIntoView({ 'behavior': 'auto', 'block': 'center' }); | ||
system === null || system === void 0 ? void 0 : system.scrollIntoView({ behavior: 'auto', block: 'center' }); | ||
} | ||
}); | ||
} | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
import type { IMidiFile } from 'midi-json-parser-worker'; | ||
import type { IMidiOutput } from 'midi-player'; | ||
export declare class WebAudioFontOutput implements IMidiOutput { | ||
private audioContext; | ||
private player; | ||
private notes; | ||
private channels; | ||
private _audioContext; | ||
private _player; | ||
private _notes; | ||
private _instruments; | ||
private _pitchBends; | ||
constructor(midiJson: IMidiFile); | ||
send(data: number[] | Uint8Array, timestamp: number): void; | ||
private noteOn; | ||
private noteOff; | ||
private _noteOn; | ||
private _noteOff; | ||
private _pitchBend; | ||
private _timestampToAudioContext; | ||
clear(): void; | ||
} | ||
//# sourceMappingURL=WebAudioFontOutput.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.