2013-05-15 02:55:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
#include "looper.hxx"
|
|
|
|
|
2013-07-27 18:24:04 +01:00
|
|
|
#include "config.hxx"
|
|
|
|
|
2013-05-15 02:55:51 +01:00
|
|
|
#include "jack.hxx"
|
2013-05-20 01:08:10 +01:00
|
|
|
#include "audiobuffer.hxx"
|
2013-05-16 13:38:46 +01:00
|
|
|
#include "eventhandler.hxx"
|
2013-05-19 02:01:22 +01:00
|
|
|
#include "controllerupdater.hxx"
|
2013-05-16 13:38:46 +01:00
|
|
|
|
2013-05-15 04:05:36 +01:00
|
|
|
extern Jack* jack;
|
2013-05-15 02:55:51 +01:00
|
|
|
|
2013-05-17 09:24:24 +01:00
|
|
|
Looper::Looper(int t) :
|
2013-07-27 18:34:48 +01:00
|
|
|
track(t)
|
2013-05-17 09:24:24 +01:00
|
|
|
{
|
2013-05-19 22:12:31 +01:00
|
|
|
// pre-zero the internal sample
|
2013-07-27 18:24:04 +01:00
|
|
|
tmpRecordBuffer = (float*)malloc( sizeof(float) * MAX_BUFFER_SIZE );
|
|
|
|
memset( tmpRecordBuffer, 0, sizeof(float) * MAX_BUFFER_SIZE );
|
2013-05-19 22:12:31 +01:00
|
|
|
|
2013-05-17 09:24:24 +01:00
|
|
|
// init faust pitch shift variables
|
|
|
|
fSamplingFreq = 44100;
|
|
|
|
IOTA = 0;
|
2013-07-27 18:24:04 +01:00
|
|
|
for ( int i = 0; i < MAX_BUFFER_SIZE; i++)
|
2013-05-17 09:24:24 +01:00
|
|
|
tmpBuffer.push_back(0.f);
|
|
|
|
for (int i=0; i<65536; i++)
|
|
|
|
fVec0[i] = 0;
|
2013-07-27 18:24:04 +01:00
|
|
|
for (int i=0; i<2; i++)
|
|
|
|
fRec0[i] = 0;
|
2013-05-17 09:24:24 +01:00
|
|
|
semitoneShift = 0.0f;
|
|
|
|
windowSize = 1000;
|
|
|
|
crossfadeSize = 1000;
|
|
|
|
}
|
|
|
|
|
2013-07-27 19:06:28 +01:00
|
|
|
LooperClip* Looper::getClip(int scene)
|
|
|
|
{
|
|
|
|
return &clips[scene];
|
|
|
|
}
|
|
|
|
|
2013-05-18 19:52:12 +01:00
|
|
|
void Looper::midi(unsigned char* data)
|
2013-05-18 16:37:03 +01:00
|
|
|
{
|
2013-07-27 18:24:04 +01:00
|
|
|
/*
|
2013-05-18 19:52:12 +01:00
|
|
|
if ( data[0] - 144 == track )
|
|
|
|
{
|
|
|
|
switch ( data[1] )
|
|
|
|
{
|
|
|
|
case 48: setState( STATE_RECORD_QUEUED ); break;
|
2013-07-27 17:37:19 +01:00
|
|
|
case 53: case 54: case 55: case 56: case 57:
|
|
|
|
setState( STATE_PLAY_QUEUED );
|
|
|
|
setScene( data[1] - 53 );
|
|
|
|
break;
|
|
|
|
case 52: setState( STATE_STOP_QUEUED ); break;
|
2013-05-18 19:52:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( data[0] - 128 == track )
|
|
|
|
{
|
|
|
|
switch ( data[1] )
|
|
|
|
{
|
|
|
|
case 48: setState( STATE_STOP_QUEUED );
|
|
|
|
}
|
|
|
|
}
|
2013-05-18 20:33:13 +01:00
|
|
|
else if ( data[0] - 176 == track )
|
|
|
|
{
|
|
|
|
switch ( data[1] )
|
|
|
|
{
|
2013-07-27 21:59:20 +01:00
|
|
|
//case 7: gain = int(data[2])/127.f; break;
|
|
|
|
case 7:{
|
|
|
|
printf("volue\n");
|
|
|
|
// volume from APC
|
|
|
|
EventTrackVol e( track, data[2] / 127.f );
|
|
|
|
writeToGuiRingbuffer( &e ); }
|
|
|
|
break;
|
2013-05-18 20:33:13 +01:00
|
|
|
}
|
|
|
|
}
|
2013-07-27 18:24:04 +01:00
|
|
|
*/
|
2013-05-18 16:37:03 +01:00
|
|
|
}
|
|
|
|
|
2013-07-26 02:49:23 +01:00
|
|
|
void Looper::setScene( int sc )
|
|
|
|
{
|
|
|
|
// update Looper to play different scene
|
2013-07-27 18:34:48 +01:00
|
|
|
//scene = sc;
|
2013-07-27 18:24:04 +01:00
|
|
|
//sample = samples[scene];
|
2013-07-26 02:49:23 +01:00
|
|
|
}
|
|
|
|
|
2013-07-27 18:24:04 +01:00
|
|
|
//void Looper::setState( State s) {
|
|
|
|
/*
|
2013-05-19 02:01:22 +01:00
|
|
|
// quantize recording to next bar event
|
2013-05-16 16:31:22 +01:00
|
|
|
if ( state == STATE_RECORDING )
|
|
|
|
{
|
|
|
|
stopRecordOnBar = true;
|
|
|
|
}
|
2013-05-18 19:52:12 +01:00
|
|
|
state = s;
|
2013-05-19 02:01:22 +01:00
|
|
|
updateControllers();
|
2013-07-27 18:24:04 +01:00
|
|
|
} */
|
|
|
|
|
2013-05-19 02:01:22 +01:00
|
|
|
|
|
|
|
void Looper::updateControllers()
|
|
|
|
{
|
2013-07-27 18:24:04 +01:00
|
|
|
/*
|
2013-05-18 19:52:12 +01:00
|
|
|
if (state == STATE_RECORD_QUEUED )
|
2013-05-15 02:55:51 +01:00
|
|
|
{
|
2013-05-18 19:52:12 +01:00
|
|
|
numBeats = 0;
|
2013-05-19 02:39:06 +01:00
|
|
|
jack->getControllerUpdater()->recordArm(track, true);
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_RECORD_QUEUED);
|
2013-05-19 02:23:18 +01:00
|
|
|
}
|
|
|
|
else if (state == STATE_RECORDING )
|
|
|
|
{
|
2013-05-19 02:39:06 +01:00
|
|
|
jack->getControllerUpdater()->recordArm(track, true);
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_RECORDING);
|
2013-05-15 02:55:51 +01:00
|
|
|
}
|
2013-05-19 01:26:18 +01:00
|
|
|
else
|
|
|
|
{
|
2013-05-19 02:39:06 +01:00
|
|
|
jack->getControllerUpdater()->recordArm(track, false);
|
2013-05-19 01:26:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state == STATE_PLAY_QUEUED )
|
|
|
|
{
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_PLAY_QUEUED);
|
2013-05-19 02:01:22 +01:00
|
|
|
}
|
2013-05-19 02:23:18 +01:00
|
|
|
|
|
|
|
if ( state == STATE_PLAYING )
|
2013-05-19 02:01:22 +01:00
|
|
|
{
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_PLAYING);
|
2013-05-19 02:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state == STATE_STOP_QUEUED )
|
|
|
|
{
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_STOP_QUEUED);
|
2013-05-19 02:01:22 +01:00
|
|
|
}
|
|
|
|
else if ( state == STATE_STOPPED )
|
|
|
|
{
|
2013-07-26 02:05:35 +01:00
|
|
|
jack->getControllerUpdater()->clipSelect(track, scene, Controller::CLIP_MODE_LOADED);
|
2013-07-30 18:34:47 +01:00
|
|
|
EventLooperProgress e(track, 0 );
|
|
|
|
writeToGuiRingbuffer( &e );
|
2013-05-19 02:01:22 +01:00
|
|
|
}
|
2013-07-27 18:24:04 +01:00
|
|
|
*/
|
2013-05-15 02:55:51 +01:00
|
|
|
}
|
2013-05-16 01:38:11 +01:00
|
|
|
|
2013-07-27 19:06:28 +01:00
|
|
|
void Looper::setSample(int scene, AudioBuffer* ab)
|
2013-05-19 23:57:12 +01:00
|
|
|
{
|
2013-07-27 19:06:28 +01:00
|
|
|
clips[scene].load( ab );
|
2013-07-27 18:24:04 +01:00
|
|
|
/*
|
2013-07-27 17:50:10 +01:00
|
|
|
vector<float>& buf = ab->getData();
|
2013-05-20 01:08:10 +01:00
|
|
|
if ( buf.size() > SAMPLE_SIZE )
|
2013-05-19 23:57:12 +01:00
|
|
|
{
|
2013-05-20 01:08:10 +01:00
|
|
|
EventGuiPrint e( "Looper setSample() ERROR size > incoming sample" );
|
2013-05-19 23:57:12 +01:00
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
}
|
2013-05-20 01:08:10 +01:00
|
|
|
else
|
|
|
|
{
|
2013-07-26 02:49:23 +01:00
|
|
|
char buffer [50];
|
|
|
|
sprintf (buffer, "Looper setSample() writing to scene %i",scene);
|
|
|
|
EventGuiPrint e( buffer );
|
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
|
2013-05-20 01:08:10 +01:00
|
|
|
numBeats = ab->getBeats();
|
2013-07-26 02:49:23 +01:00
|
|
|
float* s = &sample[sc];
|
2013-05-20 01:08:10 +01:00
|
|
|
float* b = &buf[0];
|
2013-07-30 01:17:40 +01:00
|
|
|
for (unsigned int i = 0; i < buf.size(); i++)
|
2013-05-20 01:08:10 +01:00
|
|
|
{
|
|
|
|
*s++ = *b++;
|
|
|
|
}
|
|
|
|
|
|
|
|
endPoint = buf.size();
|
|
|
|
lastWrittenSampleIndex = buf.size();
|
|
|
|
|
|
|
|
//memcpy( &sample[0], &buf[0], buf.size() ); // copy sample data to pre-allocated buffer
|
|
|
|
}
|
2013-07-27 18:24:04 +01:00
|
|
|
*/
|
2013-05-19 23:57:12 +01:00
|
|
|
}
|
|
|
|
|
2013-05-16 13:38:46 +01:00
|
|
|
void Looper::process(int nframes, Buffers* buffers)
|
|
|
|
{
|
|
|
|
float* in = buffers->audio[Buffers::MASTER_INPUT];
|
2013-07-30 03:07:46 +01:00
|
|
|
|
|
|
|
// FIXME:
|
|
|
|
// using the track output causes distortion: clipping / not proper writing.
|
|
|
|
// writing to master fixes issue, so its due to trackOutput or Looper writing...?
|
2013-07-30 18:34:47 +01:00
|
|
|
float* trk = buffers->audio[Buffers::TRACK_0 + track];
|
2013-07-30 00:56:13 +01:00
|
|
|
float* out = buffers->audio[Buffers::MASTER_OUTPUT];
|
2013-05-16 13:38:46 +01:00
|
|
|
|
2013-07-27 19:06:28 +01:00
|
|
|
// process each clip individually: this allows for playback of one clip,
|
|
|
|
// while another clip records.
|
|
|
|
for ( int i = 0; i < NSCENES; i++ )
|
|
|
|
{
|
|
|
|
// handle state of clip, and do what needs doing:
|
|
|
|
// record into buffer, play from buffer, etc
|
|
|
|
if ( clips[i].recording() )
|
|
|
|
{
|
|
|
|
// copy data from input buffer to recording buffer
|
|
|
|
|
|
|
|
if ( clips[i].nframesAvailable() < LOOPER_SAMPLES_BEFORE_REQUEST )
|
|
|
|
{
|
|
|
|
// request bigger buffer for this track/scene
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( clips[i].playing() )
|
|
|
|
{
|
|
|
|
// copy data into tmpBuffer, then pitch-stretch into track buffer
|
|
|
|
for(int i = 0; i < nframes; i++ )
|
|
|
|
{
|
|
|
|
out[i] += clips[i].getSample();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2013-05-17 11:01:56 +01:00
|
|
|
float playbackSpeed = endPoint / ( float(numBeats) * fpb );
|
2013-05-19 02:34:39 +01:00
|
|
|
semitoneShift = -( 12 * log ( playbackSpeed ) / log (2) );
|
2013-05-17 11:01:56 +01:00
|
|
|
|
2013-07-26 02:05:35 +01:00
|
|
|
if ( state == STATE_PLAYING ||
|
2013-07-30 18:34:47 +01:00
|
|
|
state == STATE_STOP_QUEUED )
|
2013-05-16 13:38:46 +01:00
|
|
|
{
|
|
|
|
for(int i = 0; i < nframes; i++)
|
|
|
|
{
|
|
|
|
if ( playPoint < endPoint )
|
|
|
|
{
|
2013-05-19 02:34:39 +01:00
|
|
|
tmpBuffer[i] = sample[int(playPoint)] * gain;
|
2013-05-16 13:38:46 +01:00
|
|
|
}
|
2013-07-30 03:07:46 +01:00
|
|
|
playPoint += 1.0; //playbackSpeed;
|
2013-07-30 00:56:13 +01:00
|
|
|
|
2013-07-30 03:07:46 +01:00
|
|
|
//*out++ = sin( playPoint * 440 * 6.24 );
|
2013-07-30 18:34:47 +01:00
|
|
|
*trk = tmpBuffer[i];
|
|
|
|
*out++ = *trk++;
|
2013-05-16 13:38:46 +01:00
|
|
|
}
|
|
|
|
|
2013-05-18 20:33:13 +01:00
|
|
|
// now pitch-shift the audio in the buffer
|
2013-07-30 00:56:13 +01:00
|
|
|
//pitchShift( nframes, &tmpBuffer[0], out);
|
2013-05-17 11:01:56 +01:00
|
|
|
|
2013-07-30 18:34:47 +01:00
|
|
|
if ( uiUpdateCounter > uiUpdateConstant )
|
|
|
|
{
|
|
|
|
float prog = (float(playPoint) / (fpb*numBeats));
|
|
|
|
EventLooperProgress e(track, prog );
|
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
}
|
|
|
|
uiUpdateCounter += nframes;
|
2013-05-16 13:38:46 +01:00
|
|
|
}
|
2013-07-30 18:34:47 +01:00
|
|
|
/*
|
2013-05-16 16:31:22 +01:00
|
|
|
// stopRecordOnBar ensures we record right up to the bar measure
|
2013-05-16 16:14:14 +01:00
|
|
|
else if ( state == STATE_RECORDING || stopRecordOnBar )
|
2013-05-16 13:38:46 +01:00
|
|
|
{
|
|
|
|
for(int i = 0; i < nframes; i++)
|
|
|
|
{
|
2013-07-30 00:56:13 +01:00
|
|
|
if ( lastWrittenSampleIndex < SAMPLE_SIZE )
|
2013-05-16 13:38:46 +01:00
|
|
|
{
|
|
|
|
sample[lastWrittenSampleIndex++] = in[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-30 18:34:47 +01:00
|
|
|
*/
|
2013-05-16 13:38:46 +01:00
|
|
|
}
|
2013-05-16 15:17:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
void Looper::bar()
|
|
|
|
{
|
2013-07-27 18:24:04 +01:00
|
|
|
/*
|
2013-05-19 02:01:22 +01:00
|
|
|
int barTmpState = state;
|
2013-05-16 18:03:48 +01:00
|
|
|
// queue stop recording -> stop recording, now calculate beats in loop
|
|
|
|
if ( stopRecordOnBar )
|
|
|
|
{
|
|
|
|
stopRecordOnBar = false;
|
|
|
|
}
|
2013-05-16 16:14:14 +01:00
|
|
|
|
2013-05-16 16:31:22 +01:00
|
|
|
if ( playedBeats >= numBeats )
|
2013-05-16 15:17:49 +01:00
|
|
|
{
|
2013-05-16 16:31:22 +01:00
|
|
|
playPoint = 0;
|
|
|
|
playedBeats = 0;
|
2013-05-16 15:17:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( state == STATE_PLAY_QUEUED )
|
|
|
|
{
|
|
|
|
EventGuiPrint e( "Looper Q->Playing" );
|
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
state = STATE_PLAYING;
|
|
|
|
playPoint = 0;
|
|
|
|
endPoint = lastWrittenSampleIndex;
|
2013-07-25 15:20:16 +01:00
|
|
|
|
|
|
|
EventLooperState e2( track, scene, STATE_PLAYING );
|
|
|
|
writeToGuiRingbuffer( &e2 );
|
2013-05-16 15:17:49 +01:00
|
|
|
}
|
2013-05-18 19:52:12 +01:00
|
|
|
if ( state == STATE_RECORD_QUEUED )
|
2013-05-16 15:17:49 +01:00
|
|
|
{
|
|
|
|
EventGuiPrint e( "Looper Q->Recording" );
|
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
|
2013-07-25 15:20:16 +01:00
|
|
|
EventLooperState e2( track, scene, STATE_RECORDING );
|
|
|
|
writeToGuiRingbuffer( &e2 );
|
|
|
|
|
2013-05-16 15:17:49 +01:00
|
|
|
state = STATE_RECORDING;
|
|
|
|
playPoint = 0;
|
|
|
|
endPoint = 0;
|
|
|
|
lastWrittenSampleIndex = 0;
|
|
|
|
}
|
2013-05-19 22:12:31 +01:00
|
|
|
if ( state == STATE_STOP_QUEUED )
|
2013-05-16 15:17:49 +01:00
|
|
|
{
|
|
|
|
EventGuiPrint e( "Looper Q->Stopped" );
|
|
|
|
writeToGuiRingbuffer( &e );
|
|
|
|
|
2013-07-25 15:20:16 +01:00
|
|
|
EventLooperState e2( track, scene, STATE_STOPPED );
|
|
|
|
writeToGuiRingbuffer( &e2 );
|
|
|
|
|
2013-05-16 15:17:49 +01:00
|
|
|
state = STATE_STOPPED;
|
|
|
|
endPoint = lastWrittenSampleIndex;
|
|
|
|
}
|
2013-05-19 02:01:22 +01:00
|
|
|
|
|
|
|
if ( barTmpState != state )
|
|
|
|
{
|
2013-05-19 02:23:18 +01:00
|
|
|
updateControllers();
|
2013-05-19 02:01:22 +01:00
|
|
|
}
|
2013-07-27 18:24:04 +01:00
|
|
|
*/
|
2013-05-16 15:17:49 +01:00
|
|
|
}
|
|
|
|
|
2013-05-16 16:14:14 +01:00
|
|
|
void Looper::beat()
|
|
|
|
{
|
2013-07-27 18:24:04 +01:00
|
|
|
/*
|
2013-05-16 18:03:48 +01:00
|
|
|
if (state == STATE_RECORDING || stopRecordOnBar )
|
2013-05-16 16:14:14 +01:00
|
|
|
{
|
2013-05-16 16:31:22 +01:00
|
|
|
numBeats++;
|
2013-05-16 16:14:14 +01:00
|
|
|
}
|
2013-05-16 18:03:48 +01:00
|
|
|
playedBeats++;
|
2013-07-27 18:24:04 +01:00
|
|
|
*
|
|
|
|
*/
|
2013-05-16 16:14:14 +01:00
|
|
|
}
|
|
|
|
|
2013-05-17 09:24:24 +01:00
|
|
|
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];
|
2013-07-30 01:17:40 +01:00
|
|
|
//float* output0 = &output[0];
|
2013-05-17 09:24:24 +01:00
|
|
|
|
|
|
|
for (int i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
float fTemp0 = (float)input0[i];
|
|
|
|
fVec0[IOTA&65535] = fTemp0;
|
|
|
|
fRec0[0] = fmodf((fRec0[1] + fSlow1),fSlow0);
|
|
|
|
int iTemp1 = int(fRec0[0]);
|
|
|
|
int iTemp2 = (1 + iTemp1);
|
|
|
|
float fTemp3 = min((fSlow2 * fRec0[0]), 1.f );
|
|
|
|
float fTemp4 = (fSlow0 + fRec0[0]);
|
|
|
|
int iTemp5 = int(fTemp4);
|
2013-05-18 20:33:13 +01:00
|
|
|
|
2013-07-26 02:26:12 +01:00
|
|
|
*output++ += (float)(((1 - fTemp3) * (((fTemp4 - iTemp5) *
|
2013-05-17 09:24:24 +01:00
|
|
|
fVec0[(IOTA-int((int((1 + iTemp5)) & 65535)))&65535]) + ((0 - ((
|
|
|
|
fRec0[0] + fSlow3) - iTemp5)) * fVec0[(IOTA-int((iTemp5 & 65535)))
|
|
|
|
&65535]))) + (fTemp3 * (((fRec0[0] - iTemp1) * fVec0[(IOTA-int((int(
|
|
|
|
iTemp2) & 65535)))&65535]) + ((iTemp2 - fRec0[0]) * fVec0[(IOTA-int((
|
|
|
|
iTemp1 & 65535)))&65535]))));
|
2013-05-18 20:33:13 +01:00
|
|
|
|
2013-05-17 09:24:24 +01:00
|
|
|
fRec0[1] = fRec0[0];
|
|
|
|
IOTA = IOTA+1;
|
|
|
|
}
|
|
|
|
}
|