@euirim/microsoft-cognitiveservices-speech-sdk
Version:
Microsoft Cognitive Services Speech SDK for JavaScript
1 lines • 5.91 kB
Source Map (JSON)
{"version":3,"sources":["src/sdk/Audio/BaseAudioPlayer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAgC,MAAM,YAAY,CAAC;AAI7E;;;;GAIG;AACH,qBAAa,eAAe;IAExB,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,qBAAqB,CAAU;IACvC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAAS;IAE1B;;;OAGG;gBACgB,WAAW,EAAE,iBAAiB;IAIjD;;;OAGG;IACI,eAAe,CAAC,YAAY,EAAE,WAAW,GAAG,IAAI;IAQvD;;OAEG;IACI,SAAS,IAAI,IAAI;IASxB,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;CAkC5B","file":"BaseAudioPlayer.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license.\n\nimport { InvalidOperationError } from \"../../common/Error\";\nimport { AudioStreamFormat, PullAudioInputStreamCallback } from \"../Exports\";\nimport { AudioStreamFormatImpl } from \"./AudioStreamFormat\";\n\ntype AudioDataTypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array;\n/**\n * Base audio player class\n * TODO: Plays only PCM for now.\n * @class\n */\nexport class BaseAudioPlayer {\n\n private audioContext: AudioContext = null;\n private gainNode: GainNode = null;\n private audioFormat: AudioStreamFormatImpl;\n private autoUpdateBufferTimer: any = 0;\n private samples: Float32Array;\n private startTime: number;\n\n /**\n * Creates and initializes an instance of this class.\n * @constructor\n */\n public constructor(audioFormat: AudioStreamFormat) {\n this.init(audioFormat);\n }\n\n /**\n * play Audio sample\n * @param newAudioData audio data to be played.\n */\n public playAudioSample(newAudioData: ArrayBuffer): void {\n this.ensureInitializedContext();\n const audioData = this.formatAudioData(newAudioData);\n const newSamplesData = new Float32Array(this.samples.length + audioData.length);\n newSamplesData.set(this.samples, 0);\n newSamplesData.set(audioData, this.samples.length);\n this.samples = newSamplesData;\n }\n /**\n * stops audio and clears the buffers\n */\n public stopAudio(): void {\n if (this.audioContext !== null) {\n this.samples = new Float32Array();\n clearInterval(this.autoUpdateBufferTimer);\n this.audioContext.close();\n this.audioContext = null;\n }\n }\n\n private init(audioFormat: AudioStreamFormat): void {\n this.audioFormat = audioFormat as AudioStreamFormatImpl;\n this.samples = new Float32Array();\n }\n\n private ensureInitializedContext(): void {\n if (this.audioContext === null) {\n this.createAudioContext();\n const timerPeriod = 200;\n this.autoUpdateBufferTimer = setInterval(() => {\n this.updateAudioBuffer();\n }, timerPeriod);\n }\n }\n\n private createAudioContext(): void {\n // new ((window as any).AudioContext || (window as any).webkitAudioContext)();\n this.audioContext = new AudioContext();\n\n // TODO: Various examples shows this gain node, it does not seem to be needed unless we plan\n // to control the volume, not likely\n this.gainNode = this.audioContext.createGain();\n this.gainNode.gain.value = 1;\n this.gainNode.connect(this.audioContext.destination);\n this.startTime = this.audioContext.currentTime;\n }\n\n private formatAudioData(audioData: ArrayBuffer): Float32Array {\n switch (this.audioFormat.bitsPerSample) {\n case 8:\n return this.formatArrayBuffer(new Int8Array(audioData), 128);\n case 16:\n return this.formatArrayBuffer(new Int16Array(audioData), 32768);\n case 32:\n return this.formatArrayBuffer(new Int32Array(audioData), 2147483648);\n default:\n throw new InvalidOperationError(\"Only WAVE_FORMAT_PCM (8/16/32 bps) format supported at this time\");\n }\n }\n\n private formatArrayBuffer(audioData: AudioDataTypedArray, maxValue: number): Float32Array {\n const float32Data = new Float32Array(audioData.length);\n for (let i = 0; i < audioData.length; i++) {\n float32Data[i] = audioData[i] / maxValue;\n }\n return float32Data;\n }\n\n private updateAudioBuffer(): void {\n if (this.samples.length === 0) {\n return;\n }\n\n const channelCount = this.audioFormat.channels;\n const bufferSource = this.audioContext.createBufferSource();\n const frameCount = this.samples.length / channelCount;\n const audioBuffer = this.audioContext.createBuffer(channelCount, frameCount, this.audioFormat.samplesPerSec);\n\n // TODO: Should we do the conversion in the pushAudioSample instead?\n for (let channel = 0; channel < channelCount; channel++) {\n // Fill in individual channel data\n let channelOffset = channel;\n const audioData = audioBuffer.getChannelData(channel);\n for (let i = 0; i < this.samples.length; i++, channelOffset += channelCount) {\n audioData[i] = this.samples[channelOffset];\n }\n }\n\n if (this.startTime < this.audioContext.currentTime) {\n this.startTime = this.audioContext.currentTime;\n }\n\n bufferSource.buffer = audioBuffer;\n bufferSource.connect(this.gainNode);\n bufferSource.start(this.startTime);\n\n // Make sure we play the next sample after the current one.\n this.startTime += audioBuffer.duration;\n\n // Clear the samples for the next pushed data.\n this.samples = new Float32Array();\n }\n}\n"]}