@voice-ping/cognitive-services-speech
Version:
VoicePing Cognitive Services Speech SDK for JavaScript forked from Microsoft
160 lines (158 loc) • 7.65 kB
JavaScript
// 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