/* * 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::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]->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 pitchShift( 1, &tmp, &out[i] ); } //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::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