UNPKG

@voice-ping/cognitive-services-speech

Version:

VoicePing Cognitive Services Speech SDK for JavaScript forked from Microsoft

160 lines (158 loc) 7.65 kB
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import { connectivity, type, } from "../common.speech/Exports"; import { AudioSourceErrorEvent, AudioSourceInitializingEvent, AudioSourceOffEvent, AudioSourceReadyEvent, AudioStreamNodeAttachedEvent, AudioStreamNodeAttachingEvent, AudioStreamNodeDetachedEvent, AudioStreamNodeErrorEvent, ChunkedArrayBufferStream, createNoDashGuid, Deferred, Events, EventSource, PromiseHelper, } from "../common/Exports"; import { AudioStreamFormat } from "../sdk/Audio/AudioStreamFormat"; export class FileAudioSource { constructor(file, audioSourceId) { this.privStreams = {}; this.turnOn = () => { if (typeof FileReader === "undefined") { const errorMsg = "Browser does not support FileReader."; this.onEvent(new AudioSourceErrorEvent(errorMsg, "")); // initialization error - no streamid at this point return PromiseHelper.fromError(errorMsg); } else if (this.privFile.name.lastIndexOf(".wav") !== this.privFile.name.length - 4) { const errorMsg = this.privFile.name + " is not supported. Only WAVE files are allowed at the moment."; this.onEvent(new AudioSourceErrorEvent(errorMsg, "")); return PromiseHelper.fromError(errorMsg); } this.onEvent(new AudioSourceInitializingEvent(this.privId)); // no stream id this.onEvent(new AudioSourceReadyEvent(this.privId)); return PromiseHelper.fromResult(true); }; this.id = () => { return this.privId; }; this.attach = (audioNodeId) => { this.onEvent(new AudioStreamNodeAttachingEvent(this.privId, audioNodeId)); return this.upload(audioNodeId).onSuccessContinueWith((stream) => { this.onEvent(new AudioStreamNodeAttachedEvent(this.privId, audioNodeId)); return { detach: () => { stream.readEnded(); delete this.privStreams[audioNodeId]; this.onEvent(new AudioStreamNodeDetachedEvent(this.privId, audioNodeId)); this.turnOff(); }, id: () => { return audioNodeId; }, read: () => { return stream.read(); }, }; }); }; this.detach = (audioNodeId) => { if (audioNodeId && this.privStreams[audioNodeId]) { this.privStreams[audioNodeId].close(); delete this.privStreams[audioNodeId]; this.onEvent(new AudioStreamNodeDetachedEvent(this.privId, audioNodeId)); } }; this.turnOff = () => { for (const streamId in this.privStreams) { if (streamId) { const stream = this.privStreams[streamId]; if (stream && !stream.isClosed) { stream.close(); } } } this.onEvent(new AudioSourceOffEvent(this.privId)); // no stream now return PromiseHelper.fromResult(true); }; this.readHeader = () => { // Read the wave header. const header = this.privFile.slice(0, 44); const headerReader = new FileReader(); const headerResult = new Deferred(); const processHeader = (event) => { const header = event.target.result; const view = new DataView(header); // RIFF 4 bytes. const riff = String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3)); if ("RIFF" !== riff) { headerResult.reject("Invalid WAV header in file, RIFF was not found"); } // length, 4 bytes // RIFF Type & fmt 8 bytes const type = String.fromCharCode(view.getUint8(8), view.getUint8(9), view.getUint8(10), view.getUint8(11), view.getUint8(12), view.getUint8(13), view.getUint8(14)); if ("WAVEfmt" !== type) { headerResult.reject("Invalid WAV header in file, WAVEfmt was not found"); } const channelCount = view.getUint16(22, true); const sampleRate = view.getUint32(24, true); const bitsPerSample = view.getUint16(34, true); headerResult.resolve(AudioStreamFormat.getWaveFormatPCM(sampleRate, bitsPerSample, channelCount)); }; headerReader.onload = processHeader; headerReader.readAsArrayBuffer(header); return headerResult.promise(); }; this.upload = (audioNodeId) => { return this.turnOn() .onSuccessContinueWithPromise((_) => { return this.privAudioFormatPromise.onSuccessContinueWith((format) => { const fileStream = new ChunkedArrayBufferStream(3200); const reader = new FileReader(); const stream = new ChunkedArrayBufferStream(format.avgBytesPerSec / 10, audioNodeId); this.privStreams[audioNodeId] = stream; const processFile = (event) => { if (stream.isClosed) { return; // output stream was closed (somebody called TurnOff). We're done here. } stream.writeStreamChunk({ buffer: reader.result, isEnd: false, timeReceived: Date.now(), }); stream.close(); }; reader.onload = processFile; reader.onerror = (event) => { const errorMsg = `Error occurred while processing '${this.privFile.name}'. ${event}`; this.onEvent(new AudioStreamNodeErrorEvent(this.privId, audioNodeId, errorMsg)); throw new Error(errorMsg); }; const chunk = this.privFile.slice(44); reader.readAsArrayBuffer(chunk); return stream; }); }); }; this.onEvent = (event) => { this.privEvents.onEvent(event); Events.instance.onEvent(event); }; this.privId = audioSourceId ? audioSourceId : createNoDashGuid(); this.privEvents = new EventSource(); this.privFile = file; // Read the header. this.privAudioFormatPromise = this.readHeader(); } get format() { return this.privAudioFormatPromise; } get blob() { return PromiseHelper.fromResult(this.privFile); } get events() { return this.privEvents; } get deviceInfo() { return this.privAudioFormatPromise.onSuccessContinueWithPromise((result) => { return PromiseHelper.fromResult({ bitspersample: result.bitsPerSample, channelcount: result.channels, connectivity: connectivity.Unknown, manufacturer: "Speech SDK", model: "File", samplerate: result.samplesPerSec, type: type.File, }); }); } } //# sourceMappingURL=FileAudioSource.js.map