From 99c3f2696f56d4e0596a7840ac93f1dc60c858f3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 3 Apr 2016 14:45:14 -0700 Subject: [PATCH 1/6] GUAC-1511: ArrayBufferWriter must not exceed maximum instruction size of 8192 bytes. --- .../src/main/webapp/modules/ArrayBufferWriter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/ArrayBufferWriter.js b/guacamole-common-js/src/main/webapp/modules/ArrayBufferWriter.js index 733e331b1..8f33dd2be 100644 --- a/guacamole-common-js/src/main/webapp/modules/ArrayBufferWriter.js +++ b/guacamole-common-js/src/main/webapp/modules/ArrayBufferWriter.js @@ -71,13 +71,13 @@ Guacamole.ArrayBufferWriter = function(stream) { var bytes = new Uint8Array(data); // If small enough to fit into single instruction, send as-is - if (bytes.length <= 8064) + if (bytes.length <= 6048) __send_blob(bytes); // Otherwise, send as multiple instructions else { - for (var offset=0; offset Date: Sun, 3 Apr 2016 14:47:33 -0700 Subject: [PATCH 2/6] GUAC-1511: Provide ArrayBufferWriter with ArrayBuffer. Providing 16-bit TypedArray results in truncation of each sample to the low 8 bits. --- guacamole-common-js/src/main/webapp/modules/AudioRecorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index dff35d7de..ac7580a5b 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -237,7 +237,7 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { // Send blobs when audio buffers are received processor.onaudioprocess = function processAudio(e) { - writer.sendData(toSampleArray(e.inputBuffer)); + writer.sendData(toSampleArray(e.inputBuffer).buffer); }; // Connect processing node to user's audio input source From b6e13465a7ea17ada126b3376b4e07ede35726a9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 3 Apr 2016 14:47:54 -0700 Subject: [PATCH 3/6] GUAC-1511: Reduce size of buffer. --- guacamole-common-js/src/main/webapp/modules/AudioRecorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index ac7580a5b..65758822d 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -179,7 +179,7 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { * @private * @type {Number} */ - var bufferSize = format.bytesPerSample * 4096; + var bufferSize = format.bytesPerSample * 256; /** * Converts the given AudioBuffer into an audio packet, ready for streaming From 6a4fab67aa2d2579cc9a2c69e8ea02075ec92c50 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 3 Apr 2016 15:03:15 -0700 Subject: [PATCH 4/6] GUAC-1511: Take number of channels into account when creating SampleArray. --- guacamole-common-js/src/main/webapp/modules/AudioRecorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index 65758822d..5b7b1ca7e 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -199,7 +199,7 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { var toSampleArray = function toSampleArray(audioBuffer) { // Get array for raw PCM storage - var data = new SampleArray(audioBuffer.length); + var data = new SampleArray(audioBuffer.length * format.channels); // Convert each channel for (var channel = 0; channel < format.channels; channel++) { From 77cc8ef72062d64a3afc0fe61a629a11951beda7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 3 Apr 2016 15:04:01 -0700 Subject: [PATCH 5/6] GUAC-1511: Use constant buffer size. --- .../src/main/webapp/modules/AudioRecorder.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index 5b7b1ca7e..9e5fba663 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -114,6 +114,18 @@ Guacamole.AudioRecorder.getInstance = function getInstance(stream, mimetype) { */ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { + /** + * The size of audio buffer to request from the Web Audio API when + * recording or processing audio, in sample-frames. This must be a power of + * two between 256 and 16384 inclusive, as required by + * AudioContext.createScriptProcessor(). + * + * @private + * @constant + * @type {Number} + */ + var BUFFER_SIZE = 512; + /** * The format of audio this recorder will encode. * @@ -171,16 +183,6 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { */ var maxSampleValue = (format.bytesPerSample === 1) ? 128 : 32768; - /** - * The size of audio buffer to request from the Web Audio API when - * recording audio. This must be a power of two between 256 and 16384 - * inclusive, as required by AudioContext.createScriptProcessor(). - * - * @private - * @type {Number} - */ - var bufferSize = format.bytesPerSample * 256; - /** * Converts the given AudioBuffer into an audio packet, ready for streaming * along the underlying output stream. Unlike the raw audio packets used by @@ -232,7 +234,7 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { getUserMedia({ 'audio' : true }, function streamReceived(mediaStream) { // Create processing node which receives appropriately-sized audio buffers - var processor = context.createScriptProcessor(bufferSize, format.channels, format.channels); + var processor = context.createScriptProcessor(BUFFER_SIZE, format.channels, format.channels); processor.connect(context.destination); // Send blobs when audio buffers are received From 1047afbb438e54fa1b0e2c3e9c88435bb31d3234 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 3 Apr 2016 16:16:35 -0700 Subject: [PATCH 6/6] GUAC-1511: Apply basic resampling when copying audio from source to destination. --- .../src/main/webapp/modules/AudioRecorder.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index 9e5fba663..f9de4dc8b 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -200,8 +200,12 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { */ var toSampleArray = function toSampleArray(audioBuffer) { + // Calculate the number of samples in both input and output + var inSamples = audioBuffer.length; + var outSamples = Math.floor(audioBuffer.duration * format.rate); + // Get array for raw PCM storage - var data = new SampleArray(audioBuffer.length * format.channels); + var data = new SampleArray(outSamples * format.channels); // Convert each channel for (var channel = 0; channel < format.channels; channel++) { @@ -210,9 +214,14 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { // Fill array with data from audio buffer channel var offset = channel; - for (var i = 0; i < audioData.length; i++) { - data[offset] = audioData[i] * maxSampleValue; + for (var i = 0; i < outSamples; i++) { + + // Apply naiive resampling + var inOffset = Math.floor(i / outSamples * inSamples); + data[offset] = Math.floor(audioData[inOffset] * maxSampleValue); + offset += format.channels; + } }