#include "looper.hxx" #include "jack.hxx" #include "eventhandler.hxx" #include "controllerupdater.hxx" extern Jack* jack; Looper::Looper(int t) : track(t), state(STATE_STOPPED), fpb(120), gain(1.f), numBeats (4), playedBeats(0), stopRecordOnBar(false), endPoint (0), playPoint (0), lastWrittenSampleIndex(0) { // init faust pitch shift variables fSamplingFreq = 44100; IOTA = 0; int bufferSize = 1024; for ( int i = 0; i < bufferSize; i++) tmpBuffer.push_back(0.f); for (int i=0; i<65536; i++) fVec0[i] = 0; semitoneShift = 0.0f; windowSize = 1000; crossfadeSize = 1000; for (int i=0; i<2; i++) fRec0[i] = 0; } void Looper::midi(unsigned char* data) { if ( data[0] - 144 == track ) { switch ( data[1] ) { case 48: setState( STATE_RECORD_QUEUED ); break; case 53: setState( STATE_PLAY_QUEUED ); break; case 52: setState( STATE_STOPPED ); break; } } else if ( data[0] - 128 == track ) { switch ( data[1] ) { case 48: setState( STATE_STOP_QUEUED ); } } else if ( data[0] - 176 == track ) { switch ( data[1] ) { case 7: gain = int(data[2])/127.f; break; } } } void Looper::setState(State s) { // quantize recording to next bar event if ( state == STATE_RECORDING ) { stopRecordOnBar = true; } state = s; updateControllers(); } void Looper::updateControllers() { if (state == STATE_RECORD_QUEUED ) { numBeats = 0; jack->getControllerUpdator()->recordArm(track, true); jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_RECORD_QUEUED); } else if (state == STATE_RECORDING ) { jack->getControllerUpdator()->recordArm(track, true); jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_RECORDING); } else { jack->getControllerUpdator()->recordArm(track, false); } if (state == STATE_PLAY_QUEUED ) { jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_PLAY_QUEUED); } if ( state == STATE_PLAYING ) { jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_PLAYING); } if (state == STATE_STOP_QUEUED ) { jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_LOADED); } else if ( state == STATE_STOPPED ) { jack->getControllerUpdator()->clipSelect(track, currentClip, Controller::CLIP_MODE_LOADED); } } void Looper::process(int nframes, Buffers* buffers) { float* in = buffers->audio[Buffers::MASTER_INPUT]; float* out = buffers->audio[Buffers::MASTER_OUTPUT]; float playbackSpeed = endPoint / ( float(numBeats) * fpb ); semitoneShift = -( 12 * log ( playbackSpeed ) / log (2) ); if ( state == STATE_PLAYING ) { for(int i = 0; i < nframes; i++) { if ( playPoint < endPoint ) { tmpBuffer[i] = sample[int(playPoint)] * gain; } playPoint += playbackSpeed; } // now pitch-shift the audio in the buffer pitchShift( nframes, &tmpBuffer[0], out); float prog = (float(playPoint) / (fpb*numBeats)); EventLooperProgress e(track, prog ); writeToGuiRingbuffer( &e ); } // stopRecordOnBar ensures we record right up to the bar measure else if ( state == STATE_RECORDING || stopRecordOnBar ) { for(int i = 0; i < nframes; i++) { if ( lastWrittenSampleIndex < 44100 * 60 ) { sample[lastWrittenSampleIndex++] = in[i]; } } } } void Looper::bar() { int barTmpState = state; // queue stop recording -> stop recording, now calculate beats in loop if ( stopRecordOnBar ) { stopRecordOnBar = false; } if ( playedBeats >= numBeats ) { playPoint = 0; playedBeats = 0; } if ( state == STATE_PLAY_QUEUED ) { EventGuiPrint e( "Looper Q->Playing" ); writeToGuiRingbuffer( &e ); state = STATE_PLAYING; playPoint = 0; endPoint = lastWrittenSampleIndex; } if ( state == STATE_RECORD_QUEUED ) { EventGuiPrint e( "Looper Q->Recording" ); writeToGuiRingbuffer( &e ); state = STATE_RECORDING; playPoint = 0; endPoint = 0; lastWrittenSampleIndex = 0; } if ( state == STATE_PLAY_QUEUED ) { EventGuiPrint e( "Looper Q->Stopped" ); writeToGuiRingbuffer( &e ); state = STATE_STOPPED; endPoint = lastWrittenSampleIndex; } if ( barTmpState != state ) { updateControllers(); } } void Looper::beat() { if (state == STATE_RECORDING || stopRecordOnBar ) { numBeats++; } playedBeats++; } void Looper::setLoopLength(float l) { numBeats *= l; // smallest loop = 4 beats if ( numBeats < 4 ) numBeats = 4; char buffer [50]; sprintf (buffer, "Looper loop lenght = %i", numBeats ); EventGuiPrint e( buffer ); writeToGuiRingbuffer( &e ); } 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