Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
9490f6518This file is pinned to the selected release. Download the original to open it in your design tool.
/****************************************************************************
*
* NAME: PitchShift
* VERSION: 1.2
* HOME URL: http://www.dspdimension.com
* KNOWN BUGS: none
*
* SYNOPSIS: Routine for doing pitch shifting while maintaining
* duration using the Short Time Fourier Transform.
*
* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5
* (one octave down) and 2. (one octave up). A value of exactly 1 does not change
* the pitch. numSampsToProcess tells the routine how many samples in indata[0...
* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ...
* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the
* data in-place). fftFrameSize defines the FFT frame size used for the
* processing. Typical values are 1024, 2048 and 4096. It may be any value <=
* MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT
* oversampling factor which also determines the overlap between adjacent STFT
* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is
* recommended for best quality. sampleRate takes the sample rate for the signal
* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in
* indata[] should be in the range [-1.0, 1.0), which is also the output range
* for the data, make sure you scale the data accordingly (for 16bit signed integers
* you would have to divide (and multiply) by 32768).
*
* COPYRIGHT 1999-2006 Stephan M. Bernsee <smb [AT] dspdimension [DOT] com>
*
* The Wide Open License (WOL)
*
* Permission to use, copy, modify, distribute and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice and this license appear in all source copies.
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
* ANY KIND. See http://www.dspguru.com/wol.htm for more information.
*
*****************************************************************************/
import wae from 'web-audio-engine'
const AudioContext = wae.RenderingAudioContext
const audioCtx = new AudioContext()
export default function shiftPitch(audioBuffer, shiftAmount) {
/* monoral */
let in_data_l = audioBuffer.getChannelData(0)
in_data_l = PitchShift(shiftAmount, in_data_l.length, 1024, 10, audioCtx.sampleRate, in_data_l)
audioBuffer.copyToChannel(in_data_l, 0)
}
/*
function loadTheTrack() {
var input = document.createElement('input')
input.type = 'file'
input.style = 'display:none'
input.onchange = function (e) {
var file = e.target.files[0]
console.log(file)
var reader = new FileReader()
reader.onload = function () {
console.log('decoding audio data with' + this.result)
document.getElementById('status').innerText = 'Pitch Shifting (yeah it takes a min)....'
audioCtx.decodeAudioData(
this.result,
(decodedData) => {
var source = audioCtx.createBufferSource() // creates a sound source
source.buffer = decodedData // tell the source which sound to play
source.connect(audioCtx.destination) // connect the source to the context's destination (the speakers)
console.log('starting')
document.getElementById('status').innerText = 'Playing...'
source.start(0) // play the source now
},
(e) => {
alert('Sorry this browser unable to download this file... try Chrome')
}
)
}
reader.readAsArrayBuffer(file)
}
document.getElementById('wrapper').appendChild(input)
input.click()
}
*/
function PitchShift(
/* float[*/ pitchShift,
/* long */ numSampsToProcess,
/* long */ fftFrameSize,
/* long */ osamp,
/* float[*/ sampleRate,
/* float[] */ indata,
) {
/* double */ let magn
/* double */ let phase
/* double */ let tmp
/* double */ let window
/* double */ let real
/* double */ let imag
/* double */ let freqPerBin
/* double */ let expct
/* long */ let i
/* long */ let k
/* long */ let qpd
/* long */ let index
/* long */ let inFifoLatency
/* long */ let stepSize
/* long */ let fftFrameSize2
const MAX_FRAME_LENGTH = 16000
const gInFIFO = new Array(MAX_FRAME_LENGTH).fill(0.0)
const gOutFIFO = new Array(MAX_FRAME_LENGTH).fill(0.0)
const gFFTworksp = new Array(2 * MAX_FRAME_LENGTH).fill(0.0)
const gLastPhase = new Array(MAX_FRAME_LENGTH / 2 + 1).fill(0.0)
const gSumPhase = new Array(MAX_FRAME_LENGTH / 2 + 1).fill(0.0)
const gOutputAccum = new Array(2 * MAX_FRAME_LENGTH).fill(0.0)
const gAnaFreq = new Array(MAX_FRAME_LENGTH).fill(0.0)
const gAnaMagn = new Array(MAX_FRAME_LENGTH).fill(0.0)
const gSynFreq = new Array(MAX_FRAME_LENGTH).fill(0.0)
const gSynMagn = new Array(MAX_FRAME_LENGTH).fill(0.0)
let gRover = 0
/* float[] */ const outdata = indata
/* set up some handy variables */
fftFrameSize2 = Math.trunc(fftFrameSize / 2)
stepSize = Math.trunc(fftFrameSize / osamp)
freqPerBin = sampleRate / /* (double) */ fftFrameSize
expct = (2.0 * Math.PI * /* (double) */ stepSize) / /* (double) */ fftFrameSize
inFifoLatency = Math.trunc(fftFrameSize - stepSize)
if (gRover === 0) gRover = inFifoLatency
/* main processing loop */
for (i = 0; i < numSampsToProcess; i++) {
/* As long as we have not yet collected enough data just read in */
gInFIFO[gRover] = indata[i]
outdata[i] = gOutFIFO[gRover - inFifoLatency]
gRover++
/* now we have enough data for processing */
if (gRover >= fftFrameSize) {
gRover = inFifoLatency
/* do windowing and re,im interleave */
for (k = 0; k < fftFrameSize; k++) {
window = -0.5 * Math.cos((2.0 * Math.PI * /* (double) */ k) / /* (double) */ fftFrameSize) + 0.5
gFFTworksp[2 * k] = /* (float) */ gInFIFO[k] * window
gFFTworksp[2 * k + 1] = 0.0
}
/* ***************** ANALYSIS ******************* */
/* do transform */
ShortTimeFourierTransform(gFFTworksp, fftFrameSize, -1)
/* this is the analysis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* de-interlace FFT buffer */
real = gFFTworksp[2 * k]
imag = gFFTworksp[2 * k + 1]
/* compute magnitude and phase */
magn = 2.0 * Math.sqrt(real * real + imag * imag)
phase = Math.atan2(imag, real)
/* compute phase difference */
tmp = phase - gLastPhase[k]
gLastPhase[k] = /* (float) */ phase
/* subtract expected phase difference */
tmp -= /* (double) */ k * expct
/* map delta phase into +/- Pi interval */
qpd = /* (long) */ Math.trunc(tmp / Math.PI)
if (qpd >= 0) qpd += qpd & 1
else qpd -= qpd & 1
tmp -= Math.PI * /* (double) */ qpd
/* get deviation from bin frequency from the +/- Pi interval */
tmp = (osamp * tmp) / (2.0 * Math.PI)
/* compute the k-th partials' true frequency */
tmp = /* (double) */ k * freqPerBin + tmp * freqPerBin
/* store magnitude and true frequency in analysis arrays */
gAnaMagn[k] = /* (float) */ magn
gAnaFreq[k] = /* (float) */ tmp
}
/* ***************** PROCESSING ******************* */
/* this does the actual pitch shifting */
for (let zero = 0; zero < fftFrameSize; zero++) {
gSynMagn[zero] = 0
gSynFreq[zero] = 0
}
for (k = 0; k <= fftFrameSize2; k++) {
index = /* (long) */ Math.trunc(k * pitchShift)
if (index <= fftFrameSize2) {
gSynMagn[index] += gAnaMagn[k]
gSynFreq[index] = gAnaFreq[k] * pitchShift
}
}
/* ***************** SYNTHESIS ******************* */
/* this is the synthesis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* get magnitude and true frequency from synthesis arrays */
magn = gSynMagn[k]
tmp = gSynFreq[k]
/* subtract bin mid frequency */
tmp -= /* (double) */ k * freqPerBin
/* get bin deviation from freq deviation */
tmp /= freqPerBin
/* take osamp into account */
tmp = (2.0 * Math.PI * tmp) / osamp
/* add the overlap phase advance back in */
tmp += /* (double) */ k * expct
/* accumulate delta phase to get bin phase */
gSumPhase[k] += /* (float) */ tmp
phase = gSumPhase[k]
/* get real and imag part and re-interleave */
gFFTworksp[2 * k] = /* (float) */ magn * Math.cos(phase)
gFFTworksp[2 * k + 1] = /* (float) */ magn * Math.sin(phase)
}
/* zero negative frequencies */
for (k = fftFrameSize + 2; k < 2 * fftFrameSize; k++) gFFTworksp[k] = 0.0
/* do inverse transform */
ShortTimeFourierTransform(gFFTworksp, fftFrameSize, 1)
/* do windowing and add to output accumulator */
for (k = 0; k < fftFrameSize; k++) {
window = -0.5 * Math.cos((2.0 * Math.PI * /* (double) */ k) / /* (double) */ fftFrameSize) + 0.5
gOutputAccum[k] += /* (float) */ (2.0 * window * gFFTworksp[2 * k]) / (fftFrameSize2 * osamp)
}
for (k = 0; k < stepSize; k++) gOutFIFO[k] = gOutputAccum[k]
/* shift accumulator */
//memmove(gOutputAccum, gOutputAccum + stepSize, fftFrameSize * sizeof(float));
for (k = 0; k < fftFrameSize; k++) {
gOutputAccum[k] = gOutputAccum[k + stepSize]
}
/* move input FIFO */
for (k = 0; k < inFifoLatency; k++) gInFIFO[k] = gInFIFO[k + stepSize]
}
}
return outdata
}
function ShortTimeFourierTransform(/* float[] */ fftBuffer, /* long */ fftFrameSize, /* long */ sign) {
/* float */ let wr
/* float */ let wi
/* float */ let arg
/* float */ let temp
/* float */ let tr
/* float */ let ti
/* float */ let ur
/* float */ let ui
/* long */ let i
/* long */ let bitm
/* long */ let j
/* long */ let le
/* long */ let le2
/* long */ let k
for (i = 2; i < 2 * fftFrameSize - 2; i += 2) {
for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) {
if ((i & bitm) !== 0) j++
j <<= 1
}
if (i < j) {
temp = fftBuffer[i]
fftBuffer[i] = fftBuffer[j]
fftBuffer[j] = temp
temp = fftBuffer[i + 1]
fftBuffer[i + 1] = fftBuffer[j + 1]
fftBuffer[j + 1] = temp
}
}
/* long */ const max = /* (long) */ Math.trunc(Math.log(fftFrameSize) / Math.log(2.0) + 0.5)
for (k = 0, le = 2; k < max; k++) {
le <<= 1
le2 = le >> 1
ur = 1.0
ui = 0.0
arg = /* (float) */ Math.PI / (le2 >> 1)
wr = /* (float) */ Math.cos(arg)
wi = /* (float) */ sign * Math.sin(arg)
for (j = 0; j < le2; j += 2) {
for (i = j; i < 2 * fftFrameSize; i += le) {
tr = fftBuffer[i + le2] * ur - fftBuffer[i + le2 + 1] * ui
ti = fftBuffer[i + le2] * ui + fftBuffer[i + le2 + 1] * ur
fftBuffer[i + le2] = fftBuffer[i] - tr
fftBuffer[i + le2 + 1] = fftBuffer[i + 1] - ti
fftBuffer[i] += tr
fftBuffer[i + 1] += ti
}
tr = ur * wr - ui * wi
ui = ur * wi + ui * wr
ur = tr
}
}
}