I have to create a stereo ping pong delay with these parameters.
• Delay Time (0 – 3000 milliseconds)
• Feedback (0 – 0.99)
• Wet / Dry Mix (0 – 1.0)
I have managed to implement the stereo in/out and the 3 parameters, but struggling with how to implement the ping pong. I have this code in the process block, but it only replays the left and right in the opposite channels once. Is there a simple way to loop this to reply over and over and not just once or have is this not the best way to implement ping pong. Any help would be great!
//ping pong implementation
for (int i = 0; i < buffer.getNumSamples(); i++)
{
// Reduce the amplitude of each sample in the block for the
// left and right channels
//channelDataLeft[i] = channelDataLeft[i] * 0.5;
// channelDataRight[i] = channelDataRight[i] * 0.25;
if (i % 2 == 1) //if i is odd this will play
{
// Calculate the next output sample (current input sample + delayed version)
float outputSampleLeft = (channelDataLeft[i] + (mix * delayDataLeft[readIndex]));
float outputSampleRight = (channelDataRight[i] + (mix * delayDataRight[readIndex]));
// Write the current input into the delay buffer along with the delayed sample
delayDataLeft[writeIndex] = channelDataLeft[i] + (delayDataLeft[readIndex] * feedback);
delayDataRight[writeIndex] = channelDataRight[i] + (delayDataRight[readIndex] * feedback);
// Increment read and write index, check to see if it's greater than buffer length
// if yes, wrap back around to zero
if (++readIndex >= delayBufferLength)
readIndex = 0;
if (++writeIndex >= delayBufferLength)
writeIndex = 0;
// Assign output sample computed above to the output buffer
channelDataLeft[i] = outputSampleLeft;
channelDataRight[i] = outputSampleRight;
}
else //if i is even then this will play
{
// Calculate the next output sample (current input sample + delayed version swapped around from if)
float outputSampleLeft = (channelDataLeft[i] + (mix * delayDataRight[readIndex]));
float outputSampleRight = (channelDataRight[i] + (mix * delayDataLeft[readIndex]));
// Write the current input into the delay buffer along with the delayed sample
delayDataLeft[writeIndex] = channelDataLeft[i] + (delayDataLeft[readIndex] * feedback);
delayDataRight[writeIndex] = channelDataRight[i] + (delayDataRight[readIndex] * feedback);
// Increment read and write index, check to see if it's greater than buffer length
// if yes, wrap back around to zero
if (++readIndex >= delayBufferLength)
readIndex = 0;
if (++writeIndex >= delayBufferLength)
writeIndex = 0;
// Assign output sample computed above to the output buffer
channelDataLeft[i] = outputSampleLeft;
channelDataRight[i] = outputSampleRight;
}
}
Not really sure why you have the modulo one and different behavior based on sample index. A ping-pong delay should have two delay buffers, one for each channel. The input of one stereo channel plus the feedback of the opposite channel's delay buffer should be be fed into each delay.
Here is a good image of the audio signal graph of it:
Here is some pseudo-code of the logic:
float wetDryMix = 0.5f;
float wetFactor = wetDryMix;
float dryFactor = 1.0f - wetDryMix;
float feedback = 0.6f;
int sampleRate = 44100;
int sampleCount = sampleRate * 10;
float[] leftInSamples = new float[sampleCount];
float[] rightInSamples = new float[sampleCount];
float[] leftOutSamples = new float[sampleCount];
float[] rightOutSamples = new float[sampleCount];
int delayBufferSize = sampleRate * 3;
float[] delayBufferLeft = new float[delayBufferSize];
float[] delayBufferRight = new float[delayBufferSize];
int delaySamples = sampleRate / 2;
int delayReadIndex = 0;
int delayWriteIndex = delaySamples;
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
//Read samples in from input
leftChannel = leftInSamples[sampleIndex];
rightChannel = rightInSamples[sampleIndex];
//Make sure delay ring buffer indices are within range
delayReadIndex = delayReadIndex % delayBufferSize;
delayWriteIndex = delayWriteIndex % delayBufferSize;
//Get the current output of delay ring buffer
float delayOutLeft = delayBufferLeft[delayReadIndex];
float delayOutRight = delayBufferRight[delayReadIndex];
//Calculate what is put into delay buffer. It is the current input signal plus the delay output attenuated by the feedback factor
//Notice that the right delay output is fed into the left delay and vice versa
//In this version sound from each stereo channel will ping pong back and forth
float delayInputLeft = leftChannel + delayOutRight * feedback;
float delayInputRight = rightChannel + delayOutLeft * feedback;
//Alternatively you could use a mono signal that is pushed to one delay channel along with the current feedback delay
//This will ping-pong a mixed mono signal between channels
//float delayInputLeft = leftChannel + rightChannel + delayOutRight * feedback;
//float delayInputRight = delayOutLeft * feedback;
//Push the calculated delay value into the delay ring buffers
delayBufferLeft[delayWriteIndex] = delayInputLeft;
delayBufferRight[delayWriteIndex] = delayInputRight;
//Calculate resulting output by mixing the dry input signal with the current delayed output
float outputLeft = leftChannel * dryFactor + delayOutLeft * wetFactor;
float outputRight = rightChannel * dryFactor + delayOutRight * wetFactor;
leftOutSamples[sampleIndex] = outputLeft;
rightOutSamples[sampleIndex] = outputRight;
//Increment ring buffer indices
delayReadIndex++;
delayWriteIndex++;
}
Related
I'm trying to create a simple spectrum via QAudioProbe but my spectrum does not "feel the beat". every bin in spectrum just goes high and low.
Here is my code processing buffer from QAudioProbe :
void Waveform::bufferReady(QAudioBuffer buffer){
int n = buffer.frameCount();
cfg = kiss_fft_alloc(n, 0/*is_inverse_fft*/, NULL, NULL);
QAudioBuffer::S16U *frames = buffer.data<QAudioBuffer::S16U>();
qDeleteAll(m_finalData);
m_finalData.clear();
kiss_fft_cpx output[n],input[n];
for (int i=0; i < n; i++)
{
// frames[i].right contains the i-th sample from the right channel
// frames[i].left contains the i-th sample from the left channel
// if the signal is mono and not stereo, then only one of the channels will have data
qreal hanawindow = 0.5 * (1 - qCos((2 * M_PI * i) / (n - 1)));
input[i].r = frames[i].right * hanawindow; // WindowFunction
input[i].i = 0;
}
kiss_fft(cfg, input, output); // DO FFT
int step = n/(2*60); // distance to take value for bin from list. Here is 60bins
for(int i=0; i< n/2;i+=step){
qreal magnitude = qSqrt(output[i].i*output[i].i + output[i].r*output[i].r);
qreal amplitude = 0.15 * log10(magnitude);
amplitude = qMax(qreal(0.0), amplitude);
amplitude = qMin(qreal(1.0), amplitude);
m_finalData.append(new Sample(amplitude));
}
qDebug() << "Number of Bins : " << m_finalData.count();
emit dataReady();
}
I don't know what are problems with the above code. I've been trying a lot of other ways but the spectrum still weird.
I'm wondering if anyone would possibly be able to give me some advice on how to implement a cross-correlation function within two simple delay lines that I have set up. My problem is that I have two hard coded delay lines that I can manually change to align two signals going in. I'm using a DI signal and a microphone signal from a bass amp. If I use this code in its current state it will delay the DI signal, but what I want it to do, is take the two signals and align them within the DSP for it to output them in phase with one and other. My current code can be seen below:
#include <Bela.h>
#define DELAY_BUFFER_SIZE 44100
// Buffer holding previous samples per channel
float gDelayBuffer_l[DELAY_BUFFER_SIZE] = {0};
float gDelayBuffer_r[DELAY_BUFFER_SIZE] = {0};
// Write pointer
int gDelayBufWritePtr = 0;
// Amount of delay
float gDelayAmount = 1;
// Amount of feedback
float gDelayFeedbackAmount = 0;
// Level of pre-delay input
float gDelayAmountPre = 1;
// Amount of delay in samples
int gDelayInSamples = 22050;
// Buffer holding previous samples per channel
float hDelayBuffer_l[DELAY_BUFFER_SIZE] = {0};
float hDelayBuffer_r[DELAY_BUFFER_SIZE] = {0};
// Write pointer
int hDelayBufWritePtr = 0;
// Amount of delay
float hDelayAmount = 1;
// Amount of feedback
float hDelayFeedbackAmount = 0;
// Level of pre-delay input
float hDelayAmountPre = 1;
// Amount of delay in samples
int hDelayInSamples = 44100;
bool setup(BelaContext *context, void *userData)
{
return true;
}
void render(BelaContext *context, void *userData)
{
for(unsigned int n = 0; n < context->analogFrames; n++) {
float out_l = 0;
float out_r = 0;
// Read audio inputs
out_l = analogRead(context,n,0);
out_r = analogRead(context,n,1);
// Increment delay buffer write pointer
if(++gDelayBufWritePtr>DELAY_BUFFER_SIZE)
gDelayBufWritePtr = 0;
// Increment delay buffer write pointer
// Calculate the sample that will be written into the delay buffer...
// 1. Multiply the current (dry) sample by the pre-delay gain level (set above)
// 2. Get the previously delayed sample from the buffer, multiply it by the feedback gain and add it to the current sample
float del_input_l = (gDelayAmountPre * out_l + gDelayBuffer_l[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayFeedbackAmount);
float del_input_r = (gDelayAmountPre * out_r + gDelayBuffer_r[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayFeedbackAmount);
// Now we can write it into the delay buffer
gDelayBuffer_l[gDelayBufWritePtr] = del_input_l;
gDelayBuffer_r[gDelayBufWritePtr] = del_input_r;
// Get the delayed sample (by reading `gDelayInSamples` many samples behind our current write pointer) and add it to our output sample
out_l = gDelayBuffer_l[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayAmount;
out_r = gDelayBuffer_r[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayAmount;
// Write the sample into the output buffer
analogWrite(context, n, 0, out_l);
analogWrite(context, n, 1, out_r);
}
for(unsigned int n = 0; n < context->analogFrames; n++) {
float out_l = 0;
float out_r = 0;
// Read audio inputs
out_l = analogRead(context,n,2);
out_r = analogRead(context,n,3);
// Increment delay buffer write pointer
if(++hDelayBufWritePtr>DELAY_BUFFER_SIZE)
hDelayBufWritePtr = 0;
// Increment delay buffer write pointer
if(++hDelayBufWritePtr>DELAY_BUFFER_SIZE)
hDelayBufWritePtr = 0;
// Calculate the sample that will be written into the delay buffer...
// 1. Multiply the current (dry) sample by the pre-delay gain level (set above)
// 2. Get the previously delayed sample from the buffer, multiply it by the feedback gain and add it to the current sample
float del_input_l = (hDelayAmountPre * out_l + hDelayBuffer_l[(hDelayBufWritePtr-hDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * hDelayFeedbackAmount);
float del_input_r = (hDelayAmountPre * out_r + hDelayBuffer_r[(hDelayBufWritePtr-hDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * hDelayFeedbackAmount);
// Now we can write it into the delay buffer
hDelayBuffer_l[hDelayBufWritePtr] = del_input_l;
hDelayBuffer_r[hDelayBufWritePtr] = del_input_r;
// Get the delayed sample (by reading `gDelayInSamples` many samples behind our current write pointer) and add it to our output sample
out_l = hDelayBuffer_l[(hDelayBufWritePtr-hDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * hDelayAmount;
out_r = hDelayBuffer_r[(hDelayBufWritePtr-hDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * hDelayAmount;
// Write the sample into the output buffer
analogWrite(context, n, 2, out_l);
analogWrite(context, n, 3, out_r);
}
}
void cleanup(BelaContext *context, void *userData)
{
}
I am new to plug in Development and C++. I was trying to write a chorus plug in using XCode Audio Unit Template. However, when I test the plug in with a sine wave I can hear some mild distortion. I believe I did something wrong with the interpolation technique I am using even though I went through it a thousand times and could not figure out what I did wrong. Here is the code I have written that includes the important parts of the audio unit:
private: //state variables...
enum {kWaveArraySize = 2000}; //number of points in the LFO sine array to hold the points
float mSine[kWaveArraySize];
float *waveArrayPointer; //pointer to point in the array Variable to hold Sampling Rate
Float32 SR;
long mSamplesProcessed; //variable to keep track of samples processed
enum {sampleLimit = (int)10E6}; //limit to reset sine wave
float mCurrentScale, mNextScale; //scaling factor for the LFO sine
TAUBuffer<Float32> Buffer; //circular buffer
Float32 rawIndex; //raw read Index
UInt32 ReadIndex, NextIndex; //the Read Index and the sample after the Read Index for Linear Interpolation
UInt32 WriteIndex; //the Write Index
UInt32 BufferSize; //Size of Buffer
UInt32 MaxBufferSize; //Allocated Number of Samples
Float32 DelayTime; //Delay Time going to be calculated according to LFO
Float32 inputSample, outputSample,
freq, Depth, //Variables to hold the frequency of the LFO and Depth parameter
samplesPerCycle, //number of samples per LFO cycle
InterpOutput, //interpolated output variable
fracDelay, DryValue, WetValue; //fractional Delay, Dry and Wet value variables
VibratoUnit::VibratoUnitKernel::VibratoUnitKernel (AUEffectBase *inAudioUnit) : AUKernelBase (inAudioUnit),
mSamplesProcessed(0), mCurrentScale(0)
{
for (int i = 0; i<kWaveArraySize; ++i) //loop to calculate one cycle of LFO
{
double radians = i * 2.0 * pi / kWaveArraySize;
mSine[i] = (sin(radians) + 1.0) * 0.5;
}
SR = GetSampleRate();
BufferSize = SR;
MaxBufferSize = BufferSize + 20;
Buffer.AllocateClear(MaxBufferSize);
ReadIndex = MaxBufferSize - 1;
WriteIndex = MaxBufferSize - 1; //Give both ReadIndex and WriteIndex a Value outside the buffer so they would be reset to 0 in the process method
void VibratoUnit::VibratoUnitKernel::Reset() //Reset and clear
{
mCurrentScale = 0;
mSamplesProcessed = 0;
Buffer.Clear();
}
//------------------PROCESS METHOD-----------------------//
void VibratoUnit::VibratoUnitKernel::Process( const Float32 *inSourceP,
Float32 *inDestP,
UInt32 inFramesToProcess,
UInt32 inNumChannels,
bool &ioSilence )
{
UInt32 nSampleFrames = inFramesToProcess;
const Float32 *sourceP = inSourceP;
Float32 *destP = inDestP;
freq = GetParameter(kParamFreq);
Depth = GetParameter(kParamDepth);
Depth = (SR/1000.0)*Depth;
WetValue = GetParameter(kParamDryWet);
DryValue = 1.0 - WetValue;
waveArrayPointer = &mSine[0];
samplesPerCycle = SR/freq;
mNextScale = kWaveArraySize/samplesPerCycle;
//----processing loop----//
while (nSampleFrames-- > 0) {
int index = static_cast<long> (mSamplesProcessed * mCurrentScale)%kWaveArraySize; //Find index for in the LFO wave table
if ((mNextScale != mCurrentScale) && (index == 0))
{
mCurrentScale = mNextScale;
mSamplesProcessed = 0; //change LFO in 0 crossing
}
if ((mSamplesProcessed >= sampleLimit) && (index == 0))
{
mSamplesProcessed = 0; // reset samples processed
}
if (WriteIndex >= BufferSize) //reset write Index if goes outside the buffer
{
WriteIndex = 0;
}
inputSample = *sourceP;
sourceP += inNumChannels;
DelayTime = waveArrayPointer[index]; //receive raw sine value between 0 and 1
DelayTime = (Depth*DelayTime)+Depth; //calculate delay value according to sine wave
rawIndex = WriteIndex - DelayTime; //calculate rawIndex relative to the write Index position
if (rawIndex < 0) {
rawIndex = BufferSize + rawIndex;
}
ReadIndex = (UInt32)rawIndex; //calculate readIndex according to rawIndex position
fracDelay = DelayTime - (UInt32)DelayTime; //calculate fractional delay time
NextIndex = ReadIndex + 1; //for interpolation
if (NextIndex >= BufferSize) //bounds checking
{
NextIndex = 0;
}
InterpOutput = (fracDelay*Buffer[ReadIndex]) + ((1.0-fracDelay)*Buffer[NextIndex]); //calculate interpolated value
Buffer[ReadIndex] = InterpOutput; //write the interpolated output to buffer
Buffer[WriteIndex] = inputSample; //write inputsample to buffer
outputSample = (Buffer[ReadIndex]*WetValue) + (inputSample * DryValue); //read output sample from buffer
WriteIndex++; //increment writeIndex
mSamplesProcessed++; //increment samplesprocessed
*destP = outputSample;
destP += inNumChannels;
}
}
Thank you for your help in advance.
I am having trouble understanding a particular area of code in the Steinberg VST Synth example
In this function:
void VstXSynth::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{
float* out1 = outputs[0];
float* out2 = outputs[1];
if (noteIsOn)
{
float baseFreq = freqtab[currentNote & 0x7f] * fScaler;
float freq1 = baseFreq + fFreq1; // not really linear...
float freq2 = baseFreq + fFreq2;
float* wave1 = (fWaveform1 < .5) ? sawtooth : pulse;
float* wave2 = (fWaveform2 < .5) ? sawtooth : pulse;
float wsf = (float)kWaveSize;
float vol = (float)(fVolume * (double)currentVelocity * midiScaler);
VstInt32 mask = kWaveSize - 1;
if (currentDelta > 0)
{
if (currentDelta >= sampleFrames) // future
{
currentDelta -= sampleFrames;
return;
}
memset (out1, 0, currentDelta * sizeof (float));
memset (out2, 0, currentDelta * sizeof (float));
out1 += currentDelta;
out2 += currentDelta;
sampleFrames -= currentDelta;
currentDelta = 0;
}
// loop
while (--sampleFrames >= 0)
{
// this is all very raw, there is no means of interpolation,
// and we will certainly get aliasing due to non-bandlimited
// waveforms. don't use this for serious projects...
(*out1++) = wave1[(VstInt32)fPhase1 & mask] * fVolume1 * vol;
(*out2++) = wave2[(VstInt32)fPhase2 & mask] * fVolume2 * vol;
fPhase1 += freq1;
fPhase2 += freq2;
}
}
else
{
memset (out1, 0, sampleFrames * sizeof (float));
memset (out2, 0, sampleFrames * sizeof (float));
}
}
The way I understand the function is that if a midi note is currently on, we need to copy our wave table into the outputs array to pass back to the VstHost. What I don't understand specifically is what the area in the if (currentDelta > 0) conditional block is doing. It seems like its just writing zeros to the output arrays...
A full version of the file can be found at http://pastebin.com/SdAXkRyW
The incomming MIDI NoteOn event can have an offset relative to the start of the buffers you receive (called deltaFrames). The currentDelta keeps track of when the note should play relative to the start of the buffers received.
So if the currentDelta > sampleFrames, that means the note should not play in this cycle (future) - early exit.
If the currentDelta is within range of this cycle then the memory is cleared up to the moment the note should produce output (memset) and the pointers are manipulated to make it look like the buffers begin right on the spot where the sound should play - length -sampleFrames- is also adjusted.
Then in the loop the sound is produced.
Hope it helps.
Marc
currently i m working on the Tizen IDE.
I had read the input data from the microPhone and apply the FFT on it... but everytime i gets the nan output.
here is my code..
ShortBuffer *pBuffer1 = pData->AsShortBufferN();
fft = new KissFFT(BUFFER_SIZE);
std::vector<short> input(pBuffer1->GetPointer(),
pBuffer1->GetPointer() + BUFFER_SIZE); // this contains audio data
std::vector<float> specturm(BUFFER_SIZE);
fft->spectrum(input, specturm);
applying FFT..
void KissFFT::spectrum(KissFFTO* fft, std::vector<short>& samples2,
std::vector<float>& spectrum) {
int len = fft->numSamples / 2 + 1;
kiss_fft_scalar* samples = (kiss_fft_scalar*) &samples2[0];
kiss_fftr(fft->config, samples, fft->spectrum);
for (int i = 0; i < len; i++) {
float re = scale(fft->spectrum[i].r) * fft->numSamples;
float im = scale(fft->spectrum[i].i) * fft->numSamples;
if (i > 0)
spectrum[i] = sqrtf(re * re + im * im) / (fft->numSamples / 2);
else
spectrum[i] = sqrtf(re * re + im * im) / (fft->numSamples);
AppLog("specturm %d",spectrum[i]); // everytime returns returns nan output
}
}
KissFFTO* KissFFT::create(int numSamples) {
KissFFTO* fft = new KissFFTO();
fft->config = kiss_fftr_alloc(numSamples/2, 0, NULL, NULL);
fft->spectrum = new kiss_fft_cpx[numSamples / 2 + 1];
fft->numSamples = numSamples;
return fft;
}
In fft->config there should be some parameters about the size of FFT like 2048, 4096, i.e. powers of 2. If you increase this value, you can get more resolution in frequency.