/*
* Author: Harry van Haaren 2013
* harryhaaren@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "looper.hxx"
#include "config.hxx"
#include
#include "jack.hxx"
#include "audiobuffer.hxx"
#include "eventhandler.hxx"
#include "controllerupdater.hxx"
extern Jack* jack;
Looper::Looper(int t) :
AudioProcessor(),
TimeObserver(),
track(t)
{
uiUpdateConstant= jack->getSamplerate() / 30.f;
uiUpdateCounter = jack->getSamplerate() / 30.f;
// pre-zero the internal sample
//tmpRecordBuffer = (float*)malloc( sizeof(float) * MAX_BUFFER_SIZE );
//memset( tmpRecordBuffer, 0, sizeof(float) * MAX_BUFFER_SIZE );
for(int i = 0; i < 10; i++ )
{
clips[i] = new LooperClip(track, i);
}
tmpBuffer.resize( MAX_BUFFER_SIZE );
fpb = 22050;
// init faust pitch shift variables
fSamplingFreq = jack->getSamplerate();
IOTA = 0;
//tmpRecordBuffer.resize(MAX_BUFFER_SIZE);
for (int i=0; i<65536; i++)
fVec0[i] = 0;
for (int i=0; i<2; i++)
fRec0[i] = 0;
semitoneShift = 0.0f;
windowSize = 1000;
crossfadeSize = 1000;
}
LooperClip* Looper::getClip(int scene)
{
return clips[scene];
}
void Looper::beat()
{
//FIXME: Need to keep looperClips in sync when there exists no int N
// such that playSpeed*N==1
// for(int i=0;igetPlayhead()+1.0;
// long targetFrames = clips[i]->getBeats() * fpb;
// long actualFrames = clips[i]->getActualAudioLength();//getBufferLenght();
// float playSpeed = 1.0;
// if ( targetFrames != 0 && actualFrames != 0 )
// {
// playSpeed = float(actualFrames) / targetFrames;
// }
// clips[i]->setPlayHead(iph-(iph%fpb)*playSpeed);
// }
}
void Looper::setRequestedBuffer(int s, AudioBuffer* ab)
{
clips[s]->setRequestedBuffer( ab );
}
void Looper::setFpb(int f)
{
fpb = f;
}
void Looper::process(unsigned int nframes, Buffers* buffers)
{
// process each clip individually: this allows for playback of one clip,
// while another clip records.
for ( int clip = 0; clip < NSCENES; clip++ )
{
// handle state of clip, and do what needs doing:
// record into buffer, play from buffer, etc
if ( clips[clip]->recording() )
{
if ( clips[clip]->recordSpaceAvailable() < LOOPER_SAMPLES_BEFORE_REQUEST &&
!clips[clip]->newBufferInTransit() )
{
EventLooperClipRequestBuffer e( track, clip, clips[clip]->audioBufferSize() + LOOPER_SAMPLES_UPDATE_SIZE);
writeToGuiRingbuffer( &e );
clips[clip]->newBufferInTransit(true);
}
// copy data from input buffer to recording buffer
float* input = buffers->audio[Buffers::MASTER_INPUT];
clips[clip]->record( nframes, input, 0 );
}
else if ( clips[clip]->playing() )
{
// copy data into tmpBuffer, then pitch-stretch into track buffer
long targetFrames = clips[clip]->getBeats() * fpb;
long actualFrames = clips[clip]->getActualAudioLength();//getBufferLenght();
float playSpeed = 1.0;
if ( targetFrames != 0 && actualFrames != 0 )
{
playSpeed = float(actualFrames) / targetFrames;
}
float* out = buffers->audio[Buffers::TRACK_0 + track];
for(unsigned int i = 0; i < nframes; i++ )
{
// REFACTOR into system that is better than per sample function calls
float tmp = clips[clip]->getSample(playSpeed);
float deltaPitch = 12 * log ( playSpeed ) / log (2);
semitoneShift = -deltaPitch;
// write the pitch-shifted signal to the track buffer
//FIXME: pitchShift adds delay even for playSpeed = 1.0!!
//we should use something better (e.g librubberband)
if(playSpeed!=1.0f)
pitchShift( 1, &tmp, &out[i] );
else
out[i]+=tmp;
}
//printf("Looper %i playing(), speed = %f\n", track, playSpeed );
if ( uiUpdateCounter > uiUpdateConstant )
{
jack->getControllerUpdater()->setTrackSceneProgress(track, clip, clips[clip]->getProgress() );
uiUpdateCounter = 0;
}
uiUpdateCounter += nframes;
}
}
}
void Looper::resetTimeState()
{
for(int i=0;isetPlayHead(0.0);
}
void Looper::pitchShift(int count, float* input, float* output)
{
float fSlow0 = windowSize;
float fSlow1 = ((1 + fSlow0) - powf(2,(0.08333333333333333f * semitoneShift)));
float fSlow2 = (1.0f / crossfadeSize);
float fSlow3 = (fSlow0 - 1);
float* input0 = &input[0];
//float* output0 = &output[0];
for (int i=0; i