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,
|
||||
|
||||
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:
|
||||
|
|
|
@ -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
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);
|
||||
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"),
|
||||
|
|
24
src/gui.cxx
24
src/gui.cxx
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue