mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 07:45:00 +01:00
new mini, standalone MIDI beatbox/live looper
This is for experiments with loop sequencing, MIDI region generation and superclock stuff
This commit is contained in:
parent
cec84d242d
commit
70def122de
5 changed files with 617 additions and 0 deletions
404
tools/bb/bb.cc
Normal file
404
tools/bb/bb.cc
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <jack/midiport.h>
|
||||||
|
|
||||||
|
#include "evoral/midi_events.h"
|
||||||
|
|
||||||
|
#include "bb.h"
|
||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
second_simultaneous_midi_byte_is_first (uint8_t a, uint8_t b)
|
||||||
|
{
|
||||||
|
bool b_first = false;
|
||||||
|
|
||||||
|
/* two events at identical times. we need to determine
|
||||||
|
the order in which they should occur.
|
||||||
|
|
||||||
|
the rule is:
|
||||||
|
|
||||||
|
Controller messages
|
||||||
|
Program Change
|
||||||
|
Note Off
|
||||||
|
Note On
|
||||||
|
Note Pressure
|
||||||
|
Channel Pressure
|
||||||
|
Pitch Bend
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((a) >= 0xf0 || (b) >= 0xf0 || ((a & 0xf) != (b & 0xf))) {
|
||||||
|
|
||||||
|
/* if either message is not a channel message, or if the channels are
|
||||||
|
* different, we don't care about the type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b_first = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
switch (b & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
b_first = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
switch (a & 0xf0) {
|
||||||
|
case MIDI_CMD_CONTROL:
|
||||||
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
|
case MIDI_CMD_NOTE_OFF:
|
||||||
|
case MIDI_CMD_NOTE_ON:
|
||||||
|
case MIDI_CMD_NOTE_PRESSURE:
|
||||||
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
|
break;
|
||||||
|
case MIDI_CMD_BENDER:
|
||||||
|
b_first = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeatBox::BeatBox (int sr)
|
||||||
|
: _start_requested (false)
|
||||||
|
, _running (false)
|
||||||
|
, _measures (2)
|
||||||
|
, _tempo (120)
|
||||||
|
, _meter_beats (4)
|
||||||
|
, _meter_beat_type (4)
|
||||||
|
, _input (0)
|
||||||
|
, _output (0)
|
||||||
|
, superclock_cnt (0)
|
||||||
|
, last_start (0)
|
||||||
|
, _sample_rate (sr)
|
||||||
|
, whole_note_superclocks (0)
|
||||||
|
, beat_superclocks (0)
|
||||||
|
, measure_superclocks (0)
|
||||||
|
, _quantize_divisor (4)
|
||||||
|
, clear_pending (false)
|
||||||
|
{
|
||||||
|
for (uint32_t n = 0; n < 1024; ++n) {
|
||||||
|
event_pool.push_back (new Event());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BeatBox::~BeatBox ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
BeatBox::register_ports (jack_client_t* jack)
|
||||||
|
{
|
||||||
|
if ((_input = jack_port_register (jack, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0)) == 0) {
|
||||||
|
cerr << "no input port\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((_output = jack_port_register (jack, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0)) == 0) {
|
||||||
|
cerr << "no output port\n";
|
||||||
|
jack_port_unregister (jack, _input);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BeatBox::start ()
|
||||||
|
{
|
||||||
|
/* compute tempo, beat steps etc. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
superclocks_per_minute = superclock_ticks_per_second * 60;
|
||||||
|
beats_per_minute = _tempo;
|
||||||
|
whole_notes_per_minute = beats_per_minute / _meter_beat_type;
|
||||||
|
*/
|
||||||
|
|
||||||
|
whole_note_superclocks = (superclock_ticks_per_second * 60) / (_tempo / _meter_beat_type);
|
||||||
|
cerr << "there are " << _tempo / _meter_beat_type << " whole notes per minute, which is " << superclock_ticks_per_second * 60 << " sct, so wns = " << whole_note_superclocks << endl;
|
||||||
|
beat_superclocks = whole_note_superclocks / _meter_beat_type;
|
||||||
|
measure_superclocks = beat_superclocks * _meter_beats;
|
||||||
|
|
||||||
|
/* we can start */
|
||||||
|
|
||||||
|
_start_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BeatBox::stop ()
|
||||||
|
{
|
||||||
|
_start_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
BeatBox::process (int nsamples)
|
||||||
|
{
|
||||||
|
if (!_running) {
|
||||||
|
if (_start_requested) {
|
||||||
|
_running = true;
|
||||||
|
last_start = superclock_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!_start_requested) {
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
superclock_t superclocks = samples_to_superclock (nsamples, _sample_rate);
|
||||||
|
|
||||||
|
if (!_running) {
|
||||||
|
superclock_cnt += superclocks;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
superclock_t process_start = superclock_cnt - last_start;
|
||||||
|
superclock_t process_end = process_start + superclocks;
|
||||||
|
const superclock_t loop_length = _measures * measure_superclocks;
|
||||||
|
const superclock_t orig_superclocks = superclocks;
|
||||||
|
|
||||||
|
process_start %= loop_length;
|
||||||
|
process_end %= loop_length;
|
||||||
|
|
||||||
|
bool two_pass_required;
|
||||||
|
superclock_t offset = 0;
|
||||||
|
|
||||||
|
if (process_end < process_start) {
|
||||||
|
two_pass_required = true;
|
||||||
|
process_end = loop_length;
|
||||||
|
superclocks = process_end - process_start;
|
||||||
|
} else {
|
||||||
|
two_pass_required = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* buffer;
|
||||||
|
void* out_buf;
|
||||||
|
void* in_buf;
|
||||||
|
jack_midi_event_t in_event;
|
||||||
|
jack_nframes_t event_index;
|
||||||
|
jack_nframes_t event_count;
|
||||||
|
|
||||||
|
/* do this on the first pass only */
|
||||||
|
out_buf = jack_port_get_buffer (_output, nsamples);
|
||||||
|
jack_midi_clear_buffer (out_buf);
|
||||||
|
|
||||||
|
second_pass:
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
|
||||||
|
if (clear_pending) {
|
||||||
|
|
||||||
|
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
|
||||||
|
event_pool.push_back (*ee);
|
||||||
|
}
|
||||||
|
_current_events.clear ();
|
||||||
|
clear_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
|
||||||
|
Event* e = (*ee);
|
||||||
|
|
||||||
|
if (e->size && (e->time >= process_start && e->time < process_end)) {
|
||||||
|
if ((buffer = jack_midi_event_reserve (out_buf, superclock_to_samples (offset + e->time - process_start, _sample_rate), e->size)) != 0) {
|
||||||
|
memcpy (buffer, e->buf, e->size);
|
||||||
|
} else {
|
||||||
|
cerr << "Could not reserve space for output event @ " << e << " of size " << e->size << " @ " << offset + e->time - process_start
|
||||||
|
<< " (samples: " << superclock_to_samples (offset + e->time - process_start, _sample_rate) << ") offset is " << offset
|
||||||
|
<< ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->time >= process_end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* input */
|
||||||
|
|
||||||
|
in_buf = jack_port_get_buffer (_input, nsamples);
|
||||||
|
event_index = 0;
|
||||||
|
|
||||||
|
while (jack_midi_event_get (&in_event, in_buf, event_index++) == 0) {
|
||||||
|
|
||||||
|
superclock_t event_time = superclock_cnt + samples_to_superclock (in_event.time, _sample_rate);
|
||||||
|
superclock_t elapsed_time = event_time - last_start;
|
||||||
|
superclock_t in_loop_time = elapsed_time % loop_length;
|
||||||
|
superclock_t quantized_time;
|
||||||
|
|
||||||
|
if (_quantize_divisor != 0) {
|
||||||
|
const superclock_t time_per_beat = whole_note_superclocks / _quantize_divisor;
|
||||||
|
quantized_time = (in_loop_time / time_per_beat) * time_per_beat;
|
||||||
|
} else {
|
||||||
|
quantized_time = elapsed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_event.size > 24) {
|
||||||
|
cerr << "Ignored large MIDI event\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_pool.empty()) {
|
||||||
|
cerr << "No more events, grow pool\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Event* e = event_pool.back();
|
||||||
|
event_pool.pop_back ();
|
||||||
|
|
||||||
|
e->time = quantized_time;
|
||||||
|
e->size = in_event.size;
|
||||||
|
memcpy (e->buf, in_event.buffer, in_event.size);
|
||||||
|
|
||||||
|
_current_events.insert (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
superclock_cnt += superclocks;
|
||||||
|
|
||||||
|
if (two_pass_required) {
|
||||||
|
offset = superclocks;
|
||||||
|
superclocks = orig_superclocks - superclocks;
|
||||||
|
process_start = 0;
|
||||||
|
process_end = superclocks;
|
||||||
|
two_pass_required = false;
|
||||||
|
cerr << "2nd Pass for " << superclocks << " offset = " << offset << endl;
|
||||||
|
goto second_pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BeatBox::set_quantize (int divisor)
|
||||||
|
{
|
||||||
|
_quantize_divisor = divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BeatBox::clear ()
|
||||||
|
{
|
||||||
|
clear_pending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BeatBox::EventComparator::operator() (Event const * a, Event const *b) const
|
||||||
|
{
|
||||||
|
if (a->time == b->time) {
|
||||||
|
if (a->buf[0] == b->buf[0]) {
|
||||||
|
return a < b;
|
||||||
|
}
|
||||||
|
return !second_simultaneous_midi_byte_is_first (a->buf[0], b->buf[0]);
|
||||||
|
}
|
||||||
|
return a->time < b->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process (jack_nframes_t nsamples, void* arg)
|
||||||
|
{
|
||||||
|
BeatBox* bbox = static_cast<BeatBox*> (arg);
|
||||||
|
return bbox->process (nsamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char* argv[])
|
||||||
|
{
|
||||||
|
jack_client_t* jack;
|
||||||
|
const char *server_name = NULL;
|
||||||
|
jack_options_t options = JackNullOption;
|
||||||
|
jack_status_t status;
|
||||||
|
|
||||||
|
if ((jack = jack_client_open ("beatbox", options, &status, server_name)) == 0) {
|
||||||
|
cerr << "Could not connect to JACK\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeatBox* bbox = new BeatBox (jack_get_sample_rate (jack));
|
||||||
|
BBGUI* gui = new BBGUI (&argc, &argv, jack, bbox);
|
||||||
|
|
||||||
|
bbox->register_ports (jack);
|
||||||
|
|
||||||
|
jack_set_process_callback (jack, process, bbox);
|
||||||
|
jack_activate (jack);
|
||||||
|
|
||||||
|
bbox->start ();
|
||||||
|
|
||||||
|
gui->run ();
|
||||||
|
}
|
||||||
79
tools/bb/bb.h
Normal file
79
tools/bb/bb.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef __bb_h__
|
||||||
|
#define __bb_h__
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <jack/jack.h>
|
||||||
|
|
||||||
|
typedef uint64_t superclock_t;
|
||||||
|
|
||||||
|
static const superclock_t superclock_ticks_per_second = 1235025792000; // 2^10 * 3^4 * 5^3 * 7^2 * 11 * 13 * 17
|
||||||
|
inline superclock_t superclock_to_samples (superclock_t s, int sr) { return (s * sr) / superclock_ticks_per_second; }
|
||||||
|
inline superclock_t samples_to_superclock (int samples, int sr) { return (samples * superclock_ticks_per_second) / sr; }
|
||||||
|
|
||||||
|
class BeatBox {
|
||||||
|
public:
|
||||||
|
BeatBox (int sample_rate);
|
||||||
|
~BeatBox ();
|
||||||
|
|
||||||
|
int register_ports (jack_client_t*);
|
||||||
|
int process (int nframes);
|
||||||
|
|
||||||
|
bool running() const { return _running || _start_requested; }
|
||||||
|
void start ();
|
||||||
|
void stop ();
|
||||||
|
void clear ();
|
||||||
|
|
||||||
|
void set_measure_count (int measures);
|
||||||
|
void set_meter (int beats, int beat_type);
|
||||||
|
void set_tempo (float bpm);
|
||||||
|
|
||||||
|
void set_quantize (int divisor);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _start_requested;
|
||||||
|
bool _running;
|
||||||
|
int _measures;
|
||||||
|
float _tempo;
|
||||||
|
int _meter_beats;
|
||||||
|
int _meter_beat_type;
|
||||||
|
jack_port_t* _input;
|
||||||
|
jack_port_t* _output;
|
||||||
|
superclock_t superclock_cnt;
|
||||||
|
superclock_t last_start;
|
||||||
|
|
||||||
|
int _sample_rate;
|
||||||
|
superclock_t whole_note_superclocks;
|
||||||
|
superclock_t beat_superclocks;
|
||||||
|
superclock_t measure_superclocks;
|
||||||
|
int _quantize_divisor;
|
||||||
|
bool clear_pending;
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
superclock_t time;
|
||||||
|
size_t size;
|
||||||
|
unsigned char buf[24];
|
||||||
|
|
||||||
|
Event () : time (0), size (0) {}
|
||||||
|
Event (jack_nframes_t t, size_t sz, unsigned char* b) : time (t), size (sz) { memcpy (buf, b, std::min (sizeof (buf), sz)); }
|
||||||
|
Event (Event const & other) : time (other.time), size (other.size) { memcpy (buf, other.buf, other.size); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EventComparator {
|
||||||
|
bool operator () (Event const * a, Event const * b) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::set<Event*,EventComparator> Events;
|
||||||
|
Events _current_events;
|
||||||
|
|
||||||
|
typedef std::vector<Event*> EventPool;
|
||||||
|
EventPool event_pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __bb_h__ */
|
||||||
77
tools/bb/gui.cc
Normal file
77
tools/bb/gui.cc
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include "bb.h"
|
||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
BBGUI::BBGUI (int* argc, char** argv[], jack_client_t* j, BeatBox* bb)
|
||||||
|
: jack (j)
|
||||||
|
, bbox (bb)
|
||||||
|
, main (argc, argv)
|
||||||
|
, quantize_off (quantize_group, "None")
|
||||||
|
, quantize_32nd (quantize_group, "ThirtySecond")
|
||||||
|
, quantize_16th (quantize_group, "Sixteenth")
|
||||||
|
, quantize_8th (quantize_group, "Eighth")
|
||||||
|
, quantize_quarter (quantize_group, "Quarter")
|
||||||
|
, quantize_half (quantize_group, "Half")
|
||||||
|
, quantize_whole (quantize_group, "Whole")
|
||||||
|
, play_button ("Run")
|
||||||
|
, clear_button ("Clear")
|
||||||
|
{
|
||||||
|
quantize_off.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 0));
|
||||||
|
quantize_32nd.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 32));
|
||||||
|
quantize_16th.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 16));
|
||||||
|
quantize_8th.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 8));
|
||||||
|
quantize_quarter.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 4));
|
||||||
|
quantize_half.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 2));
|
||||||
|
quantize_whole.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &BBGUI::set_quantize), 1));
|
||||||
|
|
||||||
|
quantize_button_box.pack_start (quantize_off);
|
||||||
|
quantize_button_box.pack_start (quantize_32nd);
|
||||||
|
quantize_button_box.pack_start (quantize_16th);
|
||||||
|
quantize_button_box.pack_start (quantize_8th);
|
||||||
|
quantize_button_box.pack_start (quantize_quarter);
|
||||||
|
quantize_button_box.pack_start (quantize_half);
|
||||||
|
quantize_button_box.pack_start (quantize_whole);
|
||||||
|
|
||||||
|
play_button.signal_toggled().connect (sigc::mem_fun (*this, &BBGUI::toggle_play));
|
||||||
|
clear_button.signal_clicked().connect (sigc::mem_fun (*this, &BBGUI::clear));
|
||||||
|
|
||||||
|
misc_button_box.pack_start (play_button);
|
||||||
|
misc_button_box.pack_start (clear_button);
|
||||||
|
|
||||||
|
global_vbox.pack_start (misc_button_box);
|
||||||
|
global_vbox.pack_start (quantize_button_box, true, true);
|
||||||
|
window.add (global_vbox);
|
||||||
|
window.show_all ();
|
||||||
|
}
|
||||||
|
|
||||||
|
BBGUI::~BBGUI ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BBGUI::run ()
|
||||||
|
{
|
||||||
|
window.show ();
|
||||||
|
main.run ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BBGUI::set_quantize (int divisor)
|
||||||
|
{
|
||||||
|
bbox->set_quantize (divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BBGUI::clear ()
|
||||||
|
{
|
||||||
|
bbox->clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BBGUI::toggle_play ()
|
||||||
|
{
|
||||||
|
if (bbox->running()) {
|
||||||
|
bbox->stop ();
|
||||||
|
} else {
|
||||||
|
bbox->start ();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
tools/bb/gui.h
Normal file
43
tools/bb/gui.h
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef __bb_gui_h__
|
||||||
|
#define __bb_gui_h__
|
||||||
|
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
|
||||||
|
class BeatBox;
|
||||||
|
|
||||||
|
class BBGUI {
|
||||||
|
public:
|
||||||
|
BBGUI (int*, char** [], jack_client_t* jack, BeatBox* bb);
|
||||||
|
~BBGUI ();
|
||||||
|
|
||||||
|
void run ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
jack_client_t* jack;
|
||||||
|
BeatBox* bbox;
|
||||||
|
Gtk::Main main;
|
||||||
|
Gtk::Window window;
|
||||||
|
|
||||||
|
Gtk::RadioButtonGroup quantize_group;
|
||||||
|
Gtk::RadioButton quantize_off;
|
||||||
|
Gtk::RadioButton quantize_32nd;
|
||||||
|
Gtk::RadioButton quantize_16th;
|
||||||
|
Gtk::RadioButton quantize_8th;
|
||||||
|
Gtk::RadioButton quantize_quarter;
|
||||||
|
Gtk::RadioButton quantize_half;
|
||||||
|
Gtk::RadioButton quantize_whole;
|
||||||
|
|
||||||
|
Gtk::ToggleButton play_button;
|
||||||
|
Gtk::Button clear_button;
|
||||||
|
|
||||||
|
Gtk::VBox global_vbox;
|
||||||
|
Gtk::VBox quantize_button_box;
|
||||||
|
Gtk::HBox misc_button_box;
|
||||||
|
|
||||||
|
void set_quantize (int divisor);
|
||||||
|
void toggle_play ();
|
||||||
|
void clear ();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __bb_gui_h__ */
|
||||||
14
tools/bb/makefile
Normal file
14
tools/bb/makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
all: bb
|
||||||
|
|
||||||
|
CXXFLAGS=-I$(AD)/5.0/libs/evoral `pkg-config --cflags jack` `pkg-config --cflags gtkmm-2.4`
|
||||||
|
LDFLAGS=`pkg-config --libs jack` `pkg-config --libs gtkmm-2.4`
|
||||||
|
|
||||||
|
bb: bb.o gui.o
|
||||||
|
$(CXX) -o bb bb.o gui.o $(LDFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
bb.o: bb.cc bb.h gui.h
|
||||||
|
gui.o: gui.cc gui.h bb.h
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f bb bb.o gui.o
|
||||||
Loading…
Add table
Add a link
Reference in a new issue