/* * 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 #ifdef DEBUG_TIME #include #endif #include "jack.hxx" #include "audiobuffer.hxx" #include "eventhandler.hxx" #include "controllerupdater.hxx" #include "timemanager.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 < NSCENES; i++ ) { clips[i] = new LooperClip(track, i); } tmpBuffer.resize( MAX_BUFFER_SIZE ); fpb = jack->getTimeManager()->getFpb(); // 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() { //TODO needed? //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(double 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() ) { // copy data from input buffer to recording buffer float* inputL = buffers->audio[Buffers::MASTER_INPUT_L]; float* inputR = buffers->audio[Buffers::MASTER_INPUT_R]; for (unsigned int i = 0; i < nframes; i++ ) { inputL[i] *= jack->getInputVolume(); inputR[i] *= jack->getInputVolume(); } clips[clip]->record( nframes, inputL, inputR); } else if ( clips[clip]->playing() ) { long targetFrames = fpb; long actualFrames = clips[clip]->getRecFpb(); #ifdef DEBUG_TIME cout << "FPB: " << fpb << "\n"; cout << "Target: " << targetFrames << " - Actual: " << actualFrames << "\n"; #endif long double playSpeed = 1.0; if ( targetFrames != 0 && actualFrames != 0 ) { playSpeed = (long double)(actualFrames) / (long double)(targetFrames); #ifdef DEBUG_TIME cout << fixed << setprecision(20) << playSpeed << "\n"; #endif } // index = first-track + (track * channels) int trackoffset = track * NCHANNELS; float* outL = buffers->audio[Buffers::SEND_TRACK_0_L + trackoffset]; float* outR = buffers->audio[Buffers::SEND_TRACK_0_R + trackoffset]; for(unsigned int i = 0; i < nframes; i++ ) { // REFACTOR into system that is better than per sample function calls float tmpL = 0; float tmpR = 0; clips[clip]->getSample(playSpeed, &tmpL, &tmpR); 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(0) { //playSpeed!=1.0f) { pitchShift( 1, &tmpL, &outL[i] ); pitchShift( 1, &tmpR, &outR[i] ); } else { outL[i]+=tmpL; outR[i]+=tmpR; } } //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; iresetPlayHead(); } 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