From d7f6fe564462f46302f04894a71930ec50257a9d Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Mon, 17 May 2021 12:36:12 +0800 Subject: [PATCH 1/2] Add render() method to get raw sample data. --- README.md | 17 +++++++++++++++++ index.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/README.md b/README.md index 4ba8414..bccaed6 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,23 @@ just call `player.pause()` to pause or `player.load()` to load a new MIDI file. Returns `true` if `destroy()` has been called on the player. Returns `false` otherwise. +### `player.render()` + +Renders the loaded MIDI file to audio data. Returns an array of `Float32Array`s +containing the raw left and right sample data for the entire song. The +`render()` method should only be called once the MIDI file and soundfonts have +been loaded: + +``` +player.on("loaded", function() { + const arrays = player.render() + console.log(arrays) +}) +``` + +Note: the uncompressed sample data can consume a lot of memory, depending on +the length of the song. + ### `player.on('error', (err) => {})` This event fires if a fatal error occurs in the player, including if a MIDI file diff --git a/index.js b/index.js index 28b4dd0..3530b59 100644 --- a/index.js +++ b/index.js @@ -148,6 +148,7 @@ class Timidity extends EventEmitter { this._songPtr = songPtr this._lib._mid_song_start(this._songPtr) debugVerbose('Song and instruments are loaded') + this.emit("loaded") } _getMissingInstruments (songPtr, missingCount) { @@ -258,6 +259,44 @@ class Timidity extends EventEmitter { } } + render () { + const songLength = this._lib._mid_song_get_total_time(this._songPtr) / 1000 + const buffSize = songLength * SAMPLE_RATE + const _array = new Int16Array(BUFFER_SIZE * 2) + const arrays = [new Float32Array(buffSize), new Float32Array(buffSize)] + let sampleCount = -1 + let lastSample = 0 + + while (sampleCount) { + const byteCount = this._lib._mid_song_read_wave( + this._songPtr, + this._bufferPtr, + BUFFER_SIZE * BYTES_PER_SAMPLE + ) + sampleCount = byteCount / BYTES_PER_SAMPLE + + if (byteCount) { + _array.set( + this._lib.HEAP16.subarray(this._bufferPtr / 2, (this._bufferPtr + byteCount) / 2) + ) + + for (let i = 0; i < sampleCount; i++) { + arrays[0][i + lastSample] = _array[i * 2] / 0x7FFF + arrays[1][i + lastSample] = _array[i * 2 + 1] / 0x7FFF + } + lastSample = lastSample + sampleCount + this.emit("progress", lastSample, buffSize); + } + } + + // reset in case the user wants to play + this.seek(0) + this.pause() + this._lib._mid_song_start(this._songPtr) + + return arrays; + } + _onAudioProcess (event) { const sampleCount = (this._songPtr && this._playing) ? this._readMidiData() From dc448ff6b0763b30197b1ea0d8ce3570a3e74aa5 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Mon, 31 May 2021 11:49:44 +0800 Subject: [PATCH 2/2] Fix Travis CI standard linting issues. --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 3530b59..756fa03 100644 --- a/index.js +++ b/index.js @@ -148,7 +148,7 @@ class Timidity extends EventEmitter { this._songPtr = songPtr this._lib._mid_song_start(this._songPtr) debugVerbose('Song and instruments are loaded') - this.emit("loaded") + this.emit('loaded') } _getMissingInstruments (songPtr, missingCount) { @@ -285,7 +285,7 @@ class Timidity extends EventEmitter { arrays[1][i + lastSample] = _array[i * 2 + 1] / 0x7FFF } lastSample = lastSample + sampleCount - this.emit("progress", lastSample, buffSize); + this.emit('progress', lastSample, buffSize) } } @@ -294,7 +294,7 @@ class Timidity extends EventEmitter { this.pause() this._lib._mid_song_start(this._songPtr) - return arrays; + return arrays } _onAudioProcess (event) {