mirror of
https://github.com/vale981/openAV-Luppp
synced 2025-03-05 09:01:39 -05:00
-Loopers have length, UI controls it, 8 tracks, config header, Master track in UI
This commit is contained in:
parent
839388f112
commit
8da75fc397
12 changed files with 231 additions and 46 deletions
8
src/config.hxx
Normal file
8
src/config.hxx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
#ifndef LUPPP_CONFIG_H
|
||||||
|
#define LUPPP_CONFIG_H
|
||||||
|
|
||||||
|
#define NTRACKS 8
|
||||||
|
|
||||||
|
#endif // LUPPP_CONFIG_H
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
134
src/gmastertrack.hxx
Normal 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
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
24
src/gui.cxx
24
src/gui.cxx
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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() );
|
||||||
|
|
|
@ -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;}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue