GUACAMOLE-25: Migrate to Lanczos interpolation (a = 3).

This commit is contained in:
Michael Jumper
2016-05-01 00:23:15 -07:00
parent 0c0ee96aaa
commit c137312963

View File

@@ -126,6 +126,17 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {
*/ */
var BUFFER_SIZE = 512; var BUFFER_SIZE = 512;
/**
* The window size to use when applying Lanczos interpolation, commonly
* denoted by the variable "a".
* See: https://en.wikipedia.org/wiki/Lanczos_resampling
*
* @private
* @contant
* @type Number
*/
var LANCZOS_WINDOW_SIZE = 3;
/** /**
* The format of audio this recorder will encode. * The format of audio this recorder will encode.
* *
@@ -218,12 +229,65 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {
*/ */
var processor = null; var processor = null;
/**
* The normalized sinc function. The normalized sinc function is defined as
* 1 for x=0 and sin(PI * x) / (PI * x) for all other values of x.
*
* See: https://en.wikipedia.org/wiki/Sinc_function
*
* @private
* @param {Number} x
* The point at which the normalized sinc function should be computed.
*
* @returns {Number}
* The value of the normalized sinc function at x.
*/
var sinc = function sinc(x) {
// The value of sinc(0) is defined as 1
if (x === 0)
return 1;
// Otherwise, normlized sinc(x) is sin(PI * x) / (PI * x)
var piX = Math.PI * x;
return Math.sin(piX) / piX;
};
/**
* Calculates the value of the Lanczos kernal at point x for a given window
* size. See: https://en.wikipedia.org/wiki/Lanczos_resampling
*
* @private
* @param {Number} x
* The point at which the value of the Lanczos kernel should be
* computed.
*
* @param {Number} a
* The window size to use for the Lanczos kernel.
*
* @returns {Number}
* The value of the Lanczos kernel at the given point for the given
* window size.
*/
var lanczos = function lanczos(x, a) {
// Lanczos is sinc(x) * sinc(x / a) for -a < x < a ...
if (-a < x && x < a)
return sinc(x) * sinc(x / a);
// ... and 0 otherwise
return 0;
};
/** /**
* Determines the value of the waveform represented by the audio data at * Determines the value of the waveform represented by the audio data at
* the given location. If the value cannot be determined exactly as it does * the given location. If the value cannot be determined exactly as it does
* not correspond to an exact sample within the audio data, the value will * not correspond to an exact sample within the audio data, the value will
* be derived through interpolating nearby samples. * be derived through interpolating nearby samples.
* *
* @private
* @param {Float32Array} audioData * @param {Float32Array} audioData
* An array of audio data, as returned by AudioBuffer.getChannelData(). * An array of audio data, as returned by AudioBuffer.getChannelData().
* *
@@ -241,17 +305,19 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {
// Convert [0, 1] range to [0, audioData.length - 1] // Convert [0, 1] range to [0, audioData.length - 1]
var index = (audioData.length - 1) * t; var index = (audioData.length - 1) * t;
// Get the closest whole integer samples indices // Determine the start and end points for the summation used by the
var left = Math.floor(index); // Lanczos interpolation algorithm (see: https://en.wikipedia.org/wiki/Lanczos_resampling)
var right = Math.ceil(index); var start = Math.floor(index) - LANCZOS_WINDOW_SIZE + 1;
var end = Math.floor(index) + LANCZOS_WINDOW_SIZE;
// Pull the values of the closest samples // Calculate the value of the Lanczos interpolation function for the
var leftValue = audioData[left]; // required range
var rightValue = audioData[right]; var sum = 0;
for (var i = start; i <= end; i++) {
sum += (audioData[i] || 0) * lanczos(index - i, LANCZOS_WINDOW_SIZE);
}
// Determine the value of the sample at the given non-integer location return sum;
// through linear interpolation of the nearest samples
return leftValue + (rightValue - leftValue) / (right - left) * (index - left);
}; };