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

This commit is contained in:
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, RECORD,
LOOPER_STATE, LOOPER_STATE,
LOOPER_LOOP_LENGTH,
METRONOME_ACTIVE, METRONOME_ACTIVE,
}; };
}; };
@ -64,6 +66,18 @@ class EventLooperState : public EventBase
EventLooperState(int t, Looper::State s) : track(t), state(s){} 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 class EventLoadSample : public EventBase
{ {
public: public:

View file

@ -62,6 +62,12 @@ void handleDspEvents()
jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventLooperState) ); jack_ringbuffer_read( rbToDsp, (char*)&ev, sizeof(EventLooperState) );
jack->setLooperState( ev.track, ev.state ); jack->setLooperState( ev.track, ev.state );
} break; } } 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: default:
{ {
// just do nothing // 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); EventLooperState e = EventLooperState(track,Looper::STATE_STOP_QUEUED);
writeToDspRingbuffer( &e ); 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 else
{ {
cout << __FILE__ << __LINE__ << " Error: unknown command string" << endl; 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"), button1(x + 5, y + 24, 100, 18,"Rec"),
button2(x + 5, y + 44, 100, 18,"Play"), button2(x + 5, y + 44, 100, 18,"Play"),
button3(x + 5, y + 64, 100, 18,"Stop"), button3(x + 5, y + 64, 100, 18,"Stop"),
button4(x + 5, y + 84, 18, 18,"4"), button4(x + 5, y + 84, 48, 18,"-"),
button5(x + 5, y +104, 18, 18,"5"), button5(x +57, y + 84, 48, 18,"+"),
button6(x + 5, y +124, 18, 18,"6"),
button6(x + 5, y +104, 18, 18,"6"),
dial1(x+15, y +155, 24, 24, "A"), dial1(x+15, y +155, 24, 24, "A"),
dial2(x+45, y +155, 24, 24, "B"), dial2(x+45, y +155, 24, 24, "B"),

View file

@ -6,44 +6,34 @@
// Hack, move to gtrack.cpp // Hack, move to gtrack.cpp
int GTrack::privateID = 0; int GTrack::privateID = 0;
int GMasterTrack::privateID = 0;
using namespace std; 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() : Gui::Gui() :
window(600,280) window(1200,280)
{ {
window.color(FL_BLACK); window.color(FL_BLACK);
window.label("Luppp 5"); window.label("Luppp 5");
Avtk::Image* header = new Avtk::Image(0,0,600,36,"header.png"); Avtk::Image* header = new Avtk::Image(0,0,600,36,"header.png");
metronomeButton = new Avtk::LightButton(0,0,200,30,"Metronome"); int i = 0;
for (; i < NTRACKS; i++ )
for (int i = 0; i < 5; i++ )
{ {
stringstream s; stringstream s;
s << "Track " << i+1; s << "Track " << i+1;
tracks.push_back( new GTrack(8 + i * 118, 40, 110, 230, s.str().c_str() ) ); 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 = new Fl_Box(655, 5, 200, 60, "BPM = 120");
box->box(FL_UP_BOX); box->box(FL_UP_BOX);
box->labelsize(36); box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL); box->labeltype(FL_SHADOW_LABEL);
*/
window.end(); window.end();
} }

View file

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

View file

@ -47,7 +47,7 @@ Jack::Jack()
cerr << "Jack() error setting timebase callback" << endl; 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) ); loopers.push_back( new Looper(i) );
timeManager.registerObserver( loopers.back() ); timeManager.registerObserver( loopers.back() );

View file

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

View file

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

View file

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

View file

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