Loading files
This 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
}
}
}
ffe63902dThis file is pinned to the selected release. Download the original to open it in your design tool.