-Loopers have length, UI controls it, 8 tracks, config header, Master track in UI

main
Harry van Haaren 2013-05-16 01:38:11 +01:00
parent 839388f112
commit 8da75fc397
12 changed files with 231 additions and 46 deletions

8
src/config.hxx Normal file
View File

@ -0,0 +1,8 @@
#ifndef LUPPP_CONFIG_H
#define LUPPP_CONFIG_H
#define NTRACKS 8
#endif // LUPPP_CONFIG_H

View File

@ -22,6 +22,8 @@ namespace Event
RECORD,
LOOPER_STATE,
LOOPER_LOOP_LENGTH,
METRONOME_ACTIVE,
};
};
@ -64,6 +66,18 @@ class EventLooperState : public EventBase
EventLooperState(int t, Looper::State s) : track(t), state(s){}
};
class EventLooperLoopLength : public EventBase
{
public:
int type() { return int(LOOPER_LOOP_LENGTH); }
uint32_t size() { return sizeof(EventLooperLoopLength); }
int track;
float scale; // multiply length by this
EventLooperLoopLength(){}
EventLooperLoopLength(int t, float s) : track(t), scale(s){}
};
class EventLoadSample : public EventBase
{
public:

View File

@ -62,6 +62,12 @@ void handleDspEvents()
jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventLooperState) );
jack->setLooperState( ev.track, ev.state );
} break; }
case Event::LOOPER_LOOP_LENGTH: {
if ( availableRead >= sizeof(EventLooperLoopLength) ) {
EventLooperLoopLength ev;
jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventLooperLoopLength) );
jack->setLooperLoopLength( ev.track, ev.scale );
} break; }
default:
{
// just do nothing

134
src/gmastertrack.hxx Normal file
View File

@ -0,0 +1,134 @@
#ifndef LUPPP_G_MASTER_TRACK_H
#define LUPPP_G_MASTER_TRACK_H
#include <iostream>
#include <FL/Fl.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Slider.H>
#include "avtk/avtk_dial.h"
#include "avtk/avtk_button.h"
#include "avtk/avtk_background.h"
#include "eventhandler.hxx"
using namespace std;
static void gmastertrack_button_callback(Fl_Widget *w, void *data) {
int track = 0;
if ( data )
track = *(int*)data;
cout << "Button " << track << " " << w->label() << " clicked" << endl;
if ( strcmp( w->label() , "Rec" ) == 0 )
{
EventLooperState e = EventLooperState(track,Looper::STATE_RECORD_QUEUED);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "Play" ) == 0 )
{
EventLooperState e = EventLooperState(track,Looper::STATE_PLAY_QUEUED);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "Stop" ) == 0 )
{
EventLooperState e = EventLooperState(track,Looper::STATE_STOP_QUEUED);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "+" ) == 0 )
{
EventLooperLoopLength e = EventLooperLoopLength(track, 2);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "-" ) == 0 )
{
EventLooperLoopLength e = EventLooperLoopLength(track, 0.5);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label(), "Metro" ) == 0 )
{
Avtk::Button* b = (Avtk::Button*)w;
b->value( !b->value() );
EventMetronomeActive e = EventMetronomeActive( b->value() );
writeToDspRingbuffer( &e );
}
else
{
cout << __FILE__ << __LINE__ << " Error: unknown command string" << endl;
}
}
class GMasterTrack : public Fl_Group
{
public:
GMasterTrack(int x, int y, int w, int h, const char* l = 0 ) :
Fl_Group(x, y, w, h),
title( strdup(l) ),
bg( x, y , w, h, title ),
/*
button1(x + 5, y + 24, 100, 18,"Rec"),
button2(x + 5, y + 44, 100, 18,"Play"),
button3(x + 5, y + 64, 100, 18,"Stop"),
button4(x + 5, y + 84, 48, 18,"-"),
button5(x +57, y + 84, 48, 18,"+"),
button6(x + 5, y +104, 18, 18,"6"),
*/
metronomeButton(x + 5,y + 24,140,30,"Metro")
/*
dial1(x+15, y +155, 24, 24, "A"),
dial2(x+45, y +155, 24, 24, "B"),
dial3(x+75, y +155, 24, 24, "C")
*/
{
ID = privateID++;
/*
button1.callback( gmastertrack_button_callback, &ID );
button2.callback( gmastertrack_button_callback, &ID );
button3.callback( gmastertrack_button_callback, &ID );
button4.callback( gmastertrack_button_callback, &ID );
button5.callback( gmastertrack_button_callback, &ID );
button6.callback( gmastertrack_button_callback, &ID );
*/
metronomeButton.callback( gmastertrack_button_callback, 0 );
end(); // close the group
}
~GMasterTrack()
{
free(title);
}
private:
int ID;
char* title;
Avtk::Background bg;
/*
Avtk::Button button1;
Avtk::Button button2;
Avtk::Button button3;
Avtk::Button button4;
Avtk::Button button5;
Avtk::Button button6;
*/
Avtk::LightButton metronomeButton;
/*
Avtk::Dial dial1;
Avtk::Dial dial2;
Avtk::Dial dial3;
*/
static int privateID;
};
#endif // LUPPP_G_MASTER_TRACK_H

View File

@ -36,6 +36,16 @@ static void gtrack_button_callback(Fl_Widget *w, void *data) {
EventLooperState e = EventLooperState(track,Looper::STATE_STOP_QUEUED);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "+" ) == 0 )
{
EventLooperLoopLength e = EventLooperLoopLength(track, 2);
writeToDspRingbuffer( &e );
}
else if ( strcmp( w->label() , "-" ) == 0 )
{
EventLooperLoopLength e = EventLooperLoopLength(track, 0.5);
writeToDspRingbuffer( &e );
}
else
{
cout << __FILE__ << __LINE__ << " Error: unknown command string" << endl;
@ -53,9 +63,10 @@ class GTrack : public Fl_Group
button1(x + 5, y + 24, 100, 18,"Rec"),
button2(x + 5, y + 44, 100, 18,"Play"),
button3(x + 5, y + 64, 100, 18,"Stop"),
button4(x + 5, y + 84, 18, 18,"4"),
button5(x + 5, y +104, 18, 18,"5"),
button6(x + 5, y +124, 18, 18,"6"),
button4(x + 5, y + 84, 48, 18,"-"),
button5(x +57, y + 84, 48, 18,"+"),
button6(x + 5, y +104, 18, 18,"6"),
dial1(x+15, y +155, 24, 24, "A"),
dial2(x+45, y +155, 24, 24, "B"),

View File

@ -6,44 +6,34 @@
// Hack, move to gtrack.cpp
int GTrack::privateID = 0;
int GMasterTrack::privateID = 0;
using namespace std;
static void gui_button_callback(Fl_Widget *w, void *data)
{
Avtk::Button* b = (Avtk::Button*)w;
if ( strcmp( w->label(), "Metronome" ) == 0 )
{
b->value( !b->value() );
EventMetronomeActive e = EventMetronomeActive( b->value() );
writeToDspRingbuffer( &e );
}
}
Gui::Gui() :
window(600,280)
window(1200,280)
{
window.color(FL_BLACK);
window.label("Luppp 5");
Avtk::Image* header = new Avtk::Image(0,0,600,36,"header.png");
metronomeButton = new Avtk::LightButton(0,0,200,30,"Metronome");
for (int i = 0; i < 5; i++ )
int i = 0;
for (; i < NTRACKS; i++ )
{
stringstream s;
s << "Track " << i+1;
tracks.push_back( new GTrack(8 + i * 118, 40, 110, 230, s.str().c_str() ) );
}
metronomeButton->callback( gui_button_callback, 0 );
master = new GMasterTrack(9 + i * 118, 40, 150, 230, "Master");
/*
box = new Fl_Box(655, 5, 200, 60, "BPM = 120");
box->box(FL_UP_BOX);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
*/
window.end();
}

View File

@ -8,7 +8,9 @@
#include "avtk/avtk_light_button.h"
#include "config.hxx"
#include "gtrack.hxx"
#include "gmastertrack.hxx"
#include <vector>
@ -23,7 +25,9 @@ class Gui
private:
Fl_Double_Window window;
Fl_Box* box;
Avtk::LightButton* metronomeButton;
GMasterTrack* master;
vector<GTrack*> tracks;
};

View File

@ -47,7 +47,7 @@ Jack::Jack()
cerr << "Jack() error setting timebase callback" << endl;
}
for(int i = 0; i < 5; i++)
for(int i = 0; i < NTRACKS; i++)
{
loopers.push_back( new Looper(i) );
timeManager.registerObserver( loopers.back() );

View File

@ -17,6 +17,7 @@
#include <jack/midiport.h>
#include <jack/transport.h>
#include "config.hxx"
#include "looper.hxx"
#include "metronome.hxx"
#include "timemanager.hxx"
@ -36,6 +37,10 @@ class Jack
{
loopers.at(t)->setState(s);
}
void setLooperLoopLength(int t, float l)
{
loopers.at(t)->setLoopLength(l);
}
Metronome* getMetronome(){return &metronome;}
TimeManager* getTimeManager(){return &timeManager;}

View File

@ -7,19 +7,11 @@ extern Jack* jack;
void Looper::setState(State s)
{
// before update, check if we recording, if so, print info
/*
if ( state == STATE_RECORDING )
// ensure we're not setting eg PLAY_QUEUED, if we're already PLAYING
if ( static_cast<int>(s) != static_cast<int>(state) + 1)
{
int newBpm = 120;// (lastWrittenSampleIndex / (44100/2) ) * 60;
cout << "Looper " << track << " ending record: endPoint @ " << lastWrittenSampleIndex
<< ". Bpm " << newBpm << " perhaps?" << endl;
jack->getTimeManager()->setBpm( newBpm );
cout << "new state " << s << endl;
state = s;
}
*/
// quantize?!
state = s;
}

View File

@ -14,17 +14,19 @@ class Looper : public Observer // for notifications
{
public:
enum State {
STATE_PLAYING = 0x01,
STATE_PLAY_QUEUED = 0x02,
STATE_RECORDING = 0x03,
STATE_RECORD_QUEUED = 0x04,
STATE_STOPPED = 0x05,
STATE_STOP_QUEUED = 0x06,
STATE_PLAYING = 0,
STATE_PLAY_QUEUED,
STATE_RECORDING,
STATE_RECORD_QUEUED,
STATE_STOPPED,
STATE_STOP_QUEUED,
};
Looper(int t) :
track(t),
state(STATE_STOPPED),
numBeats (4),
playedBeats(0),
endPoint (0),
playPoint (0),
lastWrittenSampleIndex(0)
@ -34,8 +36,13 @@ class Looper : public Observer // for notifications
void bar()
{
//cout << "Looper " << track << " got bar()" << flush;
playPoint = 0;
// only reset if we're on the last beat of a loop
if ( playedBeats >= numBeats + 1 )
{
//cout << "Looper " << track << " restting to 0 " << endl;
playPoint = 0;
playedBeats = 0;
}
if ( state == STATE_PLAY_QUEUED )
{
@ -44,7 +51,7 @@ class Looper : public Observer // for notifications
playPoint = 0;
endPoint = lastWrittenSampleIndex;
}
if ( state == STATE_RECORD_QUEUED )
if ( state == STATE_RECORD_QUEUED && state != STATE_RECORDING )
{
cout << " Q->Recording " << endl;
state = STATE_RECORDING;
@ -52,7 +59,7 @@ class Looper : public Observer // for notifications
endPoint = 0;
lastWrittenSampleIndex = 0;
}
if ( state == STATE_PLAY_QUEUED )
if ( state == STATE_PLAY_QUEUED && state != STATE_STOPPED )
{
cout << " Q->Stopped " << endl;
state = STATE_STOPPED;
@ -60,8 +67,20 @@ class Looper : public Observer // for notifications
}
}
void setLoopLength(float l)
{
numBeats *= l;
// avoid the 0 * 2 problem
if ( numBeats < 1 )
numBeats = 1;
cout << "Looper " << track << " loop lenght now " << numBeats << endl;
}
void beat()
{
playedBeats++;
//cout << "Looper " << track << " got beat()" << flush;
}
@ -108,6 +127,8 @@ class Looper : public Observer // for notifications
State state;
int fpb;
int numBeats;
int playedBeats;
int endPoint, playPoint, lastWrittenSampleIndex;
float sample[44100*60];

View File

@ -18,7 +18,7 @@ class Metronome : public Observer
Metronome() :
playPoint (0),
playBar (false),
active (true)
active (false)
{
// create beat and bar samples
endPoint = (44100.f/441);