UNPKG

@euirim/microsoft-cognitiveservices-speech-sdk

Version:
147 lines (145 loc) 7.11 kB
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import { AudioStreamFormat } from "../../src/sdk/Audio/AudioStreamFormat"; import { connectivity, type, } from "../common.speech/Exports"; import { AudioSourceErrorEvent, AudioSourceInitializingEvent, AudioSourceOffEvent, AudioSourceReadyEvent, AudioStreamNodeAttachedEvent, AudioStreamNodeAttachingEvent, AudioStreamNodeDetachedEvent, AudioStreamNodeErrorEvent, createNoDashGuid, Events, EventSource, PromiseHelper, Stream, } from "../common/Exports"; 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); } else if (this.privFile.size > FileAudioSource.MAX_SIZE) { const errorMsg = this.privFile.name + " exceeds the maximum allowed file size (" + FileAudioSource.MAX_SIZE + ")."; 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((streamReader) => { this.onEvent(new AudioStreamNodeAttachedEvent(this.privId, audioNodeId)); return { detach: () => { streamReader.close(); delete this.privStreams[audioNodeId]; this.onEvent(new AudioStreamNodeDetachedEvent(this.privId, audioNodeId)); this.turnOff(); }, id: () => { return audioNodeId; }, read: () => { return streamReader.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.upload = (audioNodeId) => { return this.turnOn() .onSuccessContinueWith((_) => { const stream = new Stream(audioNodeId); this.privStreams[audioNodeId] = stream; const reader = new FileReader(); let startOffset = 0; let endOffset = FileAudioSource.CHUNK_SIZE; const processNextChunk = (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(), }); if (endOffset < this.privFile.size) { startOffset = endOffset; endOffset = Math.min(endOffset + FileAudioSource.CHUNK_SIZE, this.privFile.size); const chunk = this.privFile.slice(startOffset, endOffset); reader.readAsArrayBuffer(chunk); } else { // we've written the entire file to the output stream, can close it now. stream.close(); } }; reader.onload = processNextChunk; 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(startOffset, endOffset); reader.readAsArrayBuffer(chunk); return stream.getReader(); }); }; this.onEvent = (event) => { this.privEvents.onEvent(event); Events.instance.onEvent(event); }; this.privId = audioSourceId ? audioSourceId : createNoDashGuid(); this.privEvents = new EventSource(); this.privFile = file; } get format() { return FileAudioSource.FILEFORMAT; } get events() { return this.privEvents; } get deviceInfo() { return PromiseHelper.fromResult({ bitspersample: FileAudioSource.FILEFORMAT.bitsPerSample, channelcount: FileAudioSource.FILEFORMAT.channels, connectivity: connectivity.Unknown, manufacturer: "Speech SDK", model: "File", samplerate: FileAudioSource.FILEFORMAT.samplesPerSec, type: type.File, }); } } // Recommended sample rate (bytes/second). FileAudioSource.SAMPLE_RATE = 16000 * 2; // 16 kHz * 16 bits // We should stream audio at no faster than 2x real-time (i.e., send five chunks // per second, with the chunk size == sample rate in bytes per second * 2 / 5). FileAudioSource.CHUNK_SIZE = FileAudioSource.SAMPLE_RATE * 2 / 5; // 10 seconds of audio in bytes = // sample rate (bytes/second) * 600 (seconds) + 44 (size of the wave header). FileAudioSource.MAX_SIZE = FileAudioSource.SAMPLE_RATE * 600 + 44; FileAudioSource.FILEFORMAT = AudioStreamFormat.getWaveFormatPCM(16000, 16, 1); //# sourceMappingURL=FileAudioSource.js.map