We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
export class AudioFileRequest { private url: string; private extension: string; private async: boolean = true; constructor (url, async) { this.url = url; this.async = async; const splitURL = url.split('.'); this.extension = splitURL[splitURL.length - 1].toLowerCase(); } onSuccess (decoded) {} onFailure (decoded?) {} send () { if (this.extension !== 'wav' && this.extension !== 'aiff' && this.extension !== 'aif') { this.onFailure(); return; } const request = new XMLHttpRequest(); request.open('GET', this.url, this.async); request.overrideMimeType('text/plain; charset=x-user-defined'); request.onreadystatechange = ((event) => { if (request.readyState === 4) { if (request.status === 200 || request.status === 0) { this.handleResponse(request.responseText); } else { this.onFailure(); } } }).bind(this); request.send(null); } handleResponse (data) { let decoder; let decoded; if (this.extension === 'wav') { decoder = new WAVDecoder(); decoded = decoder.decode(data); } else if (this.extension === 'aiff' || this.extension === 'aif') { decoder = new AIFFDecoder(); decoded = decoder.decode(data); } this.onSuccess(decoded); } } export class Decoder { getEmptyChunk () { return { name: '', length: 0 }; } readString (data, offset, length) { return data.slice(offset, offset + length); } readIntL (data, offset, length) { let value = 0; for (let i = 0; i < length; i++) { value = value + ((data.charCodeAt(offset + i) & 0xFF) * Math.pow(2, 8 * i)); } return value; } readChunkHeaderL (data, offset) { const chunk = this.getEmptyChunk(); chunk.name = this.readString(data, offset, 4); chunk.length = this.readIntL(data, offset + 4, 4); return chunk; } readIntB (data, offset, length) { let value = 0; for (let i = 0; i < length; i++) { value = value + ((data.charCodeAt(offset + i) & 0xFF) * Math.pow(2, 8 * (length - i - 1))); } return value; } readChunkHeaderB (data, offset) { const chunk = this.getEmptyChunk(); chunk.name = this.readString(data, offset, 4); chunk.length = this.readIntB(data, offset + 4, 4); return chunk; } readFloatB (data, offset) { let expon = this.readIntB(data, offset, 2); const range = 1 << 16 - 1; if (expon >= range) { expon |= ~(range - 1); } let sign = 1; if (expon < 0) { sign = -1; expon += range; } const himant = this.readIntB(data, offset + 2, 4); const lomant = this.readIntB(data, offset + 6, 4); let value; if (expon === himant && expon === lomant && lomant === 0) { value = 0; } else if (expon === 0x7FFF) { value = Number.MAX_VALUE; } else { expon -= 16383; value = (himant * 0x100000000 + lomant) * Math.pow(2, expon - 63); } return sign * value; } } export class WAVDecoder extends Decoder { decode (data) { const decoded: any = {}; let offset = 0; // Header let chunk = this.readChunkHeaderL(data, offset); offset += 8; if (chunk.name !== 'RIFF') { console.error('File is not a WAV'); return null; } let fileLength = chunk.length; fileLength += 8; const wave = this.readString(data, offset, 4); offset += 4; if (wave !== 'WAVE') { console.error('File is not a WAV'); return null; } let bytesPerSample; let numberOfChannels; let bitDepth; let sampleRate; let channels; while (offset < fileLength) { chunk = this.readChunkHeaderL(data, offset); offset += 8; if (chunk.name === 'fmt ') { // File encoding const encoding = this.readIntL(data, offset, 2); offset += 2; if (encoding !== 0x0001) { // Only support PCM console.error('Cannot decode non-PCM encoded WAV file'); return null; } // Number of channels numberOfChannels = this.readIntL(data, offset, 2); offset += 2; // Sample rate sampleRate = this.readIntL(data, offset, 4); offset += 4; // Ignore bytes/sec - 4 bytes offset += 4; // Ignore block align - 2 bytes offset += 2; // Bit depth bitDepth = this.readIntL(data, offset, 2); bytesPerSample = bitDepth / 8; offset += 2; } else if (chunk.name === 'data') { // Data must come after fmt, so we are okay to use it's variables // here const length = chunk.length / (bytesPerSample * numberOfChannels); channels = []; for (let i = 0; i < numberOfChannels; i++) { channels.push(new Float32Array(length)); } for (let i = 0; i < numberOfChannels; i++) { const channel = channels[i]; for (let j = 0; j < length; j++) { let index = offset; index += (j * numberOfChannels + i) * bytesPerSample; // Sample let value = this.readIntL(data, index, bytesPerSample); // Scale range from 0 to 2**bitDepth -> -2**(bitDepth-1) to // 2**(bitDepth-1) const range = 1 << bitDepth - 1; if (value >= range) { value |= ~(range - 1); } // Scale range to -1 to 1 channel[j] = value / range; } } offset += chunk.length; } else { offset += chunk.length; } } decoded.sampleRate = sampleRate; decoded.bitDepth = bitDepth; decoded.channels = channels; decoded.length = length; return decoded; } } export class AIFFDecoder extends Decoder { decode (data) { const decoded: any = {}; let offset = 0; // Header let chunk = this.readChunkHeaderB(data, offset); offset += 8; if (chunk.name !== 'FORM') { console.error('File is not an AIFF'); return null; } let fileLength = chunk.length; fileLength += 8; const aiff = this.readString(data, offset, 4); offset += 4; if (aiff !== 'AIFF') { console.error('File is not an AIFF'); return null; } let bytesPerSample; let numberOfChannels; let bitDepth; let sampleRate; let channels; while (offset < fileLength) { chunk = this.readChunkHeaderB(data, offset); offset += 8; if (chunk.name === 'COMM') { // Number of channels const numberOfChannels = this.readIntB(data, offset, 2); offset += 2; // Number of samples const length = this.readIntB(data, offset, 4); offset += 4; channels = []; for (let i = 0; i < numberOfChannels; i++) { channels.push(new Float32Array(length)); } // Bit depth bitDepth = this.readIntB(data, offset, 2); bytesPerSample = bitDepth / 8; offset += 2; // Sample rate sampleRate = this.readFloatB(data, offset); offset += 10; } else if (chunk.name === 'SSND') { // Data offset const dataOffset = this.readIntB(data, offset, 4); offset += 4; // Ignore block size offset += 4; // Skip over data offset offset += dataOffset; for (let i = 0; i < numberOfChannels; i++) { const channel = channels[i]; for (let j = 0; j < length; j++) { let index = offset; index += (j * numberOfChannels + i) * bytesPerSample; // Sample let value = this.readIntB(data, index, bytesPerSample); // Scale range from 0 to 2**bitDepth -> -2**(bitDepth-1) to // 2**(bitDepth-1) let range = 1 << bitDepth - 1; if (value >= range) { value |= ~(range - 1); } // Scale range to -1 to 1 channel[j] = value / range; } } offset += chunk.length - dataOffset - 8; } else { offset += chunk.length; } } decoded.sampleRate = sampleRate; decoded.bitDepth = bitDepth; decoded.channels = channels; decoded.length = length; return decoded; } }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
The text was updated successfully, but these errors were encountered: