UNPKG

@euirim/microsoft-cognitiveservices-speech-sdk

Version:
1 lines 9.05 kB
{"version":3,"sources":["src/common.browser/FileAudioSource.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,uCAAuC,CAAC;AACjG,OAAO,EAEH,wBAAwB,EAE3B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEH,gBAAgB,EAUhB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAEhB,OAAO,EAIV,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,eAAgB,YAAW,YAAY;IAGhD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAIxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAA+C;IAIjF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAkD;IAElF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAoG;IAEtI,OAAO,CAAC,WAAW,CAA8C;IAEjE,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,UAAU,CAAgC;IAElD,OAAO,CAAC,QAAQ,CAAO;gBAEJ,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM;aAM1C,MAAM,EAAI,iBAAiB;IAI/B,MAAM,yBAkBZ;IAEM,EAAE,eAER;IAEM,MAAM,qDAqBZ;IAEM,MAAM,gCAMZ;IAEM,OAAO,yBAYb;aAEU,MAAM,EAAI,WAAW,CAAC,gBAAgB,CAAC;aAIvC,UAAU,EAAI,OAAO,CAAC,wBAAwB,CAAC;IAY1D,OAAO,CAAC,MAAM,CA+Cb;IAED,OAAO,CAAC,OAAO,CAGd;CACJ","file":"FileAudioSource.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license.\n\nimport { AudioStreamFormat, AudioStreamFormatImpl } from \"../../src/sdk/Audio/AudioStreamFormat\";\nimport {\n connectivity,\n ISpeechConfigAudioDevice,\n type,\n} from \"../common.speech/Exports\";\nimport {\n AudioSourceErrorEvent,\n AudioSourceEvent,\n AudioSourceInitializingEvent,\n AudioSourceOffEvent,\n AudioSourceReadyEvent,\n AudioStreamNodeAttachedEvent,\n AudioStreamNodeAttachingEvent,\n AudioStreamNodeDetachedEvent,\n AudioStreamNodeErrorEvent,\n createNoDashGuid,\n Events,\n EventSource,\n IAudioSource,\n IAudioStreamNode,\n IStringDictionary,\n Promise,\n PromiseHelper,\n Stream,\n StreamReader,\n} from \"../common/Exports\";\n\nexport class FileAudioSource implements IAudioSource {\n\n // Recommended sample rate (bytes/second).\n private static readonly SAMPLE_RATE: number = 16000 * 2; // 16 kHz * 16 bits\n\n // We should stream audio at no faster than 2x real-time (i.e., send five chunks\n // per second, with the chunk size == sample rate in bytes per second * 2 / 5).\n private static readonly CHUNK_SIZE: number = FileAudioSource.SAMPLE_RATE * 2 / 5;\n\n // 10 seconds of audio in bytes =\n // sample rate (bytes/second) * 600 (seconds) + 44 (size of the wave header).\n private static readonly MAX_SIZE: number = FileAudioSource.SAMPLE_RATE * 600 + 44;\n\n private static readonly FILEFORMAT: AudioStreamFormatImpl = AudioStreamFormat.getWaveFormatPCM(16000, 16, 1) as AudioStreamFormatImpl;\n\n private privStreams: IStringDictionary<Stream<ArrayBuffer>> = {};\n\n private privId: string;\n\n private privEvents: EventSource<AudioSourceEvent>;\n\n private privFile: File;\n\n public constructor(file: File, audioSourceId?: string) {\n this.privId = audioSourceId ? audioSourceId : createNoDashGuid();\n this.privEvents = new EventSource<AudioSourceEvent>();\n this.privFile = file;\n }\n\n public get format(): AudioStreamFormat {\n return FileAudioSource.FILEFORMAT;\n }\n\n public turnOn = (): Promise<boolean> => {\n if (typeof FileReader === \"undefined\") {\n const errorMsg = \"Browser does not support FileReader.\";\n this.onEvent(new AudioSourceErrorEvent(errorMsg, \"\")); // initialization error - no streamid at this point\n return PromiseHelper.fromError<boolean>(errorMsg);\n } else if (this.privFile.name.lastIndexOf(\".wav\") !== this.privFile.name.length - 4) {\n const errorMsg = this.privFile.name + \" is not supported. Only WAVE files are allowed at the moment.\";\n this.onEvent(new AudioSourceErrorEvent(errorMsg, \"\"));\n return PromiseHelper.fromError<boolean>(errorMsg);\n } else if (this.privFile.size > FileAudioSource.MAX_SIZE) {\n const errorMsg = this.privFile.name + \" exceeds the maximum allowed file size (\" + FileAudioSource.MAX_SIZE + \").\";\n this.onEvent(new AudioSourceErrorEvent(errorMsg, \"\"));\n return PromiseHelper.fromError<boolean>(errorMsg);\n }\n\n this.onEvent(new AudioSourceInitializingEvent(this.privId)); // no stream id\n this.onEvent(new AudioSourceReadyEvent(this.privId));\n return PromiseHelper.fromResult(true);\n }\n\n public id = (): string => {\n return this.privId;\n }\n\n public attach = (audioNodeId: string): Promise<IAudioStreamNode> => {\n this.onEvent(new AudioStreamNodeAttachingEvent(this.privId, audioNodeId));\n\n return this.upload(audioNodeId).onSuccessContinueWith<IAudioStreamNode>(\n (streamReader: StreamReader<ArrayBuffer>) => {\n this.onEvent(new AudioStreamNodeAttachedEvent(this.privId, audioNodeId));\n return {\n detach: () => {\n streamReader.close();\n delete this.privStreams[audioNodeId];\n this.onEvent(new AudioStreamNodeDetachedEvent(this.privId, audioNodeId));\n this.turnOff();\n },\n id: () => {\n return audioNodeId;\n },\n read: () => {\n return streamReader.read();\n },\n };\n });\n }\n\n public detach = (audioNodeId: string): void => {\n if (audioNodeId && this.privStreams[audioNodeId]) {\n this.privStreams[audioNodeId].close();\n delete this.privStreams[audioNodeId];\n this.onEvent(new AudioStreamNodeDetachedEvent(this.privId, audioNodeId));\n }\n }\n\n public turnOff = (): Promise<boolean> => {\n for (const streamId in this.privStreams) {\n if (streamId) {\n const stream = this.privStreams[streamId];\n if (stream && !stream.isClosed) {\n stream.close();\n }\n }\n }\n\n this.onEvent(new AudioSourceOffEvent(this.privId)); // no stream now\n return PromiseHelper.fromResult(true);\n }\n\n public get events(): EventSource<AudioSourceEvent> {\n return this.privEvents;\n }\n\n public get deviceInfo(): Promise<ISpeechConfigAudioDevice> {\n return PromiseHelper.fromResult({\n bitspersample: FileAudioSource.FILEFORMAT.bitsPerSample,\n channelcount: FileAudioSource.FILEFORMAT.channels,\n connectivity: connectivity.Unknown,\n manufacturer: \"Speech SDK\",\n model: \"File\",\n samplerate: FileAudioSource.FILEFORMAT.samplesPerSec,\n type: type.File,\n });\n }\n\n private upload = (audioNodeId: string): Promise<StreamReader<ArrayBuffer>> => {\n return this.turnOn()\n .onSuccessContinueWith<StreamReader<ArrayBuffer>>((_: boolean) => {\n const stream = new Stream<ArrayBuffer>(audioNodeId);\n\n this.privStreams[audioNodeId] = stream;\n\n const reader: FileReader = new FileReader();\n\n let startOffset = 0;\n let endOffset = FileAudioSource.CHUNK_SIZE;\n\n const processNextChunk = (event: Event): void => {\n if (stream.isClosed) {\n return; // output stream was closed (somebody called TurnOff). We're done here.\n }\n\n stream.writeStreamChunk({\n buffer: reader.result as ArrayBuffer,\n isEnd: false,\n timeReceived: Date.now(),\n });\n\n if (endOffset < this.privFile.size) {\n startOffset = endOffset;\n endOffset = Math.min(endOffset + FileAudioSource.CHUNK_SIZE, this.privFile.size);\n const chunk = this.privFile.slice(startOffset, endOffset);\n reader.readAsArrayBuffer(chunk);\n } else {\n // we've written the entire file to the output stream, can close it now.\n stream.close();\n }\n };\n\n reader.onload = processNextChunk;\n\n reader.onerror = (event: ProgressEvent) => {\n const errorMsg = `Error occurred while processing '${this.privFile.name}'. ${event}`;\n this.onEvent(new AudioStreamNodeErrorEvent(this.privId, audioNodeId, errorMsg));\n throw new Error(errorMsg);\n };\n\n const chunk = this.privFile.slice(startOffset, endOffset);\n reader.readAsArrayBuffer(chunk);\n\n return stream.getReader();\n });\n }\n\n private onEvent = (event: AudioSourceEvent): void => {\n this.privEvents.onEvent(event);\n Events.instance.onEvent(event);\n }\n}\n"]}