From c5daccef7c3691aa949ac65d653f068289658c14 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 22 Mar 2017 18:08:44 -0400 Subject: [PATCH] Rename to ADPCMSoundDecoder, and only decode Scratch Storage now handles asset loading, so this class is now just the decoder --- ...PCMSoundLoader.js => ADPCMSoundDecoder.js} | 100 ++++++++---------- src/index.js | 2 +- 2 files changed, 45 insertions(+), 57 deletions(-) rename src/{ADPCMSoundLoader.js => ADPCMSoundDecoder.js} (59%) diff --git a/src/ADPCMSoundLoader.js b/src/ADPCMSoundDecoder.js similarity index 59% rename from src/ADPCMSoundLoader.js rename to src/ADPCMSoundDecoder.js index 3b27dd9..63d4baf 100644 --- a/src/ADPCMSoundLoader.js +++ b/src/ADPCMSoundDecoder.js @@ -3,78 +3,66 @@ var Tone = require('tone'); var log = require('./log'); /** - * Load wav audio files that have been compressed with the ADPCM format. + * Decode wav audio files that have been compressed with the ADPCM format. * This is necessary because, while web browsers have native decoders for many audio * formats, ADPCM is a non-standard format used by Scratch since its early days. * This decoder is based on code from Scratch-Flash: * https://github.com/LLK/scratch-flash/blob/master/src/sound/WAVFile.as * @constructor */ -function ADPCMSoundLoader () { +function ADPCMSoundDecoder () { } /** - * Load an ADPCM sound file from a URL, decode it, and return a promise - * with the audio buffer. - * @param {string} url - a url pointing to the ADPCM wav file + * Decode an ADPCM sound stored in an ArrayBuffer and return a promise + * with the decoded audio buffer. + * @param {ArrayBuffer} audioData - containing ADPCM encoded wav audio * @return {Tone.Buffer} */ -ADPCMSoundLoader.prototype.load = function (url) { +ADPCMSoundDecoder.prototype.decode = function (audioData) { return new Promise(function (resolve, reject) { + var stream = new ArrayBufferStream(audioData); - var request = new XMLHttpRequest(); - request.open('GET', url, true); - request.responseType = 'arraybuffer'; + var riffStr = stream.readUint8String(4); + if (riffStr != 'RIFF') { + log.warn('incorrect adpcm wav header'); + reject(); + } - request.onload = function () { - var audioData = request.response; - var stream = new ArrayBufferStream(audioData); + var lengthInHeader = stream.readInt32(); + if ((lengthInHeader + 8) != audioData.byteLength) { + log.warn('adpcm wav length in header: ' + lengthInHeader + ' is incorrect'); + } - var riffStr = stream.readUint8String(4); - if (riffStr != 'RIFF') { - log.warn('incorrect adpcm wav header'); - reject(); - } + var wavStr = stream.readUint8String(4); + if (wavStr != 'WAVE') { + log.warn('incorrect adpcm wav header'); + reject(); + } - var lengthInHeader = stream.readInt32(); - if ((lengthInHeader + 8) != audioData.byteLength) { - log.warn('adpcm wav length in header: ' + lengthInHeader + ' is incorrect'); - } + var formatChunk = this.extractChunk('fmt ', stream); + this.encoding = formatChunk.readUint16(); + this.channels = formatChunk.readUint16(); + this.samplesPerSecond = formatChunk.readUint32(); + this.bytesPerSecond = formatChunk.readUint32(); + this.blockAlignment = formatChunk.readUint16(); + this.bitsPerSample = formatChunk.readUint16(); + formatChunk.position += 2; // skip extra header byte count + this.samplesPerBlock = formatChunk.readUint16(); + this.adpcmBlockSize = ((this.samplesPerBlock - 1) / 2) + 4; // block size in bytes - var wavStr = stream.readUint8String(4); - if (wavStr != 'WAVE') { - log.warn('incorrect adpcm wav header'); - reject(); - } + var samples = this.imaDecompress(this.extractChunk('data', stream), this.adpcmBlockSize); - var formatChunk = this.extractChunk('fmt ', stream); - this.encoding = formatChunk.readUint16(); - this.channels = formatChunk.readUint16(); - this.samplesPerSecond = formatChunk.readUint32(); - this.bytesPerSecond = formatChunk.readUint32(); - this.blockAlignment = formatChunk.readUint16(); - this.bitsPerSample = formatChunk.readUint16(); - formatChunk.position += 2; // skip extra header byte count - this.samplesPerBlock = formatChunk.readUint16(); - this.adpcmBlockSize = ((this.samplesPerBlock - 1) / 2) + 4; // block size in bytes + // todo: this line is the only place Tone is used here, should be possible to remove + var buffer = Tone.context.createBuffer(1, samples.length, this.samplesPerSecond); - var samples = this.imaDecompress(this.extractChunk('data', stream), this.adpcmBlockSize); - - // todo: this line is the only place Tone is used here, should be possible to remove - var buffer = Tone.context.createBuffer(1, samples.length, this.samplesPerSecond); - - // todo: optimize this? e.g. replace the divide by storing 1/32768 and multiply? - for (var i=0; i