-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DLSpeech: Fix various issues (#2671)
* Bump tarball * Set output format * Fix iOS on sample rate * Use Array.fill for better performance * Interpolate when upsampling * Adding multi-buffering player * Fix Safari missing copyToChannel function * Add entry * Update SHA for tarball * Fix test * Fix tests * Apply suggestions from code review Co-Authored-By: Corina <[email protected]> * Link to issue * Apply PR comments
- Loading branch information
Showing
8 changed files
with
265 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 6 additions & 1 deletion
7
packages/directlinespeech/__tests__/utilities/MockAudioContext.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+1.5 KB
(100%)
...ages/directlinespeech/external/microsoft-cognitiveservices-speech-sdk-1.6.0-alpha.0.1.tgz
Binary file not shown.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
packages/directlinespeech/src/createMultiBufferingPlayer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// Currently, Web Chat uses a triple-buffer approach. | ||
const NUM_BUFFER = 3; | ||
|
||
function zeroBuffer(buffer) { | ||
const channels = buffer.numberOfChannels; | ||
|
||
for (let channel = 0; channel < channels; channel++) { | ||
const audioData = buffer.getChannelData(channel); | ||
|
||
[].fill.call(audioData, 0); | ||
} | ||
} | ||
|
||
function copyBuffer(buffer, multiChannelArrayBuffer) { | ||
const channels = buffer.numberOfChannels; | ||
|
||
for (let channel = 0; channel < channels; channel++) { | ||
const arrayBuffer = multiChannelArrayBuffer[channel]; | ||
|
||
// Note that Safari does not support AudioBuffer.copyToChannel yet. | ||
if (buffer.copyToChannel) { | ||
buffer.copyToChannel(arrayBuffer, channel); | ||
} else { | ||
const { length: arrayBufferLength } = arrayBuffer; | ||
const perChannelBuffer = buffer.getChannelData(channel); | ||
|
||
for (let offset = 0; offset < arrayBufferLength; offset++) { | ||
perChannelBuffer[offset] = arrayBuffer[offset]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// This is a multi-buffering player. Users can keep pushing buffer to Web Chat. | ||
// The buffer, realized as BufferSource, is queued to AudioContext. | ||
// Data will be queued as quickly and frequently as possible. | ||
// Web Chat does not support progressive buffering (pushing a partial buffer) and there are currently no plans to implement. | ||
|
||
export default function createMultiBufferingPlayer(audioContext, { channels, samplesPerSec }, numSamplePerBuffer) { | ||
const freeBuffers = new Array(NUM_BUFFER) | ||
.fill() | ||
.map(() => audioContext.createBuffer(channels, numSamplePerBuffer, samplesPerSec)); | ||
let queuedBufferSources = []; | ||
let nextSchedule; | ||
|
||
const queue = []; | ||
|
||
const playNext = () => { | ||
if (typeof nextSchedule !== 'number') { | ||
nextSchedule = audioContext.currentTime; | ||
} | ||
|
||
const bufferSource = audioContext.createBufferSource(); | ||
const multiChannelArrayBuffer = queue.shift(); | ||
|
||
if (typeof multiChannelArrayBuffer === 'function') { | ||
// If the queued item is a function, it is because the user called "flush". | ||
// The "flush" function will callback when all queued buffers before the "flush" call have played. | ||
multiChannelArrayBuffer(); | ||
} else if (multiChannelArrayBuffer) { | ||
const nextBuffer = freeBuffers.shift(); | ||
|
||
// If all buffers are currently occupied, prepend the data back to the queue. | ||
// When one of the buffers finish, it will call playNext() again to pick up items from the queue. | ||
if (!nextBuffer) { | ||
queue.unshift(multiChannelArrayBuffer); | ||
|
||
return; | ||
} | ||
|
||
zeroBuffer(nextBuffer); | ||
copyBuffer(nextBuffer, multiChannelArrayBuffer); | ||
|
||
bufferSource.buffer = nextBuffer; | ||
bufferSource.connect(audioContext.destination); | ||
bufferSource.start(nextSchedule); | ||
|
||
// All BufferSource data that is currently queued will be stored at the AudioContext, via bufferSource.start(). | ||
// This is for cancelAll() to effectively cancel all BufferSource queued at the AudioContext. | ||
queuedBufferSources.push(bufferSource); | ||
|
||
nextSchedule += nextBuffer.duration; | ||
|
||
bufferSource.addEventListener('ended', () => { | ||
queuedBufferSources = queuedBufferSources.filter(target => target !== bufferSource); | ||
|
||
// Declare the buffer is free to pick up on the next iteration. | ||
freeBuffers.push(nextBuffer); | ||
playNext(); | ||
}); | ||
} | ||
}; | ||
|
||
return { | ||
cancelAll: () => { | ||
queue.splice(0); | ||
|
||
// Although all buffers are cleared, there are still some BufferSources queued at the AudioContext that need to be stopped. | ||
queuedBufferSources.forEach(bufferSource => bufferSource.stop()); | ||
}, | ||
flush: () => new Promise(resolve => queue.push(resolve)), | ||
push: multiChannelArrayBuffer => { | ||
queue.push(multiChannelArrayBuffer); | ||
|
||
playNext(); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.