import BaseLfo from '../../core/BaseLfo';
const sqrt = Math.sqrt;
const cos = Math.cos;
const PI = Math.PI;
// Dct Type 2 - orthogonal matrix scaling
function getDctWeights(order, N, type = 'htk') {
const weights = new Float32Array(N * order);
const piOverN = PI / N;
const scale0 = 1 / sqrt(2);
const scale = sqrt(2 / N);
for (let k = 0; k < order; k++) {
const s = (k === 0) ? (scale0 * scale) : scale;
// const s = scale; // rta doesn't apply k=0 scaling
for (let n = 0; n < N; n++)
weights[k * N + n] = s * cos(k * (n + 0.5) * piOverN);
}
return weights;
}
const definitions = {
order: {
type: 'integer',
default: 12,
metas: { kind: 'static' },
},
};
/**
* Compute the Discrete Cosine Transform of an input `signal` or `vector`.
* (HTK style weighting).
*
* _support `standalone` usage_
*
* @memberof module:common.operator
*
* @param {Object} options - Override default parameters.
* @param {Number} [options.order=12] - Number of computed bins.
*
* @example
* import * as lfo from 'waves-lfo/client';
*
* // assuming some audio buffer
* const source = new AudioInBuffer({
* audioBuffer: audioBuffer,
* useWorker: false,
* });
*
* const slicer = new Slicer({
* frameSize: 512,
* hopSize: 512,
* });
*
* const dct = new Dct({
* order: 12,
* });
*
* const logger = new lfo.sink.Logger({ data: true });
*
* source.connect(slicer);
* slicer.connect(dct);
* dct.connect(logger);
*
* source.start();
*/
class Dct extends BaseLfo {
constructor(options = {}) {
super(definitions, options);
}
/** @private */
processStreamParams(prevStreamParams) {
this.prepareStreamParams(prevStreamParams);
const order = this.params.get('order');
const inFrameSize = prevStreamParams.frameSize;
this.streamParams.frameSize = order;
this.streamParams.frameType = 'vector';
this.streamParams.description = [];
this.weightMatrix = getDctWeights(order, inFrameSize);
this.propagateStreamParams();
}
/**
* Use the `Dct` operator in `standalone` mode (i.e. outside of a graph).
*
* @param {Array} values - Input values.
* @return {Array} - Dct of the input array.
*
* @example
* const dct = new lfo.operator.Dct({ order: 12 });
* // mandatory for use in standalone mode
* dct.initStream({ frameSize: 512, frameType: 'signal' });
* dct.inputSignal(data);
*/
inputSignal(values) {
const order = this.params.get('order');
const frameSize = values.length;
const outFrame = this.frame.data;
const weights = this.weightMatrix;
for (let k = 0; k < order; k++) {
const offset = k * frameSize;
outFrame[k] = 0;
for (let n = 0; n < frameSize; n++)
outFrame[k] += values[n] * weights[offset + n];
}
return outFrame;
}
/** @private */
processSignal(frame) {
this.inputSignal(frame.data);
}
/** @private */
processVector(frame) {
this.inputSignal(frame.data);
}
}
export default Dct;