@euirim/microsoft-cognitiveservices-speech-sdk
Version:
Microsoft Cognitive Services Speech SDK for JavaScript
147 lines (145 loc) • 7.11 kB
JavaScript
// 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