diff --git a/FL/util/ntk-perf.C b/FL/util/ntk-perf.C index 1ea8d1d..ca32236 100644 --- a/FL/util/ntk-perf.C +++ b/FL/util/ntk-perf.C @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -131,7 +131,7 @@ main ( int argc, char **argv ) } { - Fl_Scalepack *o = new Fl_Scalepack( 0, 24, 800, 600 - 24 ); + Fl_Pack *o = new Fl_Pack( 0, 24, 800, 600 - 24 ); o->type( 0 ); { diff --git a/nonlib/MIDI/event.C b/nonlib/MIDI/event.C new file mode 100644 index 0000000..7614787 --- /dev/null +++ b/nonlib/MIDI/event.C @@ -0,0 +1,162 @@ + +/*******************************************************************************/ +/* Copyright (C) 2007-2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#include "event.H" +#include +#include + +namespace MIDI +{ + void + event::_init ( void ) + { + _link = _next = _prev = NULL; + _selected = 0; + } + + event::event ( void ) + { + _init(); + } + + event::~event ( void ) + { + _link = _next = _prev = NULL; + } + +/* copy constructor */ + event::event ( const event &e ) : midievent( e ) + { + _link = _next = _prev = NULL; + _selected = e._selected; + } + + event::event ( const midievent &e ) : midievent( e ) + { + _init(); + } + + + void + event::link ( event *event ) + { + if ( event == NULL ) + { + if ( _link ) + { + _link->_link = NULL; + _link = NULL; + } + return; + } + + _link = event; + _link->_link = this; + } + + event * + event::link ( void ) const + { + return _link; + } + + bool + event::linked ( void ) const + { + return _link != NULL; + } + + void + event::select ( void ) + { + _selected = 1; + + if ( _link ) + _link->_selected = 1; + } + + void + event::deselect ( void ) + { + _selected = 0; + + if ( _link ) + _link->_selected = 0; + } + + bool + event::selected ( int n ) const + { + return _selected == n; + } + + bool + event::selected ( void ) const + { + return _selected == 1; + } + +/* override this so we can update linked event */ + void + event::note ( char note ) + { + midievent::note( note ); + + if ( _link ) + _link->midievent::note( note ); + } + +/* stupid C++ makes us override the all polymorphic functions... */ + unsigned char + event::note ( void ) const + { + return midievent::note(); + } + + tick_t + event::note_duration ( void ) const + { + return _link ? _link->timestamp() - timestamp() : 0; + } + + void + event::note_duration ( tick_t l ) + { + if ( _link ) + _link->timestamp( timestamp() + l ); + } + + void + event::get_note_properties ( note_properties *p ) const + { + p->start = timestamp(); + p->duration = note_duration(); + p->velocity = note_velocity(); + p->note = note(); + } + + void + event::set_note_properties ( const note_properties *p ) + { + timestamp( p->start ); + note_duration( p->duration ); + note_velocity( p->velocity ); + note( p->note ); + } +} diff --git a/nonlib/MIDI/event.H b/nonlib/MIDI/event.H new file mode 100644 index 0000000..1de9d42 --- /dev/null +++ b/nonlib/MIDI/event.H @@ -0,0 +1,99 @@ + +/*******************************************************************************/ +/* Copyright (C) 2007-2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +/* Higher level event interface than midievent, supporting + doublely-linked list, marking, selection, and linking of note + on/off pairs. */ + +#pragma once + +#include "midievent.H" + +namespace MIDI +{ + class event_list; + + class event; + class note_properties { + public: + tick_t start; + tick_t duration; + int note; + int velocity; + }; + + class event : public midievent + { + + + protected: + + /* these are only to be used by event_list class! */ + event *_next; + event *_prev; + + private: + + event *_link; /* other event in pair */ + + byte_t _selected; + + void _init ( void ); + + public: + + event(); + ~event(); + event ( const event &e ); + event ( const midievent &e ); + + event * next ( void ) const; + event * prev ( void ) const; + + void link ( event *event ); + event * link ( void ) const; + bool linked ( void ) const; + void select ( void ); + void deselect ( void ); + bool selected ( int n ) const; + bool selected ( void ) const; + void note ( char note ); + unsigned char note ( void ) const; + tick_t note_duration ( void ) const; + void note_duration ( tick_t l ); + + void get_note_properties ( note_properties *e ) const; + void set_note_properties ( const note_properties *e ); + + friend class event_list; + + }; + + inline event * + event::next ( void ) const + { + return _next; + } + + inline event * + event::prev ( void ) const + { + return _prev; + } +} diff --git a/nonlib/MIDI/event_list.C b/nonlib/MIDI/event_list.C new file mode 100644 index 0000000..e0c7e09 --- /dev/null +++ b/nonlib/MIDI/event_list.C @@ -0,0 +1,631 @@ + +/*******************************************************************************/ +/* Copyright (C) 2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#include "debug.h" +#include "event_list.H" + +/* The operations we perform on event lists are clumsy with STL lists + and iterators so we have a custom doubly-linked list implementation + here for complete control */ + +namespace MIDI +{ +#define RFOR_ALL( it ) for ( event *next, * it = _tail; it && ((next = it ->_prev), true) ; it = next ) +#define FOR_ALL( it ) for ( event *next, * it = _head; it && ((next = it ->_next), true) ; it = next ) +// #define FOR_ALL( e ) for ( event * e = _head; e; e = e ->_next ) +#define FOR_SELECTED( e ) FOR_ALL( e ) if ( e ->selected() ) +#define RFOR_SELECTED( e ) RFOR_ALL( e ) if ( e ->selected() ) + + + event_list::event_list ( void ) + { + _head = NULL; + _tail = NULL; + _size = 0; + } + + event_list::~event_list ( void ) + { + clear(); + } + +/* copy constructor */ + event_list::event_list ( const event_list &el ) + { + _copy( &el ); + } + + event_list & + event_list::operator= ( const event_list &rhs ) + { + if ( this != &rhs ) + { + clear(); + + _copy( &rhs ); + } + + return *this; + } + + event_list & + event_list::operator= ( const list &rhs ) + { + clear(); + + for ( list ::const_iterator me = rhs.begin(); me != rhs.end(); me++ ) + { + event *e = new event( *me ); + + _insert( NULL, e ); + } + + relink(); + + return *this; + } + +/** allow indexing */ + event * + event_list::operator[] ( unsigned int index ) + { + unsigned int i = 0; + for ( event *e = _head; e; (e = e->_next), ++i ) + if ( i == index ) + return e; + + // all else fails. + return _tail; + } + + void + event_list::_copy ( const event_list *el ) + { + if ( ! el->_head ) + { + _head = _tail = NULL; + _size = 0; + return; + } + + _head = new event( *(el->_head) ); + _head->_prev = NULL; + + event *p = _head; + + for ( event *e = el->_head->_next; e; e = e->_next ) + { + event *n = new event( *e ); + + n->_next = NULL; + p->_next = n; + n->_prev = p; + + p = n; + } + + _tail = p; + + _size = el->_size; + + relink(); + } + +/** insert event /n/ before event /o/ */ + void + event_list::_insert ( event *o, event *n ) + { + ++_size; + + if ( ! o ) + { + n->_next = NULL; + n->_prev = _tail; + + if ( _tail ) + _tail->_next = n; + + _tail = n; + if ( ! _head ) + _head = n; + return; + } + + event *t = o->_prev; + + o->_prev = n; + n->_next = o; + n->_prev = t; + + if ( ! t ) + _head = n; + else + t->_next = n; + } + + void + event_list::unlink ( event *e ) + { + if ( e->_next ) + e->_next->_prev = e->_prev; + else + _tail = e->_prev; + + if ( e->_prev ) + e->_prev->_next = e->_next; + else + _head = e->_next; + + --_size; + } + + + + void + event_list::clear ( void ) + { + for ( event *e = _head; e ; ) + { + event *n = e->_next; + delete e; + e = n; + } + + _head = NULL; + _tail = NULL; + _size = 0; + } + + void + event_list::mix ( event *ne ) + { + FOR_ALL( e ) + if ( *e == *ne ) + { + /* already have an event like this, drop it */ + + if ( ne->linked() ) + delete ne->link(); + + delete ne; + + return; + } + + insert( ne ); + if ( ne->linked() ) + insert( ne->link() ); + + } + +/** remove elements from list /el/ to this list */ + void + event_list::merge ( event_list *el ) + { + event *n; + for ( event *e = el->_head; e; e = n ) + { + n = e->_next; + + el->unlink( e ); + + insert( e ); + } + } + +/** unlink event e */ + void + event_list::remove ( event *e ) + { + unlink( e ); + delete e; + } + +/** sorted insert /e/ */ + void + event_list::insert ( event *e ) + { + /* find the place to insert */ + RFOR_ALL( i ) + if ( *e >= *i ) + { + _insert( i->_next, e ); + return; + } + + _insert( _head, e ); + } + +/** just append event without sorting */ + void + event_list::append ( event *e ) + { + _insert( NULL, e ); + } + + event * + event_list::first ( void ) const + { + return _head; + } + + event * + event_list::last ( void ) const + { + return _tail; + } + + + +/*************/ +/* Selection */ +/*************/ + +/** select all events from /start/ to /end/ inclusive */ + void + event_list::select ( tick_t start, tick_t end ) + { + FOR_ALL( e ) + { + tick_t ts = e->timestamp(); + + /* don't count note offs exactly on start */ + if ( ts == start && e->is_note_off() ) + continue; + + if ( ts >= start && ts < end ) + e->select(); + } + } + +/** select note evenets from /start/ to /end/ within range /hi/ through /lo/ */ + void + event_list::select ( tick_t start, tick_t end, int hi, int lo ) + { + FOR_ALL( e ) + { + tick_t ts = e->timestamp(); + + /* don't count note offs exactly on start */ + if ( ! e->is_note_on() ) + continue; + + if ( ts >= start && ts < end && + e->note() <= hi && e->note() >= lo ) + e->select(); + } + } + +/** select ALL events */ + void + event_list::select_all ( void ) + { + FOR_ALL( e ) + e->select(); + } + + void + event_list::select_none ( void ) + { + FOR_ALL( e ) + e->deselect(); + } + + void + event_list::invert_selection ( void ) + { + FOR_ALL( e ) + if ( ! e->is_note_off() ) + { + if ( e->selected() ) + e->deselect(); + else + e->select(); + } + } + +/** remove all selected events */ + void + event_list::remove_selected ( void ) + { + FOR_SELECTED( e ) + { + remove( e ); + } + } + +/** transpose selected notes (ignoring other event types) by /n/ tones + * (may span octaves) */ + void + event_list::transpose_selected ( int n ) + { + FOR_SELECTED( e ) + { + if ( e->is_note_on() ) + e->note( e->note() + n ); + } + + } + +/** change all notes of value /from/ to /to/ */ + void + event_list::rewrite_selected ( int from, int to ) + { + FOR_SELECTED( e ) + { + if ( e->is_note_on() && e->note() == from ) + e->note( to ); + } + } + + +/** get timestamp of earliest selected event */ + tick_t + event_list::selection_min ( void ) + { + FOR_SELECTED( e ) + return e->timestamp(); + + return 0; + } + + tick_t + event_list::selection_max ( void ) + { + RFOR_SELECTED( e ) + return e->timestamp(); + + return 0; + } + +/** move selected events by offset /o/ */ + void + event_list::move_selected ( long o ) + { + if ( o < 0 ) + if ( selection_min() < (tick_t)( 0 - o ) ) + return; + + if ( o < 0 ) + { + FOR_SELECTED( e ) + move( e, o ); + } + else + { + RFOR_SELECTED( e ) + move( e, o ); + } + } + + void + event_list::push_selection ( void ) + { + FOR_ALL( e ) + if ( e->_selected ) + ++e->_selected; + } + + void + event_list::pop_selection ( void ) + { + FOR_ALL( e ) + if ( e->_selected ) + --e->_selected; + } + + + +/** verify that all note ons are linked to note offs */ + bool + event_list::verify ( void ) const + { + FOR_ALL( e ) + if ( e->is_note_on() && ! e->linked() ) + return false; + + return true; + } + +/** link /e/ (a note on) with the next corresponding note off */ + void + event_list::link ( event *on ) + { + if ( ! on->is_note_on() ) + return; + + for ( event *off = on->_next; off; off = off->_next ) + { + if ( off->linked() ) + continue; + + if ( off->is_note_off() && + off->channel() == on->channel() && + off->note() == on->note() ) + { + on->link( off ); + return; + } + } + + WARNING( "no corresponding note_off found for note on, repairing" ); + + event *off = new event( *on ); + + off->opcode( event::NOTE_OFF ); + + on->link( off ); + + insert( off ); + } + +/** insert /l/ ticks of time at /start/ */ + void + event_list::insert_time ( tick_t start, tick_t l ) + { + FOR_ALL( e ) + { + tick_t ts = e->timestamp(); + + if ( e->is_note_off() ) + continue; + + if ( ts >= start ) + { + if ( e->is_note_on() ) + { + /* only notes ENTIRELY WITHIN the range will be moved */ + e->timestamp( ts + l ); + e->link()->timestamp( e->link()->timestamp() + l ); + } + else + e->timestamp( e->timestamp() + l ); + } + } + + sort(); + } + +/** delete events in range and close the gap */ + void + event_list::delete_time ( tick_t start, tick_t end ) + { + tick_t l = end - start; + + push_selection(); + + select( start, end ); + + remove_selected(); + + pop_selection(); + + /* cut out the slack */ + FOR_ALL( e ) + { + tick_t ts = e->timestamp(); + + if ( ts >= end ) + e->timestamp( ts - l ); + } + } + +/** link all note ons to subsequent note offs */ + void + event_list::relink ( void ) + { + /* clear links */ + FOR_ALL( e ) + e->link( NULL ); + + /* link */ + FOR_ALL( on ) + link( on ); + + if ( ! verify() ) + FATAL( "event list failed verification" ); + } + +/** resort event /e/ */ + void + event_list::sort ( event *e ) + { + unlink( e ); + + insert( e ); + } + +/** resort entire list */ + void + event_list::sort ( void ) + { + event_list *temp = new event_list( ); + + _head = temp->_head; + _tail = temp->_tail; + + FOR_ALL( n ) + temp->insert( n ); + + temp->_head = NULL; + + delete temp; + + relink(); + } + +/** move event /e/ by /o/ ticks */ + void + event_list::move ( event *e, long o ) + { + e->timestamp( e->timestamp() + o ); + + sort( e ); + } + + bool + event_list::empty ( void ) const + { + return _head == NULL; + } + + size_t + event_list::size ( void ) const + { + return _size; + } + + void + event_list::_hi_lo ( bool sel, int *hi, int *lo ) const + { + *hi = 0; + *lo = 127; + + FOR_ALL( e ) + { + if ( sel && ! e->selected() ) + continue; + + if ( ! e->is_note_on() ) + continue; + + int n = e->note(); + + if ( n > *hi ) + *hi = n; + + if ( n < *lo ) + *lo = n; + } + } + +/** set /hi/ and /lo/ to the lowest and highest pitched note events in + * this list, respectively */ + void + event_list::hi_lo_note ( int *hi, int *lo ) const + { + _hi_lo( false, hi, lo ); + } + + void + event_list::selected_hi_lo_note ( int *hi, int *lo ) const + { + _hi_lo( true, hi, lo ); + } +} diff --git a/nonlib/MIDI/event_list.H b/nonlib/MIDI/event_list.H new file mode 100644 index 0000000..47c43f3 --- /dev/null +++ b/nonlib/MIDI/event_list.H @@ -0,0 +1,91 @@ + +/*******************************************************************************/ +/* Copyright (C) 2008 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#pragma once + +#include "event.H" +#include + +namespace MIDI { + using std::list; + + class midievent; + + class event_list { + + event * _head; + event * _tail; + + size_t _size; + + void _insert ( event *o, event *n ); + void _copy ( const event_list *el ); + void _hi_lo ( bool sel, int *hi, int *lo ) const; + + public: + + event_list ( void ); + ~event_list ( void ); + event_list ( const event_list &el ); + + void clear ( void ); + void merge ( event_list *el ); + void unlink ( event *e ); + void remove ( event *e ); + void insert ( event *e ); + event * first ( void ) const; + event * last ( void ) const; + void select ( tick_t start, tick_t end ); + void select ( tick_t start, tick_t end, int hi, int lo ); + + void select_all ( void ); + void select_none ( void ); + void invert_selection ( void ); + + void remove_selected ( void ); + void transpose_selected ( int n ); + tick_t selection_min ( void ); + tick_t selection_max ( void ); + void move_selected ( long o ); + void push_selection ( void ); + void pop_selection ( void ); + bool verify ( void ) const; + void link ( event *on ); + void insert_time ( tick_t start, tick_t l ); + void delete_time ( tick_t start, tick_t end ); + void relink ( void ); + void sort ( event *e ); + void sort ( void ); + void move ( event *e, long o ); + bool empty ( void ) const; + size_t size ( void ) const; + void append ( event *e ); + void mix ( event *ne ); + void hi_lo_note ( int *hi, int *lo ) const; + void rewrite_selected ( int from, int to ); + void selected_hi_lo_note ( int *hi, int *lo ) const; + + + event_list & operator= ( const event_list &rhs ); + event_list & operator= ( const list &rhs ); + event *operator[] ( unsigned int index ); + + // friend class event; + }; +} diff --git a/nonlib/wscript b/nonlib/wscript index 0683074..6d2458e 100644 --- a/nonlib/wscript +++ b/nonlib/wscript @@ -21,6 +21,9 @@ dsp.C file.C MIDI/midievent.C string_util.C +MIDI/event_list.C +MIDI/event.C +MIDI/midievent.C ''', includes = '.', export_incdirs = [ '.', 'nonlib'], diff --git a/sequencer/src/canvas.C b/sequencer/src/canvas.C index f6d916f..6de6721 100644 --- a/sequencer/src/canvas.C +++ b/sequencer/src/canvas.C @@ -20,27 +20,178 @@ /* This is a generic double-buffering, optimizing canvas interface to grids (patterns and phrases). It draws only what is necessary to keep the display up-to-date. Actual drawing functions are in draw.C */ +#include +#include #include "canvas.H" #include "pattern.H" -#include "gui/draw.H" #include "common.h" #include "non.H" #include +#include +#include "gui/ui.H" +#include +#include + +using namespace MIDI; +extern UI *ui; extern Fl_Color velocity_colors[]; const int ruler_height = 14; -Canvas::Canvas ( int X, int Y, int W, int H, const char *L ) : Fl_Widget( X,Y,W,H,L ) + + + +class Canvas::Canvas_Panzoomer : public Fl_Panzoomer { - m.origin_x = m.origin_y = m.height = m.width = m.div_w = m.div_h = m.playhead = m.margin_top = m.margin_left = m.playhead = m.w = m.h = m.p1 = m.p2 = m.p3 = m.p4 = 0; + Fl_Offscreen backbuffer; + +public: + + Canvas_Panzoomer( int X, int Y,int W, int H, const char *L = 0 ) + : Fl_Panzoomer(X,Y,W,H,L) + { + backbuffer = 0; + } + + Canvas *canvas; + +private: + + static void draw_dash ( tick_t x, int y, tick_t l, int color, int selected, void *userdata ) + { + Canvas_Panzoomer *o = (Canvas_Panzoomer*)userdata; + + o->draw_dash( x,y,l,color,selected ); + } + + void draw_dash ( tick_t x, int y, tick_t w, int color, int selected ) const + { + if ( selected ) + color = FL_MAGENTA; + else + color = velocity_colors[ color ]; + + Canvas *c = canvas; + + double VS = (double)this->h() / c->m.maxh; + double HS = (double)this->w() / ( _xmax - _xmin ); + + y = c->ntr( y ); + + if ( y < 0 ) + return; + + y *= VS; + + fl_color( fl_color_average( color, FL_GRAY, 0.5 ) ); + + fl_rectf( + x * HS, + y, + (w * HS) + 0.5, + 1 * VS + 0.5 ); + } + +protected: + + void draw_background ( int X, int Y, int W, int H ) + { + + /* DMESSAGE( "%s%s%s%s%s%s", */ + /* damage() & FL_DAMAGE_CHILD ? "CHILD " : "", */ + /* damage() & FL_DAMAGE_ALL ? "ALL " : "", */ + /* damage() & FL_DAMAGE_USER1 ? "USER 1 ": "", */ + /* damage() & FL_DAMAGE_EXPOSE ? "EXPOSE " : "", */ + /* damage() & FL_DAMAGE_SCROLL ? "SCROLL " : "", */ + /* damage() & FL_DAMAGE_OVERLAY ? "OVERLAY " : ""); */ + + if ( ! backbuffer || + ! ( damage() & FL_DAMAGE_USER1 ) ) + { + if ( !backbuffer ) + backbuffer = fl_create_offscreen( W, H ); + + DMESSAGE( "redrawing preview" ); + + fl_begin_offscreen(backbuffer); + + fl_rectf( 0, 0, W, H, color() ); + canvas->m.grid->draw_notes( draw_dash, this ); + + fl_end_offscreen(); + } + + fl_copy_offscreen( X,Y,W,H,backbuffer,0, 0 ); + } +public: + + void resize ( int X, int Y, int W, int H ) + { + Fl_Panzoomer::resize( X,Y,W,H ); + if ( backbuffer ) + fl_delete_offscreen( backbuffer ); + backbuffer = 0; + redraw(); + } + + void draw_overlay ( void ) + { + Canvas *c = canvas; + + double HS = (double)w() /( _xmax - _xmin ); + + tick_t new_x = c->grid()->index(); + + fl_color( fl_color_add_alpha( FL_GREEN, 100 ) ); + fl_line( x() + new_x * HS, y(), x() + new_x * HS, y() + h() ); + } + +}; + +static note_properties *ghost_note = 0; + +Canvas::Canvas ( int X, int Y, int W, int H, const char *L ) : Fl_Group( X,Y,W,H,L ) +{ + + { Fl_Box *o = new Fl_Box( X, Y, W, H - 75 ); + /* this is a dummy group where the canvas goes */ + Fl_Group::current()->resizable( o ); + } + { Fl_Group *o = new Fl_Group( X, Y + H - 75, W, 75 ); + + { + Canvas_Panzoomer *o = new Canvas_Panzoomer( X, Y + H - 75, W - 14, 75 ); + o->canvas = this; + o->box( FL_FLAT_BOX ); +// o->color(fl_color_average( FL_BLACK, FL_WHITE, 0.90 )); + o->color( FL_BLACK ); +// o->color(FL_BACKGROUND_COLOR); +// o->type( FL_HORIZONTAL ); + o->callback( cb_scroll, this ); + o->when( FL_WHEN_CHANGED ); + panzoomer = o; + } + + { + Fl_Slider *o = new Fl_Slider( X + W - 14, Y + H - panzoomer->h(), 14, panzoomer->h() ); + o->range( 1, 128 ); + o->step( 1 ); + o->type( FL_VERTICAL ); + o->tooltip( "Vertical Zoom" ); + o->callback( cb_scroll, this ); + vzoom = o; + } + o->end(); + } + + m.origin_x = m.origin_y = m.height = m.width = m.div_w = m.div_h = m.margin_top = m.margin_left = m.playhead = m.w = m.h = m.p1 = m.p2 = m.p3 = m.p4 = 0; m.margin_top = ruler_height; m.draw = false; m.ruler_drawn = false; - m.mapping_drawn = false; m.grid_drawn = false; // m.current = m.previous = NULL; @@ -50,6 +201,13 @@ Canvas::Canvas ( int X, int Y, int W, int H, const char *L ) : Fl_Widget( X,Y,W, m.maxh = 128; m.vp = NULL; + + end(); +} + +Canvas::~Canvas ( ) +{ + } void @@ -58,7 +216,9 @@ Canvas::handle_event_change ( void ) /* mark the song as dirty and pass the signal on */ song.set_dirty(); - signal_draw(); +// panzoomer->redraw(); + + redraw(); } /** change grid to /g/, returns TRUE if new grid size differs from old */ @@ -80,18 +240,20 @@ Canvas::grid ( Grid *g ) resize_grid(); + vzoom->range( 1, m.maxh ); + vzoom->value( m.vp->h ); + update_mapping(); -// m.shape = m.grid->draw_shape(); - /* connect signals */ /* FIXME: what happens when we do this twice? */ g->signal_events_change.connect( mem_fun( this, &Canvas::handle_event_change ) ); g->signal_settings_change.connect( signal_settings_change.make_slot() ); + + redraw(); - signal_draw(); - signal_settings_change(); - signal_pan(); +// parent()->redraw(); +// signal_settings_change(); } /** keep row compaction tables up-to-date */ @@ -122,6 +284,8 @@ Canvas::_update_row_mapping ( void ) m.maxh = 128; m.vp->h = min( m.vp->h, m.maxh ); + + resize_grid(); } /** update everything about mapping, leaving the viewport alone */ @@ -130,8 +294,6 @@ Canvas::update_mapping ( void ) { _update_row_mapping(); - m.mapping_drawn = false; - adj_size(); int old_margin = m.margin_left; @@ -144,13 +306,14 @@ Canvas::update_mapping ( void ) m.draw = true; - if ( m.margin_left != old_margin ) - { - signal_resize(); - signal_draw(); - } - else - signal_draw(); +/* if ( m.margin_left != old_margin ) */ +/* { */ +/* // signal_resize(); */ +/* redraw(); */ +/* } */ +/* else */ + + damage(FL_DAMAGE_USER1); } @@ -164,8 +327,6 @@ Canvas::changed_mapping ( void ) if ( m.vp->y + m.vp->h > m.maxh ) m.vp->y = (m.maxh / 2) - (m.vp->h / 2); - - signal_pan(); } Grid * @@ -184,36 +345,24 @@ Canvas::adj_size ( void ) m.div_w = (m.width - m.margin_left) / m.vp->w; m.div_h = (m.height - m.margin_top) / m.vp->h; - - m.mapping_drawn = m.ruler_drawn = false; } /** reallocate buffers to match grid dimensions */ void Canvas::resize_grid ( void ) { - // _update_row_mapping(); - adj_size(); - - if ( m.vp ) - { - if ( m.vp->w != m.w || m.vp->h != m.h || - m.div_w != m.old_div_w || m.div_h != m.old_div_h ) - { - if ( m.grid_drawn ) - signal_resize(); - - m.old_div_w = m.div_w; - m.old_div_h = m.div_h; - } - else - return; - } - + DMESSAGE( "resizing grid %dx%d", m.vp->w, m.vp->h ); - m.grid_drawn = false; + Grid *g = grid(); + panzoomer->x_value( g->x_to_ts( m.vp->x), g->x_to_ts( m.vp->w ), 0, g->length()); + panzoomer->y_value( m.vp->y, m.vp->h, 0, m.maxh ); + + panzoomer->zoom_range( 2, 16 ); + +// m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) ); + } /** inform the canvas with new phsyical dimensions */ @@ -224,9 +373,9 @@ Canvas::resize ( int x, int y, int w, int h ) m.origin_y = y; m.width = w; - m.height = h; + m.height = h - 75; - Fl_Widget::resize(x,y,w,h); + Fl_Group::resize(x,y,w,h); adj_size(); } @@ -237,34 +386,6 @@ Canvas::resize ( int x, int y, int w, int h ) /* Drawing */ /***********/ -/** prepare current buffer for drawing (draw "background") */ -void -Canvas::clear ( void ) -{ - /* uint rule = m.grid->ppqn(); */ - - /* uint lx = m.grid->ts_to_x( m.grid->length() ); */ - - /* for ( uint y = m.vp->h; y--; ) */ - /* for ( uint x = m.vp->w; x--; ) */ - /* { */ - /* m.current[ x ][ y ].color = 0; */ - /* m.current[ x ][ y ].state = EMPTY; */ - /* m.current[ x ][ y ].flags = 0; */ - /* } */ - - /* for ( int x = m.vp->w - rule; x >= 0; x -= rule ) */ - /* for ( uint y = m.vp->h; y-- ; ) */ - /* m.current[ x ][ y ].state = LINE; */ - - /* int sx = (int)(lx - m.vp->x) >= 0 ? lx - m.vp->x : 0; */ - - /* for ( int x = sx; x < m.vp->w; ++x ) */ - /* for ( int y = m.vp->h; y-- ; ) */ - /* m.current[ x ][ y ].state = PARTIAL; */ - -} - /** is /x/ within the viewport? */ bool Canvas::viewable_x ( int x ) @@ -272,45 +393,6 @@ Canvas::viewable_x ( int x ) return x >= m.vp->x && x < m.vp->x + m.vp->w; } -/** flush delta of last and current buffers to screen, then flip them */ -void -Canvas::flip ( void ) -{ - /* /\* FIXME: should this not go in clear()? *\/ */ - /* if ( m.p1 != m.p2 ) */ - /* { */ - /* if ( viewable_x( m.p1 ) ) draw_line( m.p1 - m.vp->x, F_P1 ); */ - /* if ( viewable_x( m.p2 ) ) draw_line( m.p2 - m.vp->x, F_P2 ); */ - /* } */ - - /* if ( viewable_x( m.playhead ) ) draw_line( m.playhead - m.vp->x, F_PLAYHEAD ); */ - - /* const int shape = m.grid->draw_shape(); */ - - /* for ( uint y = m.vp->h; y--; ) */ - /* for ( uint x = m.vp->w; x--; ) */ - /* { */ - /* cell_t *c = &m.current[ x ][ y ]; */ - /* cell_t *p = &m.previous[ x ][ y ]; */ - - /* /\* draw selection rect *\/ */ - /* if ( m.p3 != m.p4 ) */ - /* if ( y + m.vp->y >= m.p3 && x + m.vp->x >= m.p1 && */ - /* y + m.vp->y <= m.p4 && x + m.vp->x < m.p2 ) */ - /* c->flags |= F_SELECTION; */ - - /* if ( *c != *p ) */ - /* gui_draw_shape( m.origin_x + m.margin_left + x * m.div_w, m.origin_y + m.margin_top + y * m.div_h, m.div_w, m.div_h, */ - /* shape, c->state, c->flags, c->color ); */ - /* } */ - - /* cell_t **tmp = m.previous; */ - - /* m.previous = m.current; */ - /* m.current = tmp; */ -} - - static int gui_draw_ruler ( int x, int y, int w, int div_w, int div, int ofs, int p1, int p2 ) @@ -323,10 +405,12 @@ gui_draw_ruler ( int x, int y, int w, int div_w, int div, int ofs, int p1, int p fl_color( FL_BACKGROUND_COLOR ); + w += 100; /* FIXME: hack */ + // fl_rectf( x, y, x + (div_w * w), y + h ); fl_rectf( x, y, (div_w * w), h ); - fl_color( FL_RED ); + fl_color( FL_FOREGROUND_COLOR ); fl_line( x + div_w / 2, y, x + div_w * w, y ); @@ -334,44 +418,46 @@ gui_draw_ruler ( int x, int y, int w, int div_w, int div, int ofs, int p1, int p int z = div; int i; for ( i = 0; i < w; i++ ) - if ( 0 == i % z ) + { + int k = ofs + i; + if ( 0 == k % z ) { int nx = x + (i * div_w) + (div_w / 2); - fl_color( FL_RED ); + fl_color( FL_FOREGROUND_COLOR ); - fl_line( nx, y, nx, y + h ); + fl_line( nx, y, nx, y + h - 1 ); - int k = ofs + i; sprintf( pat, "%i", 1 + (k / z )); - fl_color( FL_WHITE ); + fl_color( FL_FOREGROUND_COLOR ); fl_draw( pat, nx + div_w / 2, y + h + 1 / 2 ); } - - if ( p1 != p2 ) - { - if ( p1 >= 0 ) - { - if ( p1 < p2 ) - fl_color( FL_GREEN ); - else - fl_color( FL_RED ); - - fl_rectf( x + (div_w * p1), y + h / 2, div_w, h / 2 ); - - } - if ( p2 >= 0 ) - { - if ( p2 < p1 ) - fl_color( FL_GREEN ); - else - fl_color( FL_RED ); - fl_rectf( x + (div_w * p2), y + h / 2, div_w, h / 2 ); - - } } + /* if ( p1 != p2 ) */ + /* { */ + /* if ( p1 >= 0 ) */ + /* { */ + /* if ( p1 < p2 ) */ + /* fl_color( FL_GREEN ); */ + /* else */ + /* fl_color( FL_RED ); */ + + /* fl_rectf( x + (div_w * p1), y + h / 2, div_w, h / 2 ); */ + + /* } */ + /* if ( p2 >= 0 ) */ + /* { */ + /* if ( p2 < p1 ) */ + /* fl_color( FL_GREEN ); */ + /* else */ + /* fl_color( FL_RED ); */ + /* fl_rectf( x + (div_w * p2), y + h / 2, div_w, h / 2 ); */ + + /* } */ + /* } */ + return h; } @@ -390,7 +476,7 @@ gui_draw_string ( int x, int y, int w, int h, int color, const char *s, bool dra if ( fl_not_clipped( x, y, rw, h ) && draw ) { - fl_rectf( x,y,w,h, FL_BACKGROUND_COLOR ); +// fl_rectf( x,y,w,h, FL_BACKGROUND_COLOR ); if ( color ) fl_color( velocity_colors[ color ] ); @@ -403,15 +489,6 @@ gui_draw_string ( int x, int y, int w, int h, int color, const char *s, bool dra return rw; } -/** redraw the ruler at the top of the canvas */ -void -Canvas::redraw_ruler ( void ) -{ - m.margin_top = gui_draw_ruler( m.origin_x + m.margin_left, m.origin_y, m.vp->w, m.div_w, m.grid->division(), m.vp->x, - m.p1 - m.vp->x, m.p2 - m.vp->x ); - m.ruler_drawn = true; -} - /** callback called by Grid::draw_row_names() to draw an individual row name */ void Canvas::draw_row_name ( int y, const char *name, int color ) @@ -434,20 +511,24 @@ Canvas::draw_row_name ( int y, const char *name, int color ) if ( y < 0 || y >= m.vp->h ) draw = false; - if ( clear && draw ) - fl_rectf( bx, by, bw, bh, FL_BACKGROUND_COLOR ); - else - m.margin_left = max( m.margin_left, gui_draw_string( bx, by, - bw, bh, - color, - name, - draw ) ); + if ( draw && name ) + { + fl_rectf( bx, by, bw, bh, index(name, '#') ? FL_GRAY : FL_BLACK ); + fl_rect( bx, by, bw, bh, FL_BLACK ); + } + + m.margin_left = max( m.margin_left, gui_draw_string( bx + 1, by + 2, + bw - 1, bh - 4, + color, + name, + draw ) ); } -/** redraw row names */ void -Canvas::redraw_mapping ( void ) +Canvas::draw_mapping ( void ) { + int old_margin = m.margin_left; + m.margin_left = 0; m.draw = false; @@ -459,64 +540,89 @@ Canvas::redraw_mapping ( void ) m.draw = true; m.grid->draw_row_names( this ); - - m.mapping_drawn = true; -} - -void -Canvas::draw_mapping ( void ) -{ - if ( ! m.mapping_drawn ) redraw_mapping(); } void Canvas::draw_ruler ( void ) { - if ( ! m.ruler_drawn ) redraw_ruler(); + m.margin_top = gui_draw_ruler( m.origin_x + m.margin_left, + m.origin_y, + m.vp->w, + m.div_w, + m.grid->division(), + m.vp->x, + m.p1 - m.vp->x, + m.p2 - m.vp->x ); } -/** "draw" a shape in the backbuffer */ void -Canvas::draw_shape ( int x, int y, int w, int color ) +Canvas::damage_grid ( tick_t x, int y, tick_t w, int h = 1 ) { y = ntr( y ); if ( y < 0 ) return; - + // adjust for viewport. - + + x = m.grid->ts_to_x(x); + w = m.grid->ts_to_x(w); + x -= m.vp->x; y -= m.vp->y; if ( x < 0 || y < 0 || x >= m.vp->w || y >= m.vp->h ) return; + + damage(FL_DAMAGE_USER1, m.origin_x + m.margin_left + x * m.div_w, + m.origin_y + m.margin_top + y * m.div_h, + m.div_w * w, + m.div_h * h ); +} - fl_rectf( m.origin_x + m.margin_left + x * m.div_w, - m.origin_y + m.margin_top + y * m.div_h + 1, - m.div_w * w, - m.div_h - 1, - color ); +void +Canvas::draw_dash ( tick_t x, int y, tick_t w, int color, int selected ) const +{ + if ( m.grid->velocity_sensitive() ) + color = velocity_colors[ color ]; + else + color = velocity_colors[ 127 ]; + + y = ntr( y ); + + if ( y < 0 ) + return; + + // adjust for viewport. + + x = m.grid->ts_to_x(x); + w = m.grid->ts_to_x(w); + + x -= m.vp->x; + y -= m.vp->y; + + x = m.origin_x + m.margin_left + x * m.div_w; + y = m.origin_y + m.margin_top + y * m.div_h; + w *= m.div_w; + + /* fl_rectf( x, y + 1, w, m.div_h - 1, fl_color_add_alpha( color, 170 ) ); */ + + /* fl_rect( x, y + 1, w, m.div_h - 1, selected ? FL_MAGENTA : fl_lighter( FL_BACKGROUND_COLOR )); */ + + fl_draw_box( FL_ROUNDED_BOX, x, y + 1, w, m.div_h - 1, color ); + if ( selected ) + fl_draw_box( FL_ROUNDED_FRAME, x, y + 1, w, m.div_h - 1, FL_MAGENTA ); + +// fl_color_add_alpha( color, 170 )); } /** callback used by Grid::draw() */ void -Canvas::draw_dash ( int x, int y, int l, int color, void *userdata ) +Canvas::draw_dash ( tick_t x, int y, tick_t w, int color, int selected, void *userdata ) { Canvas *o = (Canvas*)userdata; - - color = velocity_colors[ color ]; - - o->draw_shape( x, y, 1, fl_color_average( FL_WHITE, color, 0.5 ) ); - o->draw_shape( x + 1, y, l - 1, color ); -} - -/** draw a vertical line with flags */ -void -Canvas::draw_line ( int x, int flags ) -{ - /* for ( uint y = m.vp->h; y-- ; ) */ - /* m.current[ x ][ y ].flags |= flags; */ + + o->draw_dash( x,y,w,color,selected ); } int @@ -527,120 +633,267 @@ Canvas::playhead_moved ( void ) return m.playhead != x; } +void +Canvas::redraw_playhead ( void ) +{ + int old_x = m.playhead; + + int new_x = m.grid->ts_to_x( m.grid->index() ); + + if ( old_x != new_x ) + { + window()->damage( FL_DAMAGE_OVERLAY ); + } + + if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w ) + { + if ( config.follow_playhead ) + { + new_x = m.playhead; + + panzoomer->x_value( m.grid->index() ); + panzoomer->do_callback(); + } + } +} + +void +Canvas::draw_overlay ( void ) +{ + if ( ! visible_r() ) + return; + + fl_push_no_clip(); + + fl_push_clip( x() + m.margin_left, + y() + m.margin_top, + w() - m.margin_left, + h() - panzoomer->h() - m.margin_top ); + + draw_playhead(); + + fl_pop_clip(); + + panzoomer->draw_overlay(); + + fl_pop_clip(); +} + /** draw only the playhead--without reexamining the grid */ -int +void Canvas::draw_playhead ( void ) { - /* int x = m.grid->ts_to_x( m.grid->index() ); */ + int x = m.grid->ts_to_x( m.grid->index() ); - /* if ( m.playhead == x ) */ - /* return 0; */ + if ( m.playhead == x ) + return; - /* m.playhead = x; */ + m.playhead = x; - /* if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w ) */ - /* { */ - /* if ( config.follow_playhead ) */ - /* { */ - /* m.vp->x = m.playhead / m.vp->w * m.vp->w; */ + if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w ) + return; - /* m.ruler_drawn = false; */ + int px = m.origin_x + m.margin_left + ( x - m.vp->x ) * m.div_w; - /* signal_draw(); */ + int X,Y,W,H; + + X = px; + Y = m.origin_y + m.margin_top; + W = m.div_w; + H = m.origin_y + m.margin_top + m.vp->h * m.div_h; - /* return 0; */ - /* } */ - /* } */ - - /* copy(); */ - - /* for ( uint x = m.vp->w; x-- ; ) */ - /* for ( uint y = m.vp->h; y-- ; ) */ - /* m.current[ x ][ y ].flags &= ~ (F_PLAYHEAD | F_P1 | F_P2 ); */ - - /* flip(); */ - - /* /\* actually if we're recording, we should draw the grid once per */ - /* * playhead movement also *\/ */ - /* if ( pattern::recording() == m.grid ) */ - /* { */ - /* draw(); */ - /* } */ - - /* return 1; */ + fl_rectf( X,Y,W,H, fl_color_add_alpha( FL_RED, 127 ) ); } +void +Canvas::draw_clip ( void *v, int X, int Y, int W, int H ) +{ + ((Canvas*)v)->draw_clip( X,Y,W,H ); +} + +void +Canvas::draw_clip ( int X, int Y, int W, int H ) +{ + box( FL_FLAT_BOX ); + labeltype( FL_NO_LABEL ); + + fl_push_clip( X,Y,W,H ); + + + fl_push_clip( m.origin_x + m.margin_left, + m.origin_y + m.margin_top, + w() - m.margin_left, + h() - m.margin_top - panzoomer->h() ); + + fl_rectf( m.origin_x + m.margin_left, m.origin_y + m.margin_top, w(), h(), FL_BLACK ); + + /* draw bar/beat lines */ + + for ( int gx = m.vp->x; + gx < m.vp->x + m.vp->w + 200; /* hack */ + gx++ ) + { + if ( gx % m.grid->division() == 0 ) + fl_color( fl_color_add_alpha( FL_GRAY, 50 )); + else if ( gx % m.grid->subdivision() == 0 ) + fl_color( fl_color_add_alpha( FL_GRAY, 30 )); + else + continue; + + + fl_rectf( m.origin_x + m.margin_left + ( ( gx - m.vp->x ) * m.div_w ), + m.origin_y + m.margin_top, + m.div_w, + y() + h() - m.margin_top ); + } + + m.grid->draw_notes( draw_dash, this ); + + if ( ghost_note ) + draw_dash( ghost_note->start, + ghost_note->note, + ghost_note->duration, + ghost_note->velocity, + 1 ); + + fl_color( fl_color_add_alpha( fl_rgb_color( 127,127,127 ), 50 )); + + /* draw grid */ + + if ( m.div_w > 4 ) + { + for ( int gx = m.origin_x + m.margin_left; + gx < x() + w(); + gx += m.div_w ) + fl_line( gx, m.origin_y + m.margin_top, gx, y() + h() ); + } + + if ( m.div_h > 2 ) + { + for ( int gy = m.origin_y + m.margin_top; + gy < y() + h(); + gy += m.div_h ) + fl_line( m.origin_x + m.margin_left, gy, x() + w(), gy ); + } + + + fl_pop_clip(); + + fl_pop_clip(); +} + + /** draw ONLY those nodes necessary to bring the canvas up-to-date with the grid */ void Canvas::draw ( void ) { - DMESSAGE( "drawing canvas" ); + box( FL_NO_BOX ); + labeltype( FL_NO_LABEL ); - draw_mapping(); - draw_ruler(); + /* DMESSAGE( "%s%s%s%s%s%s", */ + /* damage() & FL_DAMAGE_CHILD ? "CHILD " : "", */ + /* damage() & FL_DAMAGE_ALL ? "ALL " : "", */ + /* damage() & FL_DAMAGE_USER1 ? "USER 1 ": "", */ + /* damage() & FL_DAMAGE_EXPOSE ? "EXPOSE " : "", */ + /* damage() & FL_DAMAGE_SCROLL ? "SCROLL " : "", */ + /* damage() & FL_DAMAGE_OVERLAY ? "OVERLAY " : ""); */ - m.grid_drawn = true; - fl_rectf( m.origin_x + m.margin_left, m.origin_y + m.margin_top, w(), h(), velocity_colors[10] ); - - /* draw grid */ - - fl_color( FL_BLACK ); - for ( int gx = m.origin_x + m.margin_left; - gx < m.origin_x + m.margin_left + ( m.div_w * m.vp->w ); - gx += m.div_w ) - fl_line( gx, m.origin_y + m.margin_top, gx, m.origin_y + m.margin_top + ( m.div_w * m.vp->w ) ); + if ( damage() & FL_DAMAGE_SCROLL ) + { + draw_ruler(); - for ( int gy = m.origin_y + m.margin_top; - gy < m.origin_y + m.margin_top + ( m.div_h * m.vp->h ); - gy += m.div_h ) - fl_line( m.origin_x + m.margin_left, gy, m.origin_x + m.margin_left + ( m.div_w * m.vp->w ), gy ); + int dx = ( _old_scroll_x - m.vp->x ) * m.div_w; + int dy = ( _old_scroll_y - m.vp->y ) * m.div_h; - - m.grid->draw_notes( draw_dash, this ); + fl_scroll( m.origin_x + m.margin_left, + m.origin_y + m.margin_top, + w() - m.margin_left, + h() - m.margin_top - panzoomer->h(), + dx, dy, + draw_clip, + this ); + + if ( dy ) + draw_mapping(); + + _old_scroll_x = m.vp->x; + _old_scroll_y = m.vp->y; + + if ( damage() & FL_DAMAGE_CHILD ) + clear_damage( FL_DAMAGE_CHILD ); + } + else if ( damage() & ~FL_DAMAGE_CHILD ) + { + draw_mapping(); + draw_ruler(); + + draw_clip( x(), y(), w(), h() ); + } + + draw_children(); +} + +void +Canvas::cb_scroll ( Fl_Widget *w, void *v ) +{ + ((Canvas*)v)->cb_scroll( w ); +} + +void +Canvas::cb_scroll ( Fl_Widget *w ) +{ + if ( w == panzoomer ) + { + Fl_Panzoomer *o = (Fl_Panzoomer*)w; + + _old_scroll_x = m.vp->x; + + m.vp->x = grid()->ts_to_x( o->x_value() ); + m.vp->y = o->y_value(); + + damage( FL_DAMAGE_SCROLL ); + + if ( o->zoom_changed() ) + { + m.vp->w = m.grid->division() * o->zoom(); + resize_grid(); + redraw(); + } + } + else if ( w == vzoom ) + { + Fl_Slider *o = (Fl_Slider*)w; + + float n = o->value(); + + m.vp->h = min( (int)n, m.maxh ); + + resize_grid(); + + song.set_dirty(); + + redraw(); + } } -/* /\** redraw every node on the canvas from the buffer (without */ -/* * necessarily reexamining the grid) *\/ */ -/* void */ -/* Canvas::redraw ( void ) */ -/* { */ -/* DMESSAGE( "redrawing canvas" ); */ - -/* if ( ! m.grid_drawn ) */ -/* draw(); */ - -/* m.ruler_drawn = false; */ -/* m.mapping_drawn = false; */ - -/* draw_mapping(); */ -/* draw_ruler(); */ - -/* const int shape = m.grid->draw_shape(); */ - -/* for ( int y = m.vp->h; y--; ) */ -/* for ( int x = m.vp->w; x--; ) */ -/* { */ -/* cell_t c = m.previous[ x ][ y ]; */ - -/* if ( m.vp->x + x == m.playhead ) */ -/* c.flags |= F_PLAYHEAD; */ - -/* gui_draw_shape( m.origin_x + m.margin_left + x * m.div_w, m.origin_y + m.margin_top + y * m.div_h, m.div_w, m.div_h, */ -/* shape, c.state, c.flags, c.color ); */ -/* } */ -/* } */ /** convert pixel coords into grid coords. returns true if valid */ bool Canvas::grid_pos ( int *x, int *y ) const { + /* if ( ( *x < m.origin_x + m.margin_left ) || */ + /* ( *y < m.origin_y + m.margin_top ) || */ + /* ( *x > m.origin_x + w() ) || */ + /* (*y > m.origin_y + h() - panzoomer->h() ) ) */ + /* return false; */ + *y = (*y - m.margin_top - m.origin_y) / m.div_h; *x = (*x - m.margin_left - m.origin_x) / m.div_w; - if ( *x < 0 || *y < 0 || *x >= m.vp->w || *y >= m.vp->h ) - return false; + /* if ( *x < 0 || *y < 0 || *x >= m.vp->w || *y >= m.vp->h ) */ + /* return false; */ /* adjust for viewport */ *x += m.vp->x; @@ -663,16 +916,23 @@ Canvas::grid_pos ( int *x, int *y ) const /** if coords correspond to a row name entry, return the (absolute) note number, otherwise return -1 */ int -Canvas::is_row_name ( int x, int y ) +Canvas::is_row_press ( void ) const { - if ( x - m.origin_x >= m.margin_left ) + if ( Fl::event_inside( this->x(), + this->y() + this->m.margin_top, + this->m.margin_left, + ( this->h() - this->m.margin_top ) - this->panzoomer->h() ) ) + { + int dx,dy; + dx = Fl::event_x(); + dy = Fl::event_y(); + + grid_pos( &dx, &dy ); + + return m.grid->y_to_note(dy ); + } + else return -1; - - x = m.margin_left; - - grid_pos( &x, &y ); - - return m.grid->y_to_note( y ); } void @@ -688,7 +948,7 @@ Canvas::start_cursor ( int x, int y ) _lr(); - signal_draw(); + redraw(); } void @@ -704,61 +964,7 @@ Canvas::end_cursor ( int x, int y ) _lr(); - signal_draw(); -} - -void -Canvas::set ( int x, int y ) -{ - if ( y - m.origin_y < m.margin_top ) - /* looks like a click on the ruler */ - { - if ( x - m.margin_left - m.origin_x >= 0 ) - { - m.p1 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w); - m.ruler_drawn = false; - - m.p3 = m.p4 = 0; - } - - _lr(); - - signal_draw(); - - return; - } - - if ( ! grid_pos( &x, &y ) ) - return; - - m.grid->put( x, y, 0 ); -} - -void -Canvas::unset ( int x, int y ) -{ - if ( y - m.origin_y < m.margin_top ) - /* looks like a click on the ruler */ - { - if ( x - m.margin_left - m.origin_x >= 0 ) - { - m.p2 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w); - m.ruler_drawn = false; - - m.p3 = m.p4 = 0; - } - - _lr(); - - signal_draw(); - - return; - } - - if ( ! grid_pos( &x, &y ) ) - return; - - m.grid->del( x, y ); + redraw(); } void @@ -779,6 +985,15 @@ Canvas::adj_length ( int x, int y, int n ) m.grid->adj_duration( x, y, n ); } +void +Canvas::set_end ( int x, int y, int n ) +{ + if ( ! grid_pos( &x, &y ) ) + return; + + m.grid->set_end( x, y, n ); +} + void Canvas::select ( int x, int y ) { @@ -935,7 +1150,6 @@ Canvas::row_compact ( int n ) break; } // _reset(); - m.mapping_drawn = false; } void @@ -951,7 +1165,6 @@ Canvas::pan ( int dir, int n ) break; default: n *= 5; - m.mapping_drawn = false; break; } @@ -986,8 +1199,7 @@ Canvas::pan ( int dir, int n ) } } - signal_draw(); - signal_pan(); + damage(FL_DAMAGE_USER1); } void @@ -1050,3 +1262,400 @@ Canvas::notes ( void ) { return m.grid->notes(); } + +int +Canvas::handle ( int m ) +{ + Canvas *c = this; + + int ow, oh; + + int x, y; + int processed = 1; + + x = Fl::event_x(); + y = Fl::event_y(); + + static int drag_x; + static int drag_y; + static bool delete_note; + static note_properties *drag_note; + static int adjusting_velocity; + +// static note_properties; + + ow = c->grid()->viewport.w; + oh = c->grid()->viewport.h; + + switch ( m ) + { + case FL_FOCUS: + case FL_UNFOCUS: + return 1; + case FL_ENTER: + case FL_LEAVE: + fl_cursor( FL_CURSOR_DEFAULT ); + return 1; + case FL_MOVE: + { + if ( Fl::event_inside( this->x() + this->m.margin_left, + this->y() + this->m.margin_top, + this->w() - this->m.margin_left, + ( this->h() - this->m.margin_top ) - this->panzoomer->h() ) ) + fl_cursor( FL_CURSOR_HAND ); + else + fl_cursor( FL_CURSOR_DEFAULT ); + + return 1; + break; + } + case FL_KEYBOARD: + { + +/* if ( Fl::event_state() & FL_ALT || Fl::event_state() & FL_CTRL ) */ +/* // this is more than a simple keypress. */ +/* return 0; */ + + + if ( Fl::event_state() & FL_CTRL ) + { + switch ( Fl::event_key() ) + { + case FL_Delete: + c->delete_time(); + break; + case FL_Insert: + c->insert_time(); + break; + case FL_Right: + c->pan( TO_NEXT_NOTE, 0 ); + break; + case FL_Left: + c->pan( TO_PREV_NOTE, 0 ); + break; + default: + return 0; + } + } + else + if ( Fl::event_state() & FL_ALT ) + return 0; + + switch ( Fl::event_key() ) + { + case FL_Left: + c->pan( LEFT, 1 ); + break; + case FL_Right: + c->pan( RIGHT, 1 ); + break; + case FL_Up: + c->pan( UP, 1 ); + break; + case FL_Down: + c->pan( DOWN, 1 ); + break; + default: + /* have to do this to get shifted keys */ + switch ( *Fl::event_text() ) + { + case 'f': + c->pan( TO_PLAYHEAD, 0 ); + break; + case 'r': + c->select_range(); + break; + case 'q': + c->grid()->select_none(); + break; + case 'i': + c->invert_selection(); + break; + /* case '1': */ + /* c->h_zoom( 2.0f ); */ + /* break; */ + /* case '2': */ + /* c->h_zoom( 0.5f ); */ + /* break; */ + /* case '3': */ + /* c->v_zoom( 2.0f ); */ + /* break; */ + /* case '4': */ + /* c->v_zoom( 0.5f ); */ + /* break; */ + /* case ' ': */ + /* transport.toggle(); */ + /* break; */ + +#define IS_PATTERN (parent() == ui->pattern_tab) +#define IS_PHRASE (parent() == ui->phrase_tab) +#define IS_SEQUENCE (parent() == ui->sequence_tab) + case '<': + c->move_selected( LEFT, 1 ); + break; + case '>': + c->move_selected( RIGHT, 1 ); + break; + case ',': + c->move_selected( UP, 1 ); + break; + case '.': + c->move_selected( DOWN, 1 ); + break; + case 'C': + c->crop(); + break; + case 'd': + { + MESSAGE( "duplicating thing" ); + c->grid( c->grid()->clone() ); + + // number of phrases may have changed. + ui->update_sequence_widgets(); + + break; + + } + case 'D': + c->duplicate_range(); + break; + case 't': + c->grid()->trim(); + break; + default: + processed = 0; + break; + } + break; + } + break; + } + case FL_PUSH: + { + switch ( Fl::event_button() ) + { + case 1: + { + delete_note = true; + adjusting_velocity = 0; + + if ( Fl::event_ctrl() ) + { + c->select( x, y ); + processed = 2; + break; + } + + int dx = x; + int dy = y; + + grid_pos( &dx, &dy ); + + int note; + if ( ( note = c->is_row_press() ) >= 0 ) + { + if ( IS_PATTERN ) + ((pattern *)c->grid())->row_name_press( note ); + + processed = 2; + break; + } + + if ( Fl::event_inside( this->x() + this->m.margin_left, + this->y() + this->m.margin_top, + this->w() - this->m.margin_left, + ( this->h() - this->m.margin_top ) - this->panzoomer->h() ) ) + { + + if ( ! this->m.grid->is_set( dx,dy )) + { + ghost_note = new note_properties; + + ghost_note->start = this->m.grid->x_to_ts( dx ); + ghost_note->note = dy; + ghost_note->duration = this->m.grid->default_length(); + ghost_note->velocity = 64; + + delete_note = false; + + processed = 1; + break; + } + else + { + ghost_note = new note_properties; + drag_note = new note_properties; + this->m.grid->get_note_properties( dx, dy, ghost_note ); + this->m.grid->get_note_properties( dx, dy, drag_note ); + this->m.grid->del( dx, dy ); + + delete_note = true; + } + + this->m.grid->get_start( &dx, &dy ); + + drag_x = x; + drag_y = y; + + take_focus(); + } + else + processed = 0; + + break; + } + case 3: + { + int note; + if ( ( note = is_row_press() ) >= 0 ) + { + /* inside the note headings */ + + DMESSAGE( "click on row %d", note ); + if ( IS_PATTERN ) + { + Instrument *i = ((pattern *)c->grid())->mapping.instrument(); + + if ( i ) + { + ui->edit_instrument_row( i, note ); + + c->changed_mapping(); + } + } + } + else + if ( Fl::event_state() & FL_SHIFT ) + { + c->end_cursor( x, y ); + break; + } + break; + } + default: + processed = 0; + } + break; + } + case FL_RELEASE: + switch ( Fl::event_button() ) + { + case 1: + { + int dx = x; + int dy = y; + grid_pos( &dx, &dy ); + + if ( IS_PATTERN && Fl::event_state() & ( FL_ALT | FL_CTRL ) ) + c->randomize_row( y ); + else + { + if ( delete_note ) + { +// this->m.grid->del( dx, dy ); + if ( ghost_note ) + { + damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 ); + + delete ghost_note; + } + ghost_note = 0; + } + else + if ( ghost_note ) + { + this->m.grid->put( this->m.grid->ts_to_x( ghost_note->start ), + ghost_note->note, + ghost_note->duration, + ghost_note->velocity); + + delete_note = false; + + delete ghost_note; + ghost_note = 0; + } + } + + if ( drag_note ) + delete drag_note; + drag_note = 0; + + break; + } + default: + processed = 0; + break; + } + break; + case FL_DRAG: + if ( Fl::event_button1() ) + { + int dx = x; + int dy = y; + + grid_pos( &dx, &dy ); + + if ( ghost_note ) + { + damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 ); + + if ( drag_note ) + { + int ody = drag_y; + int odx = drag_x; + + grid_pos( &odx, &ody ); + + if ( ody != dy ) + { + adjusting_velocity = 1; + + ghost_note->velocity = + drag_note->velocity + + ( (drag_y - y) / 3.0f ); + + if ( ghost_note->velocity < 0 ) + ghost_note->velocity = 0; + else if ( ghost_note->velocity > 127 ) + ghost_note->velocity = 127; + } + } + + if ( ! adjusting_velocity ) + { + if ( dx > this->m.grid->ts_to_x( ghost_note->start ) ) + { + ghost_note->duration = this->m.grid->x_to_ts( dx ) - ghost_note->start; + } + + damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 ); + } + else + ghost_note->duration = drag_note->duration; + + delete_note = false; + + processed = 2; + + } + } + break; + default: + processed = 0; + } + + int nw, nh; + nw = c->grid()->viewport.w; + nh = c->grid()->viewport.h; + + if ( processed ) + window()->damage(FL_DAMAGE_OVERLAY); + + if ( processed == 1 ) + damage(FL_DAMAGE_USER1); + + if ( ! processed ) + return Fl_Group::handle( m ); + + return processed; +} diff --git a/sequencer/src/canvas.H b/sequencer/src/canvas.H index df3d258..e7bde2a 100644 --- a/sequencer/src/canvas.H +++ b/sequencer/src/canvas.H @@ -20,7 +20,7 @@ #pragma once #include "grid.H" -#include +#include #include using namespace sigc; @@ -29,9 +29,19 @@ class Mapping; enum { LEFT, RIGHT, UP, DOWN, TO_PLAYHEAD, TO_NEXT_NOTE, TO_PREV_NOTE }; +class Fl_Scrollbar; +class Fl_Slider; -class Canvas : public Fl_Widget, public trackable +class Canvas : public Fl_Group, public trackable { + class Canvas_Panzoomer; + + Canvas_Panzoomer *panzoomer; + Fl_Slider *vzoom; + + /* these are grid coords, not pixels */ + int _old_scroll_x; + int _old_scroll_y; struct { int origin_x, origin_y; @@ -79,8 +89,6 @@ class Canvas : public Fl_Widget, public trackable void _update_row_mapping ( void ); - void redraw_ruler ( void ); - void redraw_mapping ( void ); void draw_mapping ( void ); void draw_ruler ( void ); @@ -88,21 +96,24 @@ class Canvas : public Fl_Widget, public trackable void _lr ( void ); bool viewable_x ( int x ); - void draw_line ( int x, int flags ); void update_mapping ( void ); + static void cb_scroll ( Fl_Widget *w, void *v ); + void cb_scroll ( Fl_Widget *w ); + static void draw_clip ( void *v, int X, int Y, int W, int H ); + void draw_clip ( int X, int Y, int W, int H ); + public: enum { OFF, ON, TOGGLE }; signal signal_settings_change; - signal signal_draw; - signal signal_resize; - signal signal_pan; Canvas ( int X, int Y, int W, int H, const char *L=0 ); + virtual ~Canvas ( ); + void redraw_playhead ( void ); void handle_event_change ( void ); void set ( int x, int y ); void grid ( Grid *g ); @@ -112,19 +123,20 @@ public: void resize_grid ( void ); void resize ( int x, int y, int w, int h ); void copy ( void ); - void clear ( void ); - void flip ( void ); void draw_row_name ( int y, const char *name, int color ); - void draw_shape ( int x, int y, int w, int color ); - static void draw_dash ( int x, int y, int l, int color, void *userdata ); - int draw_playhead ( void ); + // void draw_shape ( int x, int y, int w, int color ); + static void draw_dash ( tick_t x, int y, tick_t l, int color, int selected, void *userdata ); + void draw_dash ( tick_t x, int y, tick_t w, int color, int selected ) const; + void damage_grid ( tick_t x, int y, tick_t w, int h ); + void draw_overlay ( void ); + void draw_playhead ( void ); void draw ( void ); - /* void redraw ( void ); */ bool grid_pos ( int *x, int *y ) const; - int is_row_name ( int x, int y ); + int is_row_press ( void ) const; void unset ( int x, int y ); void adj_color ( int x, int y, int n ); void adj_length ( int x, int y, int n ); + void set_end ( int x, int y, int n ); void select ( int x, int y ); void select_range ( void ); void invert_selection ( void ); @@ -149,6 +161,8 @@ public: void move_selected ( int dir, int n ); + virtual int handle ( int m ); + }; inline int diff --git a/sequencer/src/const.h b/sequencer/src/const.h index 0dbb4e9..0eeb0d6 100644 --- a/sequencer/src/const.h +++ b/sequencer/src/const.h @@ -27,7 +27,7 @@ const int MAX_PATTERN = 128; const unsigned int PPQN = 480; /* interval between GUI updates for playhead movement, etc. */ -const double TRANSPORT_POLL_INTERVAL = 0.05; +const double TRANSPORT_POLL_INTERVAL = 0.02; const char APP_NAME[] = "Non-Sequencer"; const char APP_TITLE[] = "The Non-Sequencer"; diff --git a/sequencer/src/dash.H b/sequencer/src/dash.H deleted file mode 100644 index 29ae679..0000000 --- a/sequencer/src/dash.H +++ /dev/null @@ -1,10 +0,0 @@ - -#pragma once - -struct dash -{ - tick_t timestamp; - tick_t length; - unsigned char color; -}; - diff --git a/sequencer/src/event.C b/sequencer/src/event.C deleted file mode 100644 index 335c4d2..0000000 --- a/sequencer/src/event.C +++ /dev/null @@ -1,144 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2007-2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -/* Higher level event interface, supporting doublely-linked list, - marking, selection, and linking of note pairs. */ - -#include "event.H" -#include -#include - -void -event::_init ( void ) -{ - _link = _next = _prev = NULL; - _selected = 0; -} - -event::event ( void ) -{ - _init(); -} - -event::~event ( void ) -{ - _link = _next = _prev = NULL; -} - -/* copy constructor */ -event::event ( const event &e ) : midievent( e ) -{ - _link = _next = _prev = NULL; - _selected = e._selected; -} - -event::event ( const midievent &e ) : midievent( e ) -{ - _init(); -} - - -void -event::link ( event *event ) -{ - if ( event == NULL ) - { - if ( _link ) - { - _link->_link = NULL; - _link = NULL; - } - return; - } - - _link = event; - _link->_link = this; -} - -event * -event::link ( void ) const -{ - return _link; -} - -bool -event::linked ( void ) const -{ - return _link != NULL; -} - -void -event::select ( void ) -{ - _selected = 1; - - if ( _link ) - _link->_selected = 1; -} - -void -event::deselect ( void ) -{ - _selected = 0; - - if ( _link ) - _link->_selected = 0; -} - -bool -event::selected ( int n ) const -{ - return _selected == n; -} - -bool -event::selected ( void ) const -{ - return _selected == 1; -} - -/* override this so we can update linked event */ -void -event::note ( char note ) -{ - midievent::note( note ); - - if ( _link ) - _link->midievent::note( note ); -} - -/* stupid C++ makes us override the all polymorphic functions... */ -unsigned char -event::note ( void ) const -{ - return midievent::note(); -} - -tick_t -event::note_duration ( void ) const -{ - return _link ? _link->timestamp() - timestamp() : 0; -} - -void -event::note_duration ( tick_t l ) -{ - if ( _link ) - _link->timestamp( timestamp() + l ); -} diff --git a/sequencer/src/event.H b/sequencer/src/event.H deleted file mode 100644 index 1c7580a..0000000 --- a/sequencer/src/event.H +++ /dev/null @@ -1,83 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2007-2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -#pragma once - -#include "common.h" -#include "midievent.H" - -#include - -class event_list; - -class event : public midievent -{ - -protected: - - /* these are only to be used by event_list class! */ - event *_next; - event *_prev; - -private: - - event *_link; /* other event in pair */ - - byte_t _selected; - - void _init ( void ); - -public: - - event(); - ~event(); - event ( const event &e ); - event ( const midievent &e ); - - event * next ( void ) const; - event * prev ( void ) const; - - void link ( event *event ); - event * link ( void ) const; - bool linked ( void ) const; - void select ( void ); - void deselect ( void ); - bool selected ( int n ) const; - bool selected ( void ) const; - void note ( char note ); - unsigned char note ( void ) const; - tick_t note_duration ( void ) const; - void note_duration ( tick_t l ); - - - friend class event_list; - -}; - -inline event * -event::next ( void ) const -{ - return _next; -} - -inline event * -event::prev ( void ) const -{ - return _prev; -} diff --git a/sequencer/src/event_list.C b/sequencer/src/event_list.C deleted file mode 100644 index c717184..0000000 --- a/sequencer/src/event_list.C +++ /dev/null @@ -1,627 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -#include "event_list.H" - -/* The operations we perform on event lists are clumsy with STL lists - and iterators so we have a custom doubly-linked list implementation - here for complete control */ - -#define RFOR_ALL( it ) for ( event *next, * it = _tail; it && ((next = it ->_prev), true) ; it = next ) -#define FOR_ALL( it ) for ( event *next, * it = _head; it && ((next = it ->_next), true) ; it = next ) -// #define FOR_ALL( e ) for ( event * e = _head; e; e = e ->_next ) -#define FOR_SELECTED( e ) FOR_ALL( e ) if ( e ->selected() ) -#define RFOR_SELECTED( e ) RFOR_ALL( e ) if ( e ->selected() ) - - -event_list::event_list ( void ) -{ - _head = NULL; - _tail = NULL; - _size = 0; -} - -event_list::~event_list ( void ) -{ - clear(); -} - -/* copy constructor */ -event_list::event_list ( const event_list &el ) -{ - _copy( &el ); -} - -event_list & -event_list::operator= ( const event_list &rhs ) -{ - if ( this != &rhs ) - { - clear(); - - _copy( &rhs ); - } - - return *this; -} - -event_list & -event_list::operator= ( const list &rhs ) -{ - clear(); - - for ( list ::const_iterator me = rhs.begin(); me != rhs.end(); me++ ) - { - event *e = new event( *me ); - - _insert( NULL, e ); - } - - relink(); - - return *this; -} - -/** allow indexing */ -event * -event_list::operator[] ( unsigned int index ) -{ - unsigned int i = 0; - for ( event *e = _head; e; (e = e->_next), ++i ) - if ( i == index ) - return e; - - // all else fails. - return _tail; -} - -void -event_list::_copy ( const event_list *el ) -{ - if ( ! el->_head ) - { - _head = _tail = NULL; - _size = 0; - return; - } - - _head = new event( *(el->_head) ); - _head->_prev = NULL; - - event *p = _head; - - for ( event *e = el->_head->_next; e; e = e->_next ) - { - event *n = new event( *e ); - - n->_next = NULL; - p->_next = n; - n->_prev = p; - - p = n; - } - - _tail = p; - - _size = el->_size; - - relink(); -} - -/** insert event /n/ before event /o/ */ -void -event_list::_insert ( event *o, event *n ) -{ - ++_size; - - if ( ! o ) - { - n->_next = NULL; - n->_prev = _tail; - - if ( _tail ) - _tail->_next = n; - - _tail = n; - if ( ! _head ) - _head = n; - return; - } - - event *t = o->_prev; - - o->_prev = n; - n->_next = o; - n->_prev = t; - - if ( ! t ) - _head = n; - else - t->_next = n; -} - -void -event_list::unlink ( event *e ) -{ - if ( e->_next ) - e->_next->_prev = e->_prev; - else - _tail = e->_prev; - - if ( e->_prev ) - e->_prev->_next = e->_next; - else - _head = e->_next; - - --_size; -} - - - -void -event_list::clear ( void ) -{ - for ( event *e = _head; e ; ) - { - event *n = e->_next; - delete e; - e = n; - } - - _head = NULL; - _tail = NULL; - _size = 0; -} - -void -event_list::mix ( event *ne ) -{ - FOR_ALL( e ) - if ( *e == *ne ) - { - /* already have an event like this, drop it */ - - if ( ne->linked() ) - delete ne->link(); - - delete ne; - - return; - } - - insert( ne ); - if ( ne->linked() ) - insert( ne->link() ); - -} - -/** remove elements from list /el/ to this list */ -void -event_list::merge ( event_list *el ) -{ - event *n; - for ( event *e = el->_head; e; e = n ) - { - n = e->_next; - - el->unlink( e ); - - insert( e ); - } -} - -/** unlink event e */ -void -event_list::remove ( event *e ) -{ - unlink( e ); - delete e; -} - -/** sorted insert /e/ */ -void -event_list::insert ( event *e ) -{ - /* find the place to insert */ - RFOR_ALL( i ) - if ( *e >= *i ) - { - _insert( i->_next, e ); - return; - } - - _insert( _head, e ); -} - -/** just append event without sorting */ -void -event_list::append ( event *e ) -{ - _insert( NULL, e ); -} - -event * -event_list::first ( void ) const -{ - return _head; -} - -event * -event_list::last ( void ) const -{ - return _tail; -} - - - -/*************/ -/* Selection */ -/*************/ - -/** select all events from /start/ to /end/ inclusive */ -void -event_list::select ( tick_t start, tick_t end ) -{ - FOR_ALL( e ) - { - tick_t ts = e->timestamp(); - - /* don't count note offs exactly on start */ - if ( ts == start && e->is_note_off() ) - continue; - - if ( ts >= start && ts < end ) - e->select(); - } -} - -/** select note evenets from /start/ to /end/ within range /hi/ through /lo/ */ -void -event_list::select ( tick_t start, tick_t end, int hi, int lo ) -{ - FOR_ALL( e ) - { - tick_t ts = e->timestamp(); - - /* don't count note offs exactly on start */ - if ( ! e->is_note_on() ) - continue; - - if ( ts >= start && ts < end && - e->note() <= hi && e->note() >= lo ) - e->select(); - } -} - -/** select ALL events */ -void -event_list::select_all ( void ) -{ - FOR_ALL( e ) - e->select(); -} - -void -event_list::select_none ( void ) -{ - FOR_ALL( e ) - e->deselect(); -} - -void -event_list::invert_selection ( void ) -{ - FOR_ALL( e ) - if ( ! e->is_note_off() ) - { - if ( e->selected() ) - e->deselect(); - else - e->select(); - } -} - -/** remove all selected events */ -void -event_list::remove_selected ( void ) -{ - FOR_SELECTED( e ) - { - remove( e ); - } -} - -/** transpose selected notes (ignoring other event types) by /n/ tones - * (may span octaves) */ -void -event_list::transpose_selected ( int n ) -{ - FOR_SELECTED( e ) - { - if ( e->is_note_on() ) - e->note( e->note() + n ); - } - -} - -/** change all notes of value /from/ to /to/ */ -void -event_list::rewrite_selected ( int from, int to ) -{ - FOR_SELECTED( e ) - { - if ( e->is_note_on() && e->note() == from ) - e->note( to ); - } -} - - -/** get timestamp of earliest selected event */ -tick_t -event_list::selection_min ( void ) -{ - FOR_SELECTED( e ) - return e->timestamp(); - - return 0; -} - -tick_t -event_list::selection_max ( void ) -{ - RFOR_SELECTED( e ) - return e->timestamp(); - - return 0; -} - -/** move selected events by offset /o/ */ -void -event_list::move_selected ( long o ) -{ - if ( o < 0 ) - if ( selection_min() < (tick_t)( 0 - o ) ) - return; - - if ( o < 0 ) - { - FOR_SELECTED( e ) - move( e, o ); - } - else - { - RFOR_SELECTED( e ) - move( e, o ); - } -} - -void -event_list::push_selection ( void ) -{ - FOR_ALL( e ) - if ( e->_selected ) - ++e->_selected; -} - -void -event_list::pop_selection ( void ) -{ - FOR_ALL( e ) - if ( e->_selected ) - --e->_selected; -} - - - -/** verify that all note ons are linked to note offs */ -bool -event_list::verify ( void ) const -{ - FOR_ALL( e ) - if ( e->is_note_on() && ! e->linked() ) - return false; - - return true; -} - -/** link /e/ (a note on) with the next corresponding note off */ -void -event_list::link ( event *on ) -{ - if ( ! on->is_note_on() ) - return; - - for ( event *off = on->_next; off; off = off->_next ) - { - if ( off->linked() ) - continue; - - if ( off->is_note_off() && - off->channel() == on->channel() && - off->note() == on->note() ) - { - on->link( off ); - return; - } - } - - WARNING( "no corresponding note_off found for note on, repairing" ); - - event *off = new event( *on ); - - off->opcode( event::NOTE_OFF ); - - on->link( off ); - - insert( off ); -} - -/** insert /l/ ticks of time at /start/ */ -void -event_list::insert_time ( tick_t start, tick_t l ) -{ - FOR_ALL( e ) - { - tick_t ts = e->timestamp(); - - if ( e->is_note_off() ) - continue; - - if ( ts >= start ) - { - if ( e->is_note_on() ) - { - /* only notes ENTIRELY WITHIN the range will be moved */ - e->timestamp( ts + l ); - e->link()->timestamp( e->link()->timestamp() + l ); - } - else - e->timestamp( e->timestamp() + l ); - } - } - - sort(); -} - -/** delete events in range and close the gap */ -void -event_list::delete_time ( tick_t start, tick_t end ) -{ - tick_t l = end - start; - - push_selection(); - - select( start, end ); - - remove_selected(); - - pop_selection(); - - /* cut out the slack */ - FOR_ALL( e ) - { - tick_t ts = e->timestamp(); - - if ( ts >= end ) - e->timestamp( ts - l ); - } -} - -/** link all note ons to subsequent note offs */ -void -event_list::relink ( void ) -{ - /* clear links */ - FOR_ALL( e ) - e->link( NULL ); - - /* link */ - FOR_ALL( on ) - link( on ); - - if ( ! verify() ) - ASSERTION( "event list failed verification" ); -} - -/** resort event /e/ */ -void -event_list::sort ( event *e ) -{ - unlink( e ); - - insert( e ); -} - -/** resort entire list */ -void -event_list::sort ( void ) -{ - event_list *temp = new event_list( ); - - _head = temp->_head; - _tail = temp->_tail; - - FOR_ALL( n ) - temp->insert( n ); - - temp->_head = NULL; - - delete temp; - - relink(); -} - -/** move event /e/ by /o/ ticks */ -void -event_list::move ( event *e, long o ) -{ - e->timestamp( e->timestamp() + o ); - - sort( e ); -} - -bool -event_list::empty ( void ) const -{ - return _head == NULL; -} - -size_t -event_list::size ( void ) const -{ - return _size; -} - -void -event_list::_hi_lo ( bool sel, int *hi, int *lo ) const -{ - *hi = 0; - *lo = 127; - - FOR_ALL( e ) - { - if ( sel && ! e->selected() ) - continue; - - if ( ! e->is_note_on() ) - continue; - - int n = e->note(); - - if ( n > *hi ) - *hi = n; - - if ( n < *lo ) - *lo = n; - } -} - -/** set /hi/ and /lo/ to the lowest and highest pitched note events in - * this list, respectively */ -void -event_list::hi_lo_note ( int *hi, int *lo ) const -{ - _hi_lo( false, hi, lo ); -} - -void -event_list::selected_hi_lo_note ( int *hi, int *lo ) const -{ - _hi_lo( true, hi, lo ); -} diff --git a/sequencer/src/event_list.H b/sequencer/src/event_list.H deleted file mode 100644 index a4a9305..0000000 --- a/sequencer/src/event_list.H +++ /dev/null @@ -1,89 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -#pragma once - -#include "event.H" - -#include -using std::list; - -class midievent; - -class event_list { - - event * _head; - event * _tail; - - size_t _size; - - void _insert ( event *o, event *n ); - void _copy ( const event_list *el ); - void _hi_lo ( bool sel, int *hi, int *lo ) const; - -public: - - event_list ( void ); - ~event_list ( void ); - event_list ( const event_list &el ); - - void clear ( void ); - void merge ( event_list *el ); - void unlink ( event *e ); - void remove ( event *e ); - void insert ( event *e ); - event * first ( void ) const; - event * last ( void ) const; - void select ( tick_t start, tick_t end ); - void select ( tick_t start, tick_t end, int hi, int lo ); - - void select_all ( void ); - void select_none ( void ); - void invert_selection ( void ); - - void remove_selected ( void ); - void transpose_selected ( int n ); - tick_t selection_min ( void ); - tick_t selection_max ( void ); - void move_selected ( long o ); - void push_selection ( void ); - void pop_selection ( void ); - bool verify ( void ) const; - void link ( event *on ); - void insert_time ( tick_t start, tick_t l ); - void delete_time ( tick_t start, tick_t end ); - void relink ( void ); - void sort ( event *e ); - void sort ( void ); - void move ( event *e, long o ); - bool empty ( void ) const; - size_t size ( void ) const; - void append ( event *e ); - void mix ( event *ne ); - void hi_lo_note ( int *hi, int *lo ) const; - void rewrite_selected ( int from, int to ); - void selected_hi_lo_note ( int *hi, int *lo ) const; - - - event_list & operator= ( const event_list &rhs ); - event_list & operator= ( const list &rhs ); - event *operator[] ( unsigned int index ); - - // friend class event; -}; diff --git a/sequencer/src/grid.C b/sequencer/src/grid.C index 9242480..b567916 100644 --- a/sequencer/src/grid.C +++ b/sequencer/src/grid.C @@ -25,6 +25,8 @@ #include "smf.H" +using namespace MIDI; + Grid::Grid ( void ) { _name = NULL; @@ -44,7 +46,8 @@ Grid::Grid ( void ) d->length = 0; _bpb = 4; - _ppqn = 1; + /* how many grid positions there are per beat */ + _ppqn = 4; viewport.h = 32; viewport.w = 32; @@ -184,31 +187,6 @@ Grid::_delete ( int x, int y ) return false; } -bool -Grid::_get ( struct dash *d, int x, int y ) const -{ - event *e = _event ( x, y, false ); - - if ( e ) - { - tick_t ts = e->timestamp(); - tick_t l = 0; - - if ( e->linked() ) - l = e->link()->timestamp() - ts; - else - WARNING( "Found unlinked note on" ); - - d->timestamp = ts_to_x( ts ); - d->length = ts_to_x( l ); - d->color = e->note_velocity(); - return true; - } - - return false; -} - - void Grid::clear ( void ) { @@ -219,13 +197,6 @@ Grid::clear ( void ) unlock(); } - -int -Grid::get ( struct dash *d, int x, int y ) const -{ - return _get( d, x, y ); -} - void Grid::del ( int x, int y ) { @@ -318,8 +289,15 @@ Grid::expand ( void ) unlock(); } +/** returns true if there is a note event at x,y */ +bool +Grid::is_set ( int x, int y ) const +{ + return _event( x, y, false ); +} + void -Grid::put ( int x, int y, tick_t l ) +Grid::put ( int x, int y, tick_t l, int velocity ) { int xl = ts_to_x( l ); @@ -328,10 +306,8 @@ Grid::put ( int x, int y, tick_t l ) event *on = new event; event *off = new event; - struct dash d; - // Don't allow overlap (Why not?) - if ( get( &d, x, y ) || get( &d, x + xl - 1, y ) ) + if ( _event( x, y, false ) || _event( x + xl - 1, y, false ) ) return; DMESSAGE( "put %d,%d", x, y ); @@ -343,18 +319,19 @@ Grid::put ( int x, int y, tick_t l ) on->status( event::NOTE_ON ); on->note( note ); on->timestamp( ts ); - on->note_velocity( 64 ); + on->note_velocity( velocity ); on->link( off ); off->status( event::NOTE_OFF ); off->note( note ); off->timestamp( ts + l ); - off->note_velocity( 64 ); + off->note_velocity( velocity ); off->link( on ); _rw->events.insert( on ); _rw->events.insert( off ); + expand(); unlock(); @@ -457,6 +434,95 @@ Grid::adj_duration ( int x, int y, int l ) } +void +Grid::set_duration ( int x, int y, int ex ) +{ + if ( ex < 1 ) + return; + + lock(); + + event *e = _event( x, y, true ); + + if ( e ) + { + DMESSAGE( "adjusting duration" ); + + e->note_duration( x_to_ts( ex ) ); + + _rw->events.sort( e->link() ); + } + + unlock(); +} + +void +Grid::get_note_properties ( int x, int y, note_properties *p ) const +{ + const event *e = _event( x, y, false ); + + e->get_note_properties( p ); + + p->start = p->start; + p->duration = p->duration; + p->note = note_to_y( p->note ); +} + +/* void */ +/* Grid::set_note_properties ( int x, int y, const note_properties *p ) */ +/* { */ +/* lock(); */ + +/* const event *e = _event( x, y, true ); */ + +/* e->set_note_properties( p ); */ + +/* unlock(); */ +/* } */ + + + + +/** if there's a note at grid coordinates x,y, then adjust them to the beginning of the note */ +int +Grid::get_start ( int *x, int *y ) const +{ + const event *e = _event( *x, *y, false ); + + if ( e ) + { + *x = ts_to_x( e->timestamp() ); + return 1; + } + else + return 0; +} + +void +Grid::set_end ( int x, int y, int ex ) +{ + lock(); + + event *e = _event( x, y, true ); + + if ( e ) + { + DMESSAGE( "adjusting duration" ); + + tick_t ts = x_to_ts( ex ); + + if ( ts > e->timestamp() && + ts - e->timestamp() > x_to_ts( 1 ) ) + { + e->note_duration( ts - e->timestamp() ); + + _rw->events.sort( e->link() ); + } + } + + unlock(); +} + void Grid::toggle_select ( int x, int y ) { @@ -544,6 +610,16 @@ Grid::select_none ( void ) unlock(); } +void +Grid::select_all ( void ) +{ + lock(); + + _rw->events.select_all(); + + unlock(); +} + void Grid::invert_selection ( void ) { @@ -656,13 +732,13 @@ Grid::print ( void ) const void Grid::draw_notes ( draw_note_func_t draw_note, void *userdata ) const { - int bx = viewport.x; - int by = viewport.y; - int bw = viewport.w; - int bh = viewport.h; + /* int bx = viewport.x; */ + /* int by = viewport.y; */ + /* int bw = viewport.w + 100; /\* FIXME: hack *\/ */ + /* int bh = viewport.h; */ - const tick_t start = x_to_ts( bx ); - const tick_t end = x_to_ts( bx + bw ); + /* const tick_t start = x_to_ts( bx ); */ + /* const tick_t end = x_to_ts( bx + bw ); */ data *d = const_cast< data *>( _rd ); @@ -677,12 +753,14 @@ Grid::draw_notes ( draw_note_func_t draw_note, void *userdata ) const const tick_t tse = e->link()->timestamp(); - if ( tse >= start && ts <= end ) - draw_note( ts_to_x( ts ), - note_to_y( e->note() ), - ts_to_x( tse - ts ), - e->note_velocity(), - userdata ); + /* if ( tse >= start && ts <= end ) */ + draw_note( // ts_to_x( ts ), + ts, + note_to_y( e->note() ), + tse - ts, + e->note_velocity(), + e->selected(), + userdata ); } } @@ -768,12 +846,17 @@ Grid::ppqn ( void ) const void Grid::resolution ( unsigned int n ) { - if ( n < 4 ) - ASSERTION( "bad resolution: %d", n ); + /* if ( n < 4 ) */ + /* ASSERTION( "bad resolution: %d", n ); */ - _ppqn = n / 4; +// _ppqn = n / 4; + _ppqn = n; DMESSAGE( "%d setting resolution to %d", n, _ppqn ); + /* ensure that the correct number of bars are in the viewport */ + + viewport.w = _ppqn * _bpb * 2; + signal_events_change(); signal_settings_change(); diff --git a/sequencer/src/grid.H b/sequencer/src/grid.H index 4d630d4..476f16c 100644 --- a/sequencer/src/grid.H +++ b/sequencer/src/grid.H @@ -19,9 +19,8 @@ #pragma once -#include "event.H" -#include "event_list.H" -#include "dash.H" +#include +#include #include "const.h" #include "instrument.H" @@ -48,7 +47,7 @@ struct data { tick_t length; int state; - event_list events; + MIDI::event_list events; data( void ) { @@ -107,7 +106,7 @@ protected: char *_notes; char *_name; int _number; - + bool _suspend_update; unsigned int _bpb; /* beats per bar */ @@ -131,9 +130,8 @@ protected: list _history; void _remove_marked ( void ); - event * _event ( int x, int y, bool write ) const; + MIDI::event * _event ( int x, int y, bool write ) const; bool _delete ( int x, int y ); - bool _get ( struct dash *d, int x, int y ) const; void _link ( void ); void _relink ( void ); void _fix_length ( void ); @@ -145,7 +143,7 @@ private: public: - typedef void draw_note_func_t ( int x, int y, int l, int velocity, void *userdata ); + typedef void draw_note_func_t ( tick_t x, int y, tick_t l, int velocity, int selected, void *userdata ); void draw_notes ( draw_note_func_t draw_note, void *userdata ) const; @@ -158,10 +156,12 @@ public: virtual ~Grid ( void ); Grid ( const Grid &rhs ); + virtual bool velocity_sensitive ( void ) const { return true; } + int y_to_note ( int y ) const; int note_to_y ( int n ) const; - tick_t x_to_ts ( uint x ) const; - uint ts_to_x ( tick_t ts ) const; + tick_t x_to_ts ( tick_t x ) const; + tick_t ts_to_x ( tick_t ts ) const; virtual Grid * create ( void ) = 0; virtual Grid * clone ( void ) = 0; @@ -170,17 +170,20 @@ public: virtual Grid * by_number ( int n ) const = 0; - virtual void put ( int x, int y, tick_t l ); + virtual void put ( int x, int y, tick_t l, int velocity = 64 ); + virtual bool is_set ( int x, int y ) const; void lock ( void ); void unlock ( void ); void clear ( void ); - int get ( struct dash *d, int x, int y ) const; void del ( int x, int y ); void adj_velocity ( int x, int y, int n ); void adj_duration ( int x, int y, int l ); + void set_duration ( int x, int y, int l ); + void set_end ( int x, int y, int l ); + int get_start ( int *x, int *y ) const; void move ( int x, int y, int nx, int ny ); - void record_event ( event *e ); + void record_event ( MIDI::event *e ); tick_t index ( void ) const; bool playing ( void ) const; @@ -219,6 +222,7 @@ public: void select ( int start, int end, int t, int b ); void delete_time ( int start, int end ); void select_none ( void ); + void select_all ( void ); void invert_selection ( void ); void resolution ( unsigned int n ); @@ -228,11 +232,19 @@ public: void draw ( Canvas *c, int bx, int by, int bw, int bh ); void print ( void ) const; - event_list * events ( void ) const; - void events ( const event_list * el ); + MIDI::event_list * events ( void ) const; + void events ( const MIDI::event_list * el ); + + void get_note_properties ( int x, int y, MIDI::note_properties *p ) const; + + virtual tick_t default_length ( void ) const + { + return PPQN; + } }; + inline int Grid::y_to_note ( int y ) const { @@ -246,14 +258,14 @@ Grid::note_to_y ( int n ) const } inline tick_t -Grid::x_to_ts ( unsigned int x ) const +Grid::x_to_ts ( tick_t x ) const { return (x * PPQN) / _ppqn; // return x * (PPQN / _ppqn); } -inline unsigned int +inline tick_t Grid::ts_to_x ( tick_t ts ) const { return (ts * _ppqn) / PPQN; diff --git a/sequencer/src/gui/Makefile b/sequencer/src/gui/Makefile deleted file mode 100644 index 6c50638..0000000 --- a/sequencer/src/gui/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - - -all: - @ make -s -C .. diff --git a/sequencer/src/gui/draw.C b/sequencer/src/gui/draw.C deleted file mode 100644 index bde407d..0000000 --- a/sequencer/src/gui/draw.C +++ /dev/null @@ -1,105 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2007-2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -/* This file contains ALL platform specific drawing code required by the canvas */ - -#include "ui.H" -#include "draw.H" - -#include "../common.h" -#include -#include - -#include "../canvas.H" - -struct color_table { - int state; - unsigned char r, g, b; -}; - -struct color_table color_defs[] = { - { EMPTY, 18, 18, 18 }, - { FULL, 255, 69, 0 }, - { PARTIAL, 0, 0, 0 }, - { CONTINUED, 80, 80, 80 }, - { LINE, 10, 10, 10 }, - { HIT, 255, 255, 255 }, - { PLAYHEAD, 10, 69, 10 }, - { SELECTED, 255, 10, 255 }, -}; - -Fl_Color *state_colors; - -Fl_Color velocity_colors[128]; -Fl_Color velocity2_colors[128]; - -bool draw_borders = 1; - -void -init_colors ( void ) -{ - unsigned int i; - /* velocity colors */ - - for ( i = 128; i--; ) - { -// velocity_colors[i] = fl_color_average( FL_GRAY, fl_rgb_color( i * 2, 255 - i * 2, 32 ), 0.4 ); -// velocity_colors[i] = fl_rgb_color( i * 2, 0, 0 ); - velocity_colors[i] = fl_rgb_color( i, 0, 0 ); - velocity2_colors[i] = fl_color_average( FL_WHITE, velocity_colors[i], 0.5 ); - } - - state_colors = (Fl_Color*)malloc(sizeof( Fl_Color ) * MAX_STATE ); - - for ( i = elementsof( color_defs ); i--; ) - { - state_colors[ color_defs[i].state ] = fl_rgb_color( color_defs[i].r, - color_defs[i].g, - color_defs[i].b ); - } -} - -extern UI *ui; - -static -void -clear_status ( void * ) -{ - ui->status->label( NULL ); -} - -/** inform the user of something via a status bar */ -void -gui_status ( const char *fmt, ... ) -{ - va_list args; - - static char pat[256]; - - if ( fmt ) - { - va_start( args, fmt ); - vsnprintf( pat, 256, fmt, args ); - va_end( args ); - } - - ui->status->label( pat ); - - Fl::add_timeout( 5.0f, clear_status ); -} diff --git a/sequencer/src/gui/draw.H b/sequencer/src/gui/draw.H deleted file mode 100644 index f7b4a0a..0000000 --- a/sequencer/src/gui/draw.H +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - -/* canvas node states */ -enum { -/* real */ - EMPTY, /* nothing */ - FULL, /* dot or dash head */ - PARTIAL, - CONTINUED, /* dash tail */ - SELECTED, -/* virtual */ - HIT, /* playhead hit */ - LINE, /* beat line */ - PLAYHEAD, - MAX_STATE, -}; - -#define MAX_REAL_STATE HIT - -#define STATE_MASK 0x0F -#define STATE_FLAG_MASK (~ (STATE_MASK) ) - -/* flags */ -enum { - F_PLAYHEAD = 1 << 0, /* playhead is on item */ - F_P1 = 1 << 1, - F_P2 = 1 << 2, - F_SELECTION = 1 << 3 /* item is part of the selection box */ -}; - - -void init_colors ( void ); -void gui_status ( const char *fmt, ... ); diff --git a/sequencer/src/gui/event_edit.fl b/sequencer/src/gui/event_edit.fl index f341860..d9eb968 100644 --- a/sequencer/src/gui/event_edit.fl +++ b/sequencer/src/gui/event_edit.fl @@ -28,6 +28,15 @@ decl {\#include "../grid.H"} {private local decl {\#include "../scale.H"} {private local } +decl {\#include } {public global +} + +decl {\#include } {selected public global +} + +decl {using namespace MIDI;} {private local +} + decl {extern Fl_Color velocity_colors[];} {private local } @@ -40,9 +49,9 @@ class Event_Editor {open } decl {Grid *_grid;} {private local } - decl {event_list *_old;} {private local + decl {MIDI::event_list *_old;} {private local } - decl {event_list *_el;} {private local + decl {MIDI::event_list *_el;} {private local } decl {int _y;} {private local } @@ -61,10 +70,9 @@ _el = _old = NULL; o->hide(); Fl::delete_widget( o );} open - xywh {966 99 655 805} type Double resizable + xywh {968 122 655 805} type Double resizable code0 {\#include "event_edit.H"} - code1 {\#include "../grid.H"} - code2 {\#include "../event_list.H"} modal size_range {0 0 659 803} visible + code1 {\#include "../grid.H"} modal size_range {0 0 659 803} visible } { Fl_Scroll {} { label {Event List} open @@ -192,7 +200,7 @@ update_widgets();} {} code {int i = 0; if ( ! _el->empty() ) -for ( event* e = (*_el)[0]; e = e->next(); i++ ) +for ( event* e = (*_el)[0]; ( e = e->next() ); i++ ) { Event_Widget *ew; @@ -253,9 +261,8 @@ while( w->shown() ) } widget_class Event_Widget {user_data_type {void *} open - xywh {943 216 590 30} type Single - code0 {\#include "../event.H"} - code1 {_event = NULL;} + xywh {945 239 590 30} type Single + code0 {_event = NULL;} class Fl_Group size_range {400 24 0 24} visible } { decl {static const Fl_Color note_color = FL_BLACK;} {private local @@ -270,11 +277,11 @@ widget_class Event_Widget {user_data_type {void *} open } decl {static const Fl_Color pitch_color = FL_GREEN} {private local } - decl {event *_event;} {private local + decl {MIDI::event *_event;} {private local } decl {Fl_Group *tab;} {private local } - Function {ev( event * e )} {open return_type void + Function {ev( MIDI::event * e )} {open return_type void } { code {if ( e && ( _event == NULL ) ) activate(); @@ -374,7 +381,7 @@ name->redraw(); // redraw();} {} } - Function {ev( void )} {open return_type {event *} + Function {ev( void )} {open return_type {MIDI::event *} } { code {return _event;} {} } @@ -491,7 +498,7 @@ do_callback();} Fl_Slider {} { label {Pressure:} user_data this - callback cb_lsb selected + callback cb_lsb xywh {359 0 230 24} type {Horz Fill} align 4 when 4 maximum 127 step 1 } } diff --git a/sequencer/src/gui/input.C b/sequencer/src/gui/input.C deleted file mode 100644 index 969589d..0000000 --- a/sequencer/src/gui/input.C +++ /dev/null @@ -1,347 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2007-2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -#include - -/* system */ -#include -#include - -#include "../non.H" -#include "draw.H" -#include "../common.h" - -#include "ui.H" -#include "../transport.H" - -extern UI *ui; - -void -async_exec ( const char *cmd ) -{ - if ( fork() ) - { - printf( "Executed command \"%s\"\n", cmd ); - return; - } - - system( cmd ); - exit(0); -} - -int -canvas_input_callback ( O_Canvas *widget, Canvas *c, int m ) -{ - // MESSAGE( "Hello, my name is %s", widget->parent()->label() ); - - int ow, oh; - - int x, y; - int processed = 1; - - x = Fl::event_x(); - y = Fl::event_y(); - - - ow = c->grid()->viewport.w; - oh = c->grid()->viewport.h; - - switch ( m ) - { - case FL_KEYBOARD: - { - -/* if ( Fl::event_state() & FL_ALT || Fl::event_state() & FL_CTRL ) */ -/* // this is more than a simple keypress. */ -/* return 0; */ - - - if ( Fl::event_state() & FL_CTRL ) - { - switch ( Fl::event_key() ) - { - case FL_Delete: - c->delete_time(); - break; - case FL_Insert: - c->insert_time(); - break; - case FL_Right: - c->pan( TO_NEXT_NOTE, 0 ); - break; - case FL_Left: - c->pan( TO_PREV_NOTE, 0 ); - break; - default: - return 0; - } - } - else - if ( Fl::event_state() & FL_ALT ) - return 0; - - switch ( Fl::event_key() ) - { - case FL_Left: - c->pan( LEFT, 1 ); - break; - case FL_Right: - c->pan( RIGHT, 1 ); - break; - case FL_Up: - c->pan( UP, 1 ); - break; - case FL_Down: - c->pan( DOWN, 1 ); - break; - case FL_Delete: - if ( Fl::event_state() & FL_SHIFT ) - c->grid()->clear(); - else - c->grid()->delete_selected(); - break; - default: - /* have to do this to get shifted keys */ - switch ( *Fl::event_text() ) - { - case 'f': - c->pan( TO_PLAYHEAD, 0 ); - break; - case 'r': - c->select_range(); - break; - case 'q': - c->grid()->select_none(); - break; - case 'i': - c->invert_selection(); - break; - case '1': - c->h_zoom( 2.0f ); - break; - case '2': - c->h_zoom( 0.5f ); - break; - case '3': - c->v_zoom( 2.0f ); - break; - case '4': - c->v_zoom( 0.5f ); - break; - case ' ': - transport.toggle(); - break; - case '[': - { - Grid *g = NULL; - -#define IS_PATTERN (widget->parent() == ui->pattern_tab) -#define IS_PHRASE (widget->parent() == ui->phrase_tab) -#define IS_SEQUENCE (widget->parent() == ui->sequence_tab) - - /* is there no nicer way to do this shit in c++? */ - g = c->grid()->by_number( c->grid()->number() - 1 ); - - if ( g ) - { - c->grid( g ); - processed = 2; - } - break; - } - case ']': - { - Grid *g = NULL; - - /* is there no nicer way to do this shit in c++? */ - g = c->grid()->by_number( c->grid()->number() + 1 ); - - if ( g ) - { - c->grid( g ); - processed = 2; - } - break; - } - case '<': - c->move_selected( LEFT, 1 ); - break; - case '>': - c->move_selected( RIGHT, 1 ); - break; - case ',': - c->move_selected( UP, 1 ); - break; - case '.': - c->move_selected( DOWN, 1 ); - break; - case 'C': - c->crop(); - break; - case 'c': - { - Grid *g = c->grid()->create(); - - if ( g ) - { - c->grid( g ); - - ui->update_sequence_widgets(); - } - - break; - } - case 'd': - { - MESSAGE( "duplicating thing" ); - c->grid( c->grid()->clone() ); - - // number of phrases may have changed. - ui->update_sequence_widgets(); - - break; - - } - case 'D': - c->duplicate_range(); - break; - case 't': - c->grid()->trim(); - break; - - case 'm': - c->grid()->mode( c->grid()->mode() == MUTE ? PLAY : MUTE ); - break; - case 's': - c->grid()->mode( c->grid()->mode() == SOLO ? PLAY : SOLO ); - break; - default: - processed = 0; - break; - } - break; - } - break; - } - case FL_PUSH: - { - switch ( Fl::event_button() ) - { - case 1: - int note; - if ( ( note = c->is_row_name( x, y ) ) >= 0 ) - { - DMESSAGE( "click on row %d", note ); - Instrument *i = ((pattern *)c->grid())->mapping.instrument(); - - if ( i ) - { - ui->edit_instrument_row( i, note ); - - c->changed_mapping(); - } - } - else - { - if ( Fl::event_state() & FL_SHIFT ) - { - c->start_cursor( x, y ); - break; - } - - if ( IS_PATTERN && Fl::event_state() & ( FL_ALT | FL_CTRL ) ) - c->randomize_row( y ); - else - c->set( x, y ); - } - break; - case 3: - if ( Fl::event_state() & FL_SHIFT ) - { - c->end_cursor( x, y ); - break; - } - - c->unset( x, y ); - break; - case 2: - c->select( x, y ); - break; - default: - processed = 0; - } - break; - } - case FL_RELEASE: - break; - case FL_DRAG: - break; -/* case FL_DRAG: */ -/* { */ -/* if ( ! lmb_down ) */ -/* break; */ - -/* // c->grid()->move( x, y, nx ); */ -/* break; */ -/* } */ - case FL_MOUSEWHEEL: - { - if ( Fl::event_state() & FL_CTRL ) - c->adj_length( x, y, (0 - Fl::event_dy()) ); - else if ( Fl::event_state() & FL_ALT ) - c->adj_color( x, y, (0 - Fl::event_dy()) * 5 ); - else if ( Fl::event_state() & FL_SHIFT ) - { - if ( Fl::event_dy() > 0 ) - { - c->pan( RIGHT, Fl::event_dy() * 5 ); - } - else - { - c->pan( LEFT, 0 - Fl::event_dy() * 5 ); - } - } - else - { - if ( Fl::event_dy() > 0 ) - { - c->pan( DOWN, Fl::event_dy() * 1 ); - } - else - { - c->pan( UP, (0 - Fl::event_dy()) * 1 ); - } - } - - break; - } - default: - processed = 0; - } - - int nw, nh; - nw = c->grid()->viewport.w; - nh = c->grid()->viewport.h; - - // layout of canvas changed... requires clearing. - if ( oh != nh || ow != nw ) - return 3; - - return processed; -} diff --git a/sequencer/src/gui/input.H b/sequencer/src/gui/input.H deleted file mode 100644 index a3e58d3..0000000 --- a/sequencer/src/gui/input.H +++ /dev/null @@ -1,12 +0,0 @@ - -#pragma once - -#include "../canvas.H" -#include "../common.h" - -class O_Canvas; - -void disp_message ( char *s ); -void async_exec ( const char *cmd ); -int canvas_input_callback ( O_Canvas *widget, Canvas *c, int m ); -int disp_init ( int argc, char **argv ); diff --git a/sequencer/src/gui/ui.fl b/sequencer/src/gui/ui.fl index 8ce1d89..374fc91 100644 --- a/sequencer/src/gui/ui.fl +++ b/sequencer/src/gui/ui.fl @@ -34,9 +34,18 @@ decl {class Fl_Scalepack;} {public local decl {class Fl_Sometimes_Input;} {public local } +decl {\#include } {public global +} + +decl {\#include } {public local +} + decl {\#include } {private local } +decl {\#include } {public global +} + decl {\#include } {private local } @@ -49,6 +58,9 @@ decl {\#include } {private local decl {\#include } {private local } +decl {\#include } {private local +} + decl {\#include "event_edit.H"} {private local } @@ -79,11 +91,25 @@ decl {class Instrument_Editor;} {private local decl {Fl_Color canvas_background_color;} {public local } -decl {extern Fl_Color velocity_colors[];} {private local +decl {Fl_Color velocity_colors[128];} {public local +} + +Function {init_colors()} {open private C return_type {static void} +} { + code {unsigned int i; + /* velocity colors */ + + Fl_Color lo = fl_color_average( FL_CYAN, FL_BLACK, 0.10 ); + Fl_Color hi = fl_color_average( FL_CYAN, FL_WHITE, 0.80 ); + + for ( i = 128; i--; ) + { + velocity_colors[i] = fl_color_average( hi, lo, 1.0 * ((float)i / 128) ); + }} {} } widget_class Visual_Metronome {open - xywh {640 936 100 100} type Double visible + xywh {1166 936 100 100} type Double visible } { Fl_Slider progress { private xywh {139 115 1149 23} type Horizontal box FLAT_BOX color 7 selection_color 54 @@ -99,6 +125,9 @@ int bw = w() / _bpb; int b = _bpb; +if ( b ) +{ + for ( int i = 0; i < b; ++i ) { if ( i == _beat ) @@ -123,7 +152,7 @@ for ( int i = 0; i < b; ++i ) } } - +} progress->resize( x(), y() + h() - 14, w(), 14 ); if ( damage() & FL_DAMAGE_CHILD ) @@ -195,6 +224,39 @@ tpb( transport.ticks_per_beat ); beat( transport.beat - 1 ); tick( transport.tick - 1 );}} {} } + code {_bpb = 0; +_tpb = 0; +_beat = 0; +_tick = 0; +_flip = 0;} {} +} + +class Overlay_Callback_Window {: {public Fl_Overlay_Window} +} { + decl {void (*_draw_overlay_callback)(void*);} {private local + } + decl {void *_draw_overlay_userdata;} {private local + } + Function {Overlay_Callback_Window(int X, int Y, int W, int H, const char *L=0) : Fl_Overlay_Window(X,Y,W,H,L)} {open + } { + code {_draw_overlay_callback = 0; +_draw_overlay_userdata = 0;} {} + } + Function {Overlay_Callback_Window(int W, int H, const char *L=0) : Fl_Overlay_Window(W,H,L)} {open + } { + code {_draw_overlay_callback = 0; +_draw_overlay_userdata = 0;} {} + } + Function {draw_overlay()} {open return_type {virtual void} + } { + code {if ( _draw_overlay_callback ) + _draw_overlay_callback( _draw_overlay_userdata );} {} + } + Function {draw_overlay_callback( void(*cb)(void*), void *v)} {open return_type void + } { + code {_draw_overlay_callback = cb; +_draw_overlay_userdata = v;} {} + } } Function {update_transport( void * )} {open return_type void @@ -275,12 +337,12 @@ playback_mode_menu = NULL; main_window = make_main_window(); seq_window = make_seq_window(); +init_colors(); + make_randomization_dialog(); // make_instrument_edit_dialog(); -Fl::add_handler( shortcut_handler ); - // use old focus behavior Fl::visible_focus( 0 ); @@ -328,6 +390,15 @@ asprintf( &path, "%s/%s", config.user_config_dir, "view" ); free( path );} {} } + Function {draw_overlay( void *v )} {open protected return_type {static void} + } { + code {((UI*)v)->draw_overlay();} {} + } + Function {draw_overlay()} {open protected return_type void + } { + code {if ( pattern_canvas_widget ) + pattern_canvas_widget->draw_overlay();} {} + } Function {make_main_window()} {open } { Fl_Window main_window { @@ -337,52 +408,149 @@ if ( Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape ) return; if ( maybe_save_song() ) - quit();} open selected - xywh {834 231 865 805} type Double color 47 resizable + quit();} open + xywh {750 223 865 805} type Double color 47 resizable code0 {o->color( FL_BACKGROUND_COLOR );} - code1 {o->xclass( APP_NAME );} size_range {700 509 0 0} visible + code1 {o->draw_overlay_callback( &UI::draw_overlay, this );} + code2 {o->xclass( APP_NAME );} + class Overlay_Callback_Window size_range {700 509 0 0} visible } { Fl_Group {} {open - xywh {0 30 865 70} box FLAT_BOX + xywh {0 25 865 65} box FLAT_BOX } { - Fl_Value_Input {} { - label BPM - callback {transport.set_beats_per_minute( o->value() );} - xywh {389 47 45 25} labelsize 9 align 1 when 8 - code1 {transport.signal_tempo_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} - code2 {o->value( transport.beats_per_minute );} - } - Fl_Value_Input {} { - callback {transport.set_beats_per_bar( o->value() );} - xywh {444 47 26 25} - code0 {transport.signal_bpb_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} - code1 {o->value( transport.beats_per_bar );} - } - Fl_Box {} { - label {/} - xywh {469 47 19 25} - } - Fl_Value_Input {} { - callback {transport.set_beat_type( o->value() );} - xywh {489 47 24 25} - code0 {transport.signal_beat_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} - code1 {o->value( transport.beat_type );} + Fl_Group {} {open + xywh {665 36 195 52} + } { + Fl_Value_Input {} { + label BPM + callback {transport.set_beats_per_minute( o->value() );} + xywh {825 68 35 19} labelsize 9 align 1 when 8 textsize 10 + code1 {transport.signal_tempo_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} + code2 {o->value( transport.beats_per_minute );} + } + Fl_Group {} { + label {Time Sig.} open + xywh {756 67 64 21} labelsize 9 + } { + Fl_Value_Input {} { + callback {transport.set_beats_per_bar( o->value() );} + xywh {756 68 24 19} textsize 10 + code0 {transport.signal_bpb_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} + code1 {o->value( transport.beats_per_bar );} + } + Fl_Box {} { + label {/} + xywh {780 67 14 21} + } + Fl_Value_Input {} { + callback {transport.set_beat_type( o->value() );} + xywh {795 68 24 19} textsize 10 + code0 {transport.signal_beat_change.connect( sigc::mem_fun( o, static_cast(&Fl_Valuator::value) ) );} + code1 {o->value( transport.beat_type );} + } + } + Fl_Choice record_mode_menu { + label {Record Mode} + callback {if ( ! transport.recording ) + config.record_mode = (record_mode_e)o->value(); +else + o->value( config.record_mode );} + xywh {755 36 105 19} box DOWN_BOX down_box BORDER_BOX color 37 labelsize 9 align 1 textsize 9 + } { + MenuItem {} { + label Merge + xywh {15 15 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label Overwrite + xywh {25 25 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label Layer + xywh {35 35 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label New + xywh {45 45 40 25} labelfont 3 labelsize 10 + } + } + Fl_Choice playback_mode_menu { + label {Playback &Mode} + xywh {665 68 85 19} box DOWN_BOX down_box BORDER_BOX color 37 labelsize 9 align 1 + } { + MenuItem {} { + label Pattern + callback {song.play_mode = PATTERN;} + xywh {5 5 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label Sequence + callback {song.play_mode = SEQUENCE;} + xywh {15 15 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label Trigger + callback {song.play_mode = TRIGGER;} + xywh {25 25 40 25} labelfont 3 labelsize 10 + } + MenuItem {} { + label Queue + callback {song.play_mode = QUEUE;} + xywh {0 0 40 24} labelfont 3 labelsize 10 + } + } + Fl_Choice edit_mode_menu { + label {Edit Mode} + xywh {665 36 85 19} box DOWN_BOX down_box BORDER_BOX color 37 labelsize 9 align 1 + } { + MenuItem {} { + label Pattern + callback {tabs->value( pattern_tab ); + + +edit_menu->activate(); +menu_bar->redraw();} + xywh {15 15 40 25} shortcut 0x80031 labelfont 3 labelsize 10 + } + MenuItem {} { + label Phrase + callback {tabs->value( phrase_tab ); + +edit_menu->activate(); +menu_bar->redraw();} + xywh {25 25 40 25} shortcut 0x80032 labelfont 3 labelsize 11 + } + MenuItem {} { + label Sequence + callback {tabs->value( sequence_tab ); + + +edit_menu->deactivate(); +menu_bar->redraw();} + xywh {25 25 40 25} shortcut 0x80033 labelfont 3 labelsize 10 + } + MenuItem {} { + label Trigger + callback {song.play_mode = TRIGGER;} + xywh {35 35 40 25} labelfont 3 labelsize 10 hide deactivate + } + } } Fl_Pack vmetro_widget { label Metronome - xywh {520 35 336 59} type HORIZONTAL box UP_BOX color 40 selection_color 48 labelsize 33 align 0 resizable + xywh {160 27 500 60} type HORIZONTAL box UP_BOX color 40 selection_color 48 labelsize 33 align 0 resizable code0 {o->box( FL_FLAT_BOX );} class Visual_Metronome } {} Fl_Pack transport_controls_group {open - xywh {4 32 156 42} type HORIZONTAL + xywh {4 27 151 60} type HORIZONTAL code0 {o->spacing( 2 );} class Fl_Scalepack } { Fl_Button play_button { label {@>} callback {transport.toggle();} - xywh {10 34 43 38} shortcut 0x20 labeltype ENGRAVED_LABEL + xywh {10 29 43 38} shortcut 0x20 labeltype ENGRAVED_LABEL } Fl_Button rec_button { label {@circle} @@ -396,10 +564,10 @@ if ( o->value() ) pattern *p = new pattern; p->length( -1 ); - pattern_c->grid( p ); + pattern_canvas_widget->grid( p ); } - ((pattern*)pattern_c->grid())->record( 0 ); + ((pattern*)pattern_canvas_widget->grid())->record( 0 ); o->labelcolor( FL_RED ); } @@ -409,97 +577,30 @@ else o->labelcolor( FL_WHITE ); }} - xywh {60 34 43 38} type Toggle shortcut 0x80072 selection_color 47 labeltype ENGRAVED_LABEL when 1 + xywh {60 29 43 38} type Toggle shortcut 0x80072 selection_color 47 labeltype ENGRAVED_LABEL when 1 } Fl_Button home_button { label {@|<} callback {transport.locate( 0 );} - xywh {110 34 43 38} shortcut 0xff50 labeltype ENGRAVED_LABEL - } - } - Fl_Choice record_mode_menu { - label {&Record Mode} - callback {if ( ! transport.recording ) - config.record_mode = (record_mode_e)o->value(); -else - o->value( config.record_mode );} open - xywh {170 47 100 25} box DOWN_BOX down_box BORDER_BOX color 37 labelsize 9 align 1 - } { - MenuItem {} { - label Merge - xywh {15 15 40 25} - } - MenuItem {} { - label Overwrite - xywh {25 25 40 25} - } - MenuItem {} { - label Layer - xywh {35 35 40 25} - } - MenuItem {} { - label New - xywh {45 45 40 25} - } - } - Fl_Choice playback_mode_menu { - label {Playback &Mode} open - xywh {279 47 100 25} box DOWN_BOX down_box BORDER_BOX color 37 labelsize 9 align 1 - } { - MenuItem {} { - label Pattern - callback {song.play_mode = PATTERN;} - xywh {5 5 40 25} - } - MenuItem {} { - label Sequence - callback {song.play_mode = SEQUENCE;} - xywh {15 15 40 25} - } - MenuItem {} { - label Trigger - callback {song.play_mode = TRIGGER;} - xywh {25 25 40 25} - } - MenuItem {} { - label Queue - callback {song.play_mode = QUEUE;} - xywh {0 0 40 24} + xywh {110 29 43 38} shortcut 0xff50 labeltype ENGRAVED_LABEL } } } Fl_Tabs tabs { - callback {((Fl_Group*)o->value())->child( 0 )->take_focus(); - -if ( o->value() == pattern_tab ) - pattern_canvas_widget->handle_pan(); -else if ( o->value() == phrase_tab ) - phrase_canvas_widget->handle_pan(); - -if ( o->value() != pattern_tab ) -{ - edit_menu->deactivate(); -} -else -{ - edit_menu->activate(); -} - -menu_bar->redraw();} open - xywh {0 79 865 711} box BORDER_BOX color 47 labeltype SHADOW_LABEL labelsize 19 when 1 resizable + callback {((Fl_Group*)o->value())->child( 0 )->take_focus();} open + xywh {0 91 865 694} box FLAT_BOX color 47 labeltype SHADOW_LABEL labelsize 19 when 1 resizable code0 {canvas_background_color = fl_rgb_color( 18, 18, 18 );} } { - Fl_Group sequence_tab { - label Sequence open - xywh {0 102 865 674} box FLAT_BOX color 37 hide resizable + Fl_Group sequence_tab {open + xywh {0 91 865 678} box FLAT_BOX color 37 labeltype NO_LABEL hide resizable code0 {update_sequence_widgets();} } { Fl_Group {} {open - xywh {10 125 233 502} labelsize 12 + xywh {10 118 233 502} labelsize 12 } { Fl_Browser playlist_browser { label Playlist - xywh {10 125 233 435} type Hold box EMBOSSED_BOX color 39 selection_color 30 labelcolor 55 align 1 when 4 textsize 18 textcolor 95 resizable + xywh {10 118 233 435} type Hold box EMBOSSED_BOX color 39 selection_color 30 labelcolor 55 align 1 when 4 textsize 18 textcolor 95 resizable code0 {static int widths[] = { 40, 30, 0 };} code1 {o->column_widths( widths ); o->column_char( '\\t' );} code2 {o->value( 1 );} @@ -519,7 +620,7 @@ if ( val > 1 ) if ( ! playlist_browser->value() ) playlist_browser->value( playlist_browser->size() ); }} - xywh {14 566 73 25} shortcut 0xffff color 88 labelcolor 23 + xywh {14 559 73 25} shortcut 0xffff color 88 labelcolor 23 } Fl_Button sequence_phrase_up_button { label Up @@ -529,7 +630,7 @@ if ( val > 1 ) playlist_browser->value( playlist_browser->value() - 1 ); update_sequence_widgets(); }} - xywh {97 566 65 25} shortcut 0xffbf + xywh {97 559 65 25} shortcut 0xffbf } Fl_Button sequence_phrase_down_button { label Down @@ -539,7 +640,7 @@ if ( val > 1 ) playlist_browser->value( playlist_browser->value() + 1 ); update_sequence_widgets(); }} - xywh {169 566 74 25} shortcut 0xffc0 + xywh {169 559 74 25} shortcut 0xffc0 } Fl_Menu_Button sequence_phrase_choice { label {Insert Phrase} @@ -553,13 +654,13 @@ if ( val ) playlist_browser->value( playlist_browser->value() + 1 ); else playlist_browser->value( playlist_browser->size() );} open - xywh {11 597 232 30} color 63 + xywh {11 590 232 30} color 63 } {} } Fl_Input sequence_name_field { label {name:} callback {playlist->name( o->value() );} - xywh {91 740 158 26} color 36 align 20 when 1 textcolor 32 + xywh {91 733 158 26} color 36 align 20 when 1 textcolor 32 } Fl_Light_Button detach_button { label Detach @@ -579,18 +680,19 @@ else sequence_tab->resize( pattern_tab->x(), pattern_tab->y(), pattern_tab->w(), pattern_tab->h() ); tabs->do_callback(); }} - xywh {7 740 78 26} + xywh {7 733 78 26} } Fl_Text_Editor sequence_notes_edit { label {Notes:} callback {playlist->notes( o->buffer()->text() );} - xywh {254 691 606 73} selection_color 48 labelsize 12 align 5 textcolor 94 + xywh {254 684 606 73} selection_color 48 labelsize 12 align 5 textcolor 94 code0 {o->buffer( sequence_notes_buffer = new Fl_Text_Buffer );} } Fl_Box triggers_widget { label Patterns - xywh {253 125 607 549} color 48 align 1 resizable + xywh {253 118 607 549} box FLAT_BOX color 48 align 1 resizable code0 {o->color( FL_BACKGROUND_COLOR );} + code1 {o->rows( 32 );} class Triggers } Fl_Group progress_group { @@ -604,141 +706,134 @@ if ( p ) if ( playlist->length() ) sequence_progress->value( playlist->index() / (double)playlist->length() );} open - xywh {10 656 233 66} + xywh {10 649 233 66} } { Fl_Slider phrase_progress { label Phrase - xywh {10 656 233 24} type Horizontal labelsize 12 align 1 + xywh {10 649 233 24} type Horizontal labelsize 12 align 1 } Fl_Slider sequence_progress { label Sequence callback {transport.locate( (tick_t)((double)playlist->length() * o->value()) );} - xywh {10 698 233 24} type Horizontal labelsize 12 align 1 + xywh {10 691 233 24} type Horizontal labelsize 12 align 1 } } } - Fl_Group phrase_tab { - label Phrase open - xywh {0 102 865 674} box FLAT_BOX color 47 hide + Fl_Group phrase_tab {open + xywh {0 91 865 678} box FLAT_BOX color 47 labeltype NO_LABEL hide code0 {update_phrase_widgets();} } { Fl_Box phrase_canvas_widget { label Phrase - xywh {1 103 863 587} box FLAT_BOX color 37 labelsize 100 align 16 resizable - code0 {o->set_canvas( phrase_c );} - code1 {phrase_c->signal_pan.connect( sigc::mem_fun( phrase_canvas_widget, &O_Canvas::handle_pan ) );} - class O_Canvas + xywh {1 91 863 592} box FLAT_BOX color 37 labelsize 100 align 16 resizable + class Canvas } Fl_Group {} {open - xywh {5 697 856 77} box FLAT_BOX color 47 + xywh {5 690 856 77} box FLAT_BOX color 47 } { Fl_Input phrase_name_field { label {name:} - callback {phrase_c->grid()->name( strdup( o->value() ) ); + callback {phrase_canvas_widget->grid()->name( strdup( o->value() ) ); // if the name changed.. update_sequence_widgets();} - xywh {5 704 155 24} box ROUNDED_BOX color 49 labelfont 2 labelcolor 55 align 20 textcolor 32 + xywh {5 697 155 24} box ROUNDED_BOX color 49 labelfont 2 labelcolor 55 align 20 textfont 2 code0 {o->up_box( FL_ROUNDED_BOX );} class Fl_Sometimes_Input } - Fl_Spinner phrase_number_spinner { - label {Phrase:} - callback {phrase *p = ((phrase *)phrase_c->grid())->by_number( o->value() ); - -if ( p ) - phrase_c->grid( p ); - -o->maximum( phrase::phrases() );} - xywh {165 704 55 24} color 36 labelsize 9 align 1 when 1 - } Fl_Light_Button phrase_mute_button { label Mute - xywh {5 740 93 23} color 37 hide + xywh {5 733 93 23} color 37 hide } Fl_Light_Button phrase_solo_button { label Solo - xywh {111 740 87 23} color 37 hide + xywh {111 733 87 23} color 37 hide } Fl_Text_Editor phrase_notes_edit { label {Notes:} - callback {phrase_c->grid()->notes( o->buffer()->text() );} - xywh {235 709 620 58} selection_color 48 labelsize 12 textcolor 94 resizable + callback {phrase_canvas_widget->grid()->notes( o->buffer()->text() );} + xywh {170 702 685 58} selection_color 48 labelsize 12 textcolor 94 resizable code0 {o->buffer( phrase_notes_buffer = new Fl_Text_Buffer );} } + Fl_Value_Slider phrase_number_spinner { + label Phrase + callback {phrase *p = ((phrase *)phrase_canvas_widget->grid())->by_number( o->value() ); + +if ( p ) + phrase_canvas_widget->grid( p ); + +o->maximum( phrase::phrases() );} + xywh {5 737 155 24} type Horizontal labelsize 10 align 1 minimum 1 maximum 128 step 1 value 1 textsize 14 + } } } - Fl_Group pattern_tab { - label Pattern open - xywh {0 102 865 688} box FLAT_BOX color 47 + Fl_Group pattern_tab {open + xywh {0 91 865 694} box FLAT_BOX color 47 labeltype NO_LABEL code0 {update_pattern_widgets();} } { Fl_Box pattern_canvas_widget { label Pattern - xywh {1 103 863 587} box FLAT_BOX color 37 labelsize 100 align 16 resizable - code0 {\#include "draw.H"} - code1 {o->set_canvas( pattern_c );} - code2 {\#include "input.H"} - code3 {pattern_c->signal_pan.connect( sigc::mem_fun( pattern_canvas_widget, &O_Canvas::handle_pan ) );} - class O_Canvas + xywh {0 91 865 637} box FLAT_BOX color 37 labelsize 100 align 16 resizable + class Canvas } - Fl_Group {} {open - xywh {0 694 862 96} box FLAT_BOX color 47 + Fl_Group pattern_settings_group {open + xywh {0 731 865 54} box FLAT_BOX color 47 } { Fl_Input pattern_name_field { label {name:} - callback {pattern_c->grid()->name( strdup( o->value() ) );} - xywh {5 704 155 24} box ROUNDED_BOX color 49 align 20 when 1 textfont 2 textcolor 55 + callback {pattern_canvas_widget->grid()->name( strdup( o->value() ) );} + xywh {5 734 185 21} box ROUNDED_BOX color 49 labelsize 12 align 20 when 8 textfont 2 textsize 12 textcolor 55 code0 {o->up_box( FL_ROUNDED_BOX );} class Fl_Sometimes_Input } Fl_Light_Button pattern_mute_button { label Mute - callback {Grid *g = pattern_c->grid(); + callback {Grid *g = pattern_canvas_widget->grid(); g->mode( g->mode() == MUTE ? PLAY : MUTE ); o->value( g->mode() == MUTE ); pattern_solo_button->value( 0 );} - xywh {167 704 60 25} type Normal color 37 + xywh {195 734 58 19} type Normal shortcut 0x6d color 37 labelsize 12 } Fl_Light_Button pattern_solo_button { label Solo - callback {Grid *g = pattern_c->grid(); + callback {Grid *g = pattern_canvas_widget->grid(); g->mode( g->mode() == SOLO ? PLAY : SOLO ); o->value( g->mode() == SOLO ); pattern_mute_button->value( 0 );} - xywh {167 744 60 25} type Normal color 37 + xywh {195 758 58 19} type Normal shortcut 0x73 color 37 labelsize 12 } Fl_Text_Editor pattern_notes_edit { label {Notes:} - callback {pattern_c->grid()->notes( o->buffer()->text() );} - xywh {235 710 222 58} selection_color 48 labelsize 12 textcolor 94 resizable + callback {pattern_canvas_widget->grid()->notes( o->buffer()->text() );} + xywh {310 734 240 46} color 40 selection_color 48 labeltype NO_LABEL labelsize 10 textsize 11 textcolor 63 resizable code0 {o->buffer( pattern_notes_buffer = new Fl_Text_Buffer );} + code1 {o->wrap_mode( Fl_Text_Editor::WRAP_AT_BOUNDS, 0 );} } - Fl_Group pattern_settings_group {open - xywh {458 694 400 78} + Fl_Group {} {open + xywh {555 731 310 49} } { Fl_Spinner pattern_channel_spinner { label Channel - callback {((pattern *)pattern_c->grid())->channel( o->value() - 1 );} - xywh {815 700 40 24} color 36 when 1 + callback {((pattern *)pattern_canvas_widget->grid())->channel( o->value() - 1 );} + xywh {820 735 40 19} color 36 labelsize 10 when 1 textsize 12 code0 {\#include "../pattern.H"} code1 {o->maximum( 16 );} } Fl_Spinner pattern_port_spinner { label Port - callback {((pattern *)pattern_c->grid())->port( o->value() - 1 );} - xywh {815 734 40 24} color 36 when 1 + callback {((pattern *)pattern_canvas_widget->grid())->port( o->value() - 1 );} + xywh {820 758 40 19} color 36 labelsize 10 when 1 textsize 12 code0 {o->maximum( 16 );} } Fl_Output mapping_text { label Mapping - xywh {464 734 145 24} align 20 + xywh {555 758 105 19} labelsize 10 align 20 textsize 11 } Fl_Menu_Button mapping_menu { label {@>} @@ -750,22 +845,22 @@ mapping_menu->item_pathname(picked, sizeof(picked)-1 ); if ( 0 == strncmp( picked, "Instrument", strlen( "Instrument" ) ) ) { - ((pattern*)pattern_c->grid())->mapping.open( Mapping::INSTRUMENT, o->text() ); + ((pattern*)pattern_canvas_widget->grid())->mapping.open( Mapping::INSTRUMENT, o->text() ); - pattern_c->changed_mapping(); + pattern_canvas_widget->changed_mapping(); pattern_key_combo->deactivate(); } else if ( 0 == strncmp( picked, "Scale", strlen( "Scale" ) ) ) { - ((pattern*)pattern_c->grid())->mapping.open( Mapping::SCALE, o->text() ); + ((pattern*)pattern_canvas_widget->grid())->mapping.open( Mapping::SCALE, o->text() ); - pattern_c->changed_mapping(); + pattern_canvas_widget->changed_mapping(); pattern_key_combo->activate(); }} open - xywh {609 734 30 24} labeltype NO_LABEL + xywh {660 758 30 19} labeltype NO_LABEL labelsize 10 textsize 12 code0 {update_mapping_menu();} } { Submenu mapping_scale_menu { @@ -779,176 +874,192 @@ if ( 0 == strncmp( picked, "Scale", strlen( "Scale" ) ) ) } Fl_Choice pattern_key_combo { label {&Key} - callback {((pattern*)pattern_c->grid())->mapping.key( o->value() ); + callback {((pattern*)pattern_canvas_widget->grid())->mapping.key( o->value() ); -pattern_c->changed_mapping();} - xywh {674 734 75 24} down_box BORDER_BOX when 1 +pattern_canvas_widget->changed_mapping();} open + xywh {720 758 75 19} down_box BORDER_BOX labelsize 10 when 1 textsize 11 } { MenuItem {} { label C - xywh {30 30 40 25} + xywh {30 30 40 25} labelsize 11 } MenuItem {} { label {C\#/Db} - xywh {40 40 40 25} + xywh {40 40 40 25} labelsize 11 } MenuItem {} { label D - xywh {50 50 40 25} + xywh {50 50 40 25} labelsize 11 } MenuItem {} { label {D\#/Eb} - xywh {60 60 40 25} + xywh {60 60 40 25} labelsize 11 } MenuItem {} { label E - xywh {70 70 40 25} + xywh {70 70 40 25} labelsize 11 } MenuItem {} { label F - xywh {80 80 40 25} + xywh {80 80 40 25} labelsize 11 } MenuItem {} { label {F\#/Gb} - xywh {90 90 40 25} + xywh {90 90 40 25} labelsize 11 } MenuItem {} { label G - xywh {100 100 40 25} + xywh {100 100 40 25} labelsize 11 } MenuItem {} { label {G\#} - xywh {110 110 40 25} + xywh {110 110 40 25} labelsize 11 } MenuItem {} { label A - xywh {0 0 40 25} + xywh {0 0 40 25} labelsize 11 } MenuItem {} { label {A\#/Bb} - xywh {10 10 40 25} + xywh {10 10 40 25} labelsize 11 } MenuItem {} { label B - xywh {20 20 40 25} + xywh {20 20 40 25} labelsize 11 } } Fl_Choice pattern_note_combo { label {&Note 1/} - callback {((pattern*)pattern_c->grid())->note( atoi( o->menu()[ o->value() ].text ));} - xywh {704 700 45 24} down_box BORDER_BOX when 1 + callback {((pattern*)pattern_canvas_widget->grid())->note( atoi( o->menu()[ o->value() ].text ));} open + xywh {730 735 45 19} down_box BORDER_BOX labelsize 10 when 1 textsize 12 } { MenuItem {} { label 1 - xywh {0 0 40 25} + xywh {0 0 40 25} labelsize 11 } MenuItem {} { label 2 - xywh {10 10 40 25} + xywh {10 10 40 25} labelsize 11 } MenuItem {} { label 4 - xywh {20 20 40 25} + xywh {20 20 40 25} labelsize 11 } MenuItem {} { label 8 - xywh {30 30 40 25} + xywh {30 30 40 25} labelsize 11 } MenuItem {} { label 16 - xywh {40 40 40 25} + xywh {40 40 40 25} labelsize 11 } MenuItem {} { label 32 - xywh {50 50 40 25} + xywh {50 50 40 25} labelsize 11 } MenuItem {} { label 64 - xywh {60 60 40 25} divider + xywh {60 60 40 25} labelsize 11 divider } MenuItem {} { label 3 - xywh {60 60 40 25} + xywh {60 60 40 25} labelsize 11 } MenuItem {} { label 6 - xywh {70 70 40 25} + xywh {70 70 40 25} labelsize 11 } MenuItem {} { label 12 - xywh {80 80 40 25} + xywh {80 80 40 25} labelsize 11 } MenuItem {} { label 24 - xywh {90 90 40 25} + xywh {90 90 40 25} labelsize 11 } } Fl_Choice pattern_res_combo { label {&Resolution 1/} - callback {pattern_c->grid()->resolution( atoi( o->menu()[ o->value() ].text ));} - xywh {584 700 55 24} down_box BORDER_BOX when 1 + callback {pattern_canvas_widget->grid()->resolution( atoi( o->menu()[ o->value() ].text ));} open + xywh {625 735 55 19} down_box BORDER_BOX labelsize 10 when 1 textsize 12 } { + MenuItem {} { + label 1 + xywh {40 40 40 25} labelsize 11 + } + MenuItem {} { + label 2 + xywh {50 50 40 25} labelsize 11 + } MenuItem {} { label 4 - xywh {30 30 40 25} + xywh {30 30 40 25} labelsize 11 } MenuItem {} { label 8 - xywh {40 40 40 25} + xywh {40 40 40 25} labelsize 11 } MenuItem {} { label 16 - xywh {50 50 40 25} + xywh {50 50 40 25} labelsize 11 } MenuItem {} { label 32 - xywh {60 60 40 25} + xywh {60 60 40 25} labelsize 11 } MenuItem {} { label 64 - xywh {80 80 40 25} + xywh {80 80 40 25} labelsize 11 } MenuItem {} { label 128 - xywh {90 90 40 25} divider + xywh {90 90 40 25} labelsize 11 divider } MenuItem {} { label 3 - xywh {70 70 40 25} + xywh {70 70 40 25} labelsize 11 } MenuItem {} { label 6 - xywh {80 80 40 25} + xywh {80 80 40 25} labelsize 11 } MenuItem {} { label 12 - xywh {90 90 40 25} + xywh {90 90 40 25} labelsize 11 } MenuItem {} { label 24 - xywh {100 100 40 25} + xywh {100 100 40 25} labelsize 11 } } } Fl_Value_Slider pattern_number_spinner { label Pattern - callback {pattern *p = ((pattern *)pattern_c->grid())->by_number( o->value() ); + callback {pattern *p = ((pattern *)pattern_canvas_widget->grid())->by_number( o->value() ); if ( p ) - pattern_c->grid( p ); + pattern_canvas_widget->grid( p ); -o->maximum( pattern::patterns() );} selected - xywh {5 744 155 24} type Horizontal labelsize 10 align 1 minimum 1 maximum 128 step 1 value 1 textsize 14 +update_pattern_widgets(); + +o->maximum( pattern::patterns() ); + +pattern_settings_group->redraw();} + xywh {45 759 140 18} type Horizontal labelsize 10 align 4 minimum 1 maximum 128 step 1 value 1 + } + Fl_Button {} { + label Select selected + tooltip {Enable selection mode (you can also just hold down shift and drag the mouse)} xywh {260 735 45 45} type Toggle selection_color 5 labelsize 10 } } } } Fl_Group {} {open - xywh {0 776 865 31} + xywh {0 784 865 23} } { Fl_Box status { label status - xywh {1 776 782 31} box UP_BOX align 84 + xywh {1 784 782 23} box UP_BOX align 84 code0 {o->label( NULL );} } Fl_Box transport_state { @@ -966,16 +1077,16 @@ if ( s != o->label() ) if ( ! strcmp( s, "INVALID" ) ) o->color( fl_darker( FL_RED ) ); else - o->color( fl_darker( FL_GREEN ) ); + o->color( FL_BACKGROUND_COLOR ); }} - xywh {783 776 82 31} box THIN_UP_BOX align 64 + xywh {783 784 82 23} box THIN_UP_BOX align 64 } } Fl_Group {} {open - xywh {0 0 865 31} + xywh {0 0 865 24} } { Fl_Menu_Bar menu_bar {open - xywh {0 0 865 30} color 47 + xywh {0 0 865 24} color 47 resizable } { Submenu {} { label {&File} open @@ -1112,7 +1223,7 @@ if ( ! fc->value() ) return; if ( tabs->value() == pattern_tab ) - ((pattern*)pattern_c->grid())->save( fc->value() );} + ((pattern*)pattern_canvas_widget->grid())->save( fc->value() );} xywh {0 0 40 25} code0 {\#include } } @@ -1122,15 +1233,10 @@ if ( tabs->value() == pattern_tab ) xywh {0 0 40 25} shortcut 0x40071 color 37 } } - Submenu edit_menu { - label {&Edit} open + Submenu {} { + label {&Settings} open xywh {0 0 74 25} color 37 } { - MenuItem {} { - label {&Events} - callback {event_editor( pattern_c->grid() );} - xywh {0 0 40 25} - } MenuItem {} { label {&Randomization Settings} callback {randomization_dialog->show();} @@ -1155,7 +1261,7 @@ else label {&Compacted} callback {int val = o->menu()[ o->value() ].value(); -pattern_c->row_compact( val ? Canvas::ON : Canvas::OFF ); +pattern_canvas_widget->row_compact( val ? Canvas::ON : Canvas::OFF ); pattern_canvas_widget->redraw();} xywh {10 10 40 25} type Toggle value 1 @@ -1177,11 +1283,6 @@ config.follow_playhead = val ? true : false;} label {&Help} open xywh {100 0 74 25} color 37 } { - MenuItem {} { - label {&Keys} - callback {show_help_dialog( "KEYS" );} - xywh {0 0 40 25} - } MenuItem {} { label {&Manual} callback {show_help_dialog( "MANUAL" );} @@ -1205,10 +1306,118 @@ ab.run();} code0 {\#include "../non.H"} } } + Submenu edit_menu { + label {&Edit} open + xywh {0 0 68 18} + } { + MenuItem {} { + label {Add New} + callback {Grid *g = pattern_canvas_widget->grid()->create(); + +pattern_canvas_widget->grid( g ); + +update_pattern_widgets(); +update_sequence_widgets();} + xywh {0 0 34 18} shortcut 0x61 + } + MenuItem {} { + label Previous + callback {pattern_number_spinner->value( max( 0, (int)pattern_number_spinner->value() - 1 ) ); +pattern_number_spinner->do_callback();} + xywh {10 10 34 18} shortcut 0x5b + } + MenuItem {} { + label Next + callback {pattern_number_spinner->value( min( 127, (int)pattern_number_spinner->value() + 1 )); +pattern_number_spinner->do_callback();} + xywh {20 20 34 18} shortcut 0x5d + } + MenuItem {} { + label Duplicate + callback {Canvas *w = pattern_canvas_widget; + +w->grid( w->grid()->clone() ); + +// number of phrases may have changed. +ui->update_sequence_widgets();} + xywh {30 30 34 18} shortcut 0x64 + } + MenuItem {} { + label {Duplicate Range} + callback {Canvas *w = pattern_canvas_widget; + +w->duplicate_range(); + +// number of phrases may have changed. +ui->update_sequence_widgets();} + xywh {40 40 34 18} shortcut 0x10064 + } + MenuItem {} { + label {Delete Selected} + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->delete_selected();} + xywh {50 50 34 18} shortcut 0xffff + } + MenuItem {} { + label Clear + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {60 60 34 18} shortcut 0x1ffff + } + MenuItem {} { + label {Edit Events} + callback {event_editor( pattern_canvas_widget->grid() );} + xywh {10 10 40 25} + } + MenuItem {} { + label {Select All} + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {70 70 34 18} shortcut 0x40061 + } + MenuItem {} { + label {Select None} + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {80 80 34 18} shortcut 0x50061 + } + MenuItem {} { + label {Invert Selection} + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {90 90 34 18} shortcut 0x50069 + } + MenuItem {} { + label Copy + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {100 100 34 18} shortcut 0x40063 + } + MenuItem {} { + label Cut + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {110 110 34 18} shortcut 0x40078 + } + MenuItem {} { + label Paste + callback {Canvas *w = pattern_canvas_widget; + +w->grid()->clear();} + xywh {120 120 34 18} shortcut 0x40076 + } + } } Fl_Button sm_indicator { label SM - xywh {825 8 35 15} box ROUNDED_BOX down_box ROUNDED_BOX color 46 selection_color 93 labelfont 3 labelcolor 39 deactivate + xywh {825 5 35 15} box ROUNDED_BOX down_box ROUNDED_BOX color 46 selection_color 93 labelfont 3 labelcolor 39 deactivate } } } @@ -1267,8 +1476,14 @@ detach_button->value( 0 );} open } { code {if ( ! pattern_settings_group ) return; - -pattern *g = (pattern *)pattern_c->grid(); + +if ( !pattern_canvas_widget) + return; + +pattern *g = (pattern *)pattern_canvas_widget->grid(); + +if ( !g ) + return; pattern_number_spinner->value( g->number() ); pattern_name_field->value( g->name() ); @@ -1297,16 +1512,20 @@ if ( g->notes() ) else pattern_notes_buffer->text( strdup( "" ) );} {} } - Function {update_phrase_widgets()} {} { - code {phrase *g = (phrase *)phrase_c->grid(); + Function {update_phrase_widgets()} {open + } { + code {if ( ! phrase_canvas_widget ) + return; + +phrase *g = (phrase *)phrase_canvas_widget->grid(); if ( ! g ) return; g->viewport.y = 0; g->viewport.h = pattern::patterns(); -phrase_c->resize_grid(); -phrase_c->changed_mapping(); +phrase_canvas_widget->resize_grid(); +phrase_canvas_widget->changed_mapping(); phrase_number_spinner->value( g->number() ); phrase_name_field->value( g->name() ); phrase_solo_button->value( g->mode() == SOLO ); @@ -1396,10 +1615,10 @@ free( sa );} {} } Function {update_canvas_widgets()} {return_type {static void} } { - code {if ( pattern_c->grid() ) + code {if ( ui->pattern_canvas_widget->grid() ) ui->update_pattern_widgets(); -if ( phrase_c->grid() ) + if ( ui->phrase_canvas_widget->grid() ) ui->update_phrase_widgets();} {} } Function {find_numeric_menu_item( const Fl_Menu_Item *menu, int n )} {return_type {static int} @@ -1478,7 +1697,7 @@ if ( p ) pattern_canvas_widget->take_focus(); - pattern_c->grid( p ); + pattern_canvas_widget->grid( p ); // update_pattern_widgets(); }} {} @@ -1493,216 +1712,6 @@ ie.run();} {} } } -Function {shortcut_handler( int e )} {return_type int -} { - code {if ( e != FL_SHORTCUT ) - return 0; - - -// this is for mainwindow shortcuts only, ignore all other windows. -if ( Fl::first_window() != ui->main_window ) - return 0; - -int processed = 0; - -// shortcuts that don't fit anywhere else (widgets that don't take shortcuts, etc.) - -\#define KEY(key) ((Fl::test_shortcut( (key) ))) - - processed = 1; - if KEY( FL_ALT + 's' ) - { - ui->tabs->value( ui->sequence_tab ); - ui->tabs->do_callback(); - } - else - if KEY( FL_ALT + 'a' ) - { - ui->tabs->value( ui->phrase_tab ); - ui->tabs->do_callback(); - } - else - if KEY( FL_ALT + 'p' ) - { - ui->tabs->value( ui->pattern_tab ); - ui->tabs->do_callback(); - } - else - if KEY( FL_ALT + 'c' ) - ui->pattern_channel_spinner->take_focus(); - else - if KEY( FL_ALT + 'o' ) - ui->pattern_port_spinner->take_focus(); - else - if KEY( FL_ALT + 'i' ) - ui->mapping_menu->take_focus(); - else - processed = 0; - -return processed;} {} -} - -class O_Canvas {open : {public Fl_Widget} -} { - decl {Canvas *_c;} {private local - } - decl {bool _border_drawn;} {private local - } - Function {O_Canvas( int X, int Y, int W, int H, const char*L=0) : Fl_Widget(X,Y,W,H,L)} {open - } { - code {_c = NULL; -_border_drawn = false; - box(FL_FLAT_BOX);} {} - } - Function {handle( int m )} {open return_type int - } { - code {// Accept focus if offered. -if ( m == FL_FOCUS || m == FL_UNFOCUS ) -{ - _border_drawn = false; - redraw_playhead(); - return 1; -} - -// Hack in click-to-focus -if ( m == FL_PUSH ) - if ( Fl::event_inside( this ) ) - take_focus(); - - -if ( Fl_Widget::handle( m ) ) - return 1; - -// Ignore events unless we have the focus. -if ( this != Fl::focus() ) - return 0; - -// MESSAGE( "got event %i for canvas %p", m, _c ); - -int p = 0; - -if ( _c ) - { - p = canvas_input_callback( this, _c, m ); - } - -return p;} {} - } - Function {resize( int x, int y, int w, int h )} {open - } { - code {Fl_Widget::resize( x, y, w, h ); - -if ( _c ) -{ - DMESSAGE( "Resizing canvas." ); - _c->resize( x + 1, y + 1, w - 1, h - 1 ); -} - - -// Fl_Window::resize( x, y, w, h );} {} - } - Function {draw()} {open return_type void - } { - code {draw_border(); - -//if ( ! takesevents() ) -// return; - -if ( _c ) -{ -/* - if ( damage() & FL_DAMAGE_ALL ) printf( " damage_all" ); - if ( damage() & FL_DAMAGE_SCROLL ) printf( " damage_scroll" ); - if ( damage() & FL_DAMAGE_USER1 ) printf( " damage_user1" ); - if ( damage() & FL_DAMAGE_USER2 ) printf( " damage_user2" ); - if ( damage() & FL_DAMAGE_EXPOSE ) printf( " damage_expose" ); - printf("\\n"); -*/ - - _c->draw(); -/* - if ( damage() & FL_DAMAGE_ALL ) - { - draw_box( FL_FLAT_BOX, x(), y(), w(), h(), canvas_background_color ); - _border_drawn = false; - draw_border(); - _c->redraw(); - _c->draw_playhead(); - } - else - { - if ( damage() & FL_DAMAGE_SCROLL ) - { - // optimized draw - _c->draw(); - } - if ( damage() & FL_DAMAGE_USER1 ) - { - // playhead - _c->draw_playhead(); - } - } -*/ -} -else -{ - WARNING( "No canvas set for widget." ); -}} {} - } - Function {set_canvas( Canvas *c )} {open - } { - code {_c = c; - -_c->resize( x(), y(), w(), h() ); - -_c->signal_draw.connect( sigc::mem_fun( this, &O_Canvas::redraw_notes ) ); -_c->signal_resize.connect( sigc::mem_fun( this, &O_Canvas::clear ) ); - -_c->signal_settings_change.connect( sigc::ptr_fun( &UI::update_canvas_widgets ) ); -_c->signal_settings_change.connect( sigc::mem_fun( song, &song_settings::set_dirty ) );} {} - } - Function {click_to_focus()} {open return_type bool - } { - code {return true;} {} - } - Function {clear( void )} {open return_type void - } { - code {redraw();} {} - } - Function {redraw_notes( void )} {open return_type void - } { - code {damage( FL_DAMAGE_SCROLL ); - -// this might be called from within draw(), in which case the above does nothing.} {} - } - Function {redraw_playhead( void )} {open return_type void - } { - code {if ( _c && _c->playhead_moved() ) - { - damage( FL_DAMAGE_USER1 ); - }} {} - } - Function {draw_border()} {open return_type void - } { - code {if ( _border_drawn ) - return; - -if ( this != Fl::focus() ) - fl_color( FL_RED ); -else - fl_color( FL_BLACK ); - -fl_line_style( FL_DASH ); -fl_rect( x(), y(), w(), h() ); -fl_line_style( FL_SOLID ); - -_border_drawn = true;} {} - } - Function {handle_pan( void )} {open return_type void - } { - } -} - class Instrument_Editor {} { Function {Instrument_Editor()} {open return_type void } { @@ -1765,11 +1774,15 @@ while ( window->shown() ) } } -class Trigger {open : {public Fl_Dial} +class Trigger {open : {public Fl_Progress} } { - Function {Trigger( int X, int Y, int W, int H, const char *L ) : Fl_Dial( X, Y, W, H, L )} {open + Function {Trigger( int X, int Y, int W, int H, const char *L =0) : Fl_Progress( X, Y, W, H, L )} {open } { - code {} {} + code {minimum( 0 ); +maximum( 1 ); +//angles( 0, 360 ); +// type( Fl_Dial::ARC_DIAL ); +// type(FL_VERTICAL);} {} } Function {handle( int m )} {open return_type int } { @@ -1846,65 +1859,34 @@ return r;} {} } widget_class Triggers {open - xywh {335 80 1278 1003} type Double hide resizable - code0 {populate();} - code1 {\#include } + xywh {390 620 335 390} type Double hide + code0 {\#include } + code1 {populate();} class Fl_Group } { - Fl_Pack rows {open - xywh {25 25 15 15} - } {} Function {populate( void )} {open private return_type void } { code {_timer = 0; -int bw = (w() / 16); -int bh = h() / (128/ 16); - -begin(); - -for ( int n = 0; n < 128 ; n += 16 ) -{ - Fl_Pack *p = new Fl_Pack( 0, 0, 25, 25 ); - - p->type( Fl_Pack::HORIZONTAL ); - - for ( int i = 0; i < 16; i++ ) - { - - Trigger *b = new Trigger( 0, 0, bw, 50, "Num" ); - + for ( int i = 0; i < 128; i++ ) + { + Trigger *b = new Trigger( 1,1,1,1 ); + char pat[4]; - sprintf( pat, "%d", n + i + 1 ); - + sprintf( pat, "%d",i +1); b->label( strdup( pat ) ); - b->minimum( 0 ); - b->maximum( 1 ); - b->angles( 0, 360 ); - b->type( FL_FILL_DIAL ); - b->color2( FL_GRAY ); - b->box( FL_ROUNDED_BOX ); + b->labelsize( 8 ); + b->color2( FL_GRAY ); + b->box( FL_FLAT_BOX ); // b->down_box( FL_ROUNDED_BOX ); b->selection_color( FL_GREEN ); b->color( FL_BLACK ); + b->color2( FL_GREEN ); b->align( FL_ALIGN_CENTER ); - - p->add( b ); - } - - p->end(); - - p->resize( 0, 0, w(), bh ); - rows->add( p ); -} - -end(); - -rows->resize( x(), y(), w(), h() ); - -redraw();} {} + add( b ); + }} {} } Function {update( void )} {open return_type void } { @@ -1918,18 +1900,18 @@ if ( ! takesevents() ) Fl_Color mode_color[3]; -// mode_color[PLAY] = fl_color_average( FL_GRAY, FL_GREEN, 0.5 ); - mode_color[PLAY] = FL_DARK2; + mode_color[PLAY] = fl_color_average( FL_GRAY, FL_GREEN, 0.5 ); +// mode_color[PLAY] = fl_color; mode_color[MUTE] = FL_LIGHT2; mode_color[SOLO] = fl_color_average( FL_GRAY, FL_RED, 0.5 ); -int i; -for ( i = 0; i < MAX_PATTERN; i++ ) +if ( ! _rows ) + return; + +for ( int i = 0; i < MAX_PATTERN; i++ ) { - - - Trigger *b = (Trigger*)(((Fl_Pack*)rows->child( i / 16 ))->child( i % 16 )); - + Trigger *b = (Trigger*)child( i ); + if ( i >= pattern::patterns() ) { b->color( FL_BLACK ); @@ -1961,28 +1943,32 @@ for ( i = 0; i < MAX_PATTERN; i++ ) } }} {} - } - Function {resize( int X, int Y, int W, int H )} {open return_type void - } { - code {for ( int i = rows->children(); i--; ) -{ - Fl_Pack *p = (Fl_Pack*) rows->child( i ); - - for ( int j = p->children(); j--; ) - { - int bw = W / p->children(); - p->child( j )->resize( 0, 0, bw, 25 );; - } - - p->resize( 0, 0, W, H / rows->children() ); - - p->redraw(); -} - -Fl_Group::resize( X, Y, W, H );} {} } decl {unsigned long _timer;} {private local } + decl {int _rows;} {private local + } + Function {rows(int v)} {open return_type void + } { + code {_rows = v; +redraw();} {} + } + Function {draw()} {open return_type void + } { + code {int _cols = 128 / _rows; + +int bw = w() / _cols; +int bh = h() / _rows; + +int t = 0; +for ( int i = 0; i < _rows; i++ ) + for ( int j = 0; j < _cols ; j++, t++ ) + child( t )->resize( x() + (bw * j), y() + (bh * i), bw, bh ); + +Fl_Group::draw();} {} + } + decl {\#include } {private local + } } class List_Chooser {open @@ -2027,3 +2013,39 @@ window->hide();} code {browser->add( item );} {} } } + +decl {float status_intensity;} {private local +} + +Function {fade_status(void*)} {open private return_type {static void} +} { + code {ui->status->labelcolor( fl_color_average( FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR, status_intensity ) ); + + status_intensity -= 0.01f; + + ui->status->redraw(); + + if ( status_intensity >= 0.01f ) + Fl::repeat_timeout( 1 / 15.0f, fade_status );} {} +} + +Function {gui_status( const char *fmt, ... )} {open C return_type void +} { + code {va_list args; + + static char pat[256]; + + if ( fmt ) + { + va_start( args, fmt ); + vsnprintf( pat, 256, fmt, args ); + va_end( args ); + } + + ui->status->label( pat ); + ui->status->redraw(); + + status_intensity = 1.0f; + Fl::remove_timeout( fade_status ); + Fl::add_timeout( 1 / 15.0f, fade_status );} {} +} diff --git a/sequencer/src/instrument.C b/sequencer/src/instrument.C index 7907ca4..d906b9d 100644 --- a/sequencer/src/instrument.C +++ b/sequencer/src/instrument.C @@ -33,8 +33,12 @@ #include + #include +#include +using namespace MIDI; + using std::list; using std::string; diff --git a/sequencer/src/instrument.H b/sequencer/src/instrument.H index b5d4079..350b644 100644 --- a/sequencer/src/instrument.H +++ b/sequencer/src/instrument.H @@ -22,7 +22,7 @@ #include using std::list; -#include "event.H" +#include struct i_map { char *name; @@ -55,7 +55,7 @@ public: void note_name ( int n, char *s ); /* inspection */ - bool translate ( midievent *e ) const; + bool translate ( MIDI::midievent *e ) const; const char * note_name ( int n ) const; int height ( void ) const; const char * name ( void ) const; diff --git a/sequencer/src/jack.C b/sequencer/src/jack.C index 14e214a..683b053 100644 --- a/sequencer/src/jack.C +++ b/sequencer/src/jack.C @@ -32,7 +32,10 @@ #include "transport.H" #include "pattern.H" #include "phrase.H" -#include "event_list.H" +#include +#include + +using namespace MIDI; #ifdef JACK_MIDI_PROTO_API /* correct for prototype version of API */ diff --git a/sequencer/src/jack.H b/sequencer/src/jack.H index 3fdb857..7d6ed99 100644 --- a/sequencer/src/jack.H +++ b/sequencer/src/jack.H @@ -1,18 +1,17 @@ #include - +#include #include "common.h" enum { CONTROL, PERFORMANCE }; -class midievent; -bool midi_input_event ( int port, midievent *e ); +bool midi_input_event ( int port, MIDI::midievent *e ); bool midi_is_active ( void ); -midievent * midi_input_event ( int port ); -void midi_output_event ( int port, const midievent *e ); -void midi_output_event ( int port, const midievent *e, tick_t duration ); +MIDI::midievent * midi_input_event ( int port ); +void midi_output_event ( int port, const MIDI::midievent *e ); +void midi_output_event ( int port, const MIDI::midievent *e, tick_t duration ); void midi_all_sound_off ( void ); const char * midi_init ( const char *name ); void midi_shutdown ( void ); -void midi_output_immediate_event ( int port, const midievent *e ); +void midi_output_immediate_event ( int port, const MIDI::midievent *e ); diff --git a/sequencer/src/main.C b/sequencer/src/main.C index 42a01c1..f7e8c91 100644 --- a/sequencer/src/main.C +++ b/sequencer/src/main.C @@ -36,8 +36,6 @@ const double NSM_CHECK_INTERVAL = 0.25f; -Canvas *pattern_c, *phrase_c, *trigger_c; - sequence *playlist; global_settings config; @@ -59,10 +57,6 @@ quit ( void ) delete ui; - delete pattern_c; - delete phrase_c; - delete trigger_c; - midi_all_sound_off(); // wait for it... @@ -81,13 +75,14 @@ clear_song ( void ) { // song.filename = NULL; - pattern_c->grid( NULL ); - phrase_c->grid( NULL ); + ui->pattern_canvas_widget->grid( NULL ); + ui->phrase_canvas_widget->grid( NULL ); playlist->reset(); playlist->insert( 0, 1 ); - pattern_c->grid( new pattern ); - phrase_c->grid( new phrase ); + + ui->pattern_canvas_widget->grid( new pattern ); + ui->phrase_canvas_widget->grid( new phrase ); song.dirty( false ); } @@ -125,11 +120,11 @@ load_song ( const char *name ) MESSAGE( "loading song \"%s\"", name ); - Grid *pattern_grid = pattern_c->grid(); - Grid *phrase_grid = phrase_c->grid(); + Grid *pattern_grid = ui->pattern_canvas_widget->grid(); + Grid *phrase_grid = ui->phrase_canvas_widget->grid(); - pattern_c->grid( NULL ); - phrase_c->grid( NULL ); + ui->pattern_canvas_widget->grid( NULL ); + ui->phrase_canvas_widget->grid( NULL ); if ( ! playlist->load( name ) ) { @@ -137,8 +132,8 @@ load_song ( const char *name ) goto failed; } - pattern_c->grid( pattern::pattern_by_number( 1 ) ); - phrase_c->grid( phrase::phrase_by_number( 1 ) ); + ui->pattern_canvas_widget->grid( pattern::pattern_by_number( 1 ) ); + ui->phrase_canvas_widget->grid( phrase::phrase_by_number( 1 ) ); song.filename = strdup( name ); @@ -148,8 +143,8 @@ load_song ( const char *name ) failed: - pattern_c->grid( pattern_grid ); - phrase_c->grid( phrase_grid ); + ui->pattern_canvas_widget->grid( pattern_grid ); + ui->phrase_canvas_widget->grid( phrase_grid ); return false; } @@ -237,26 +232,23 @@ main ( int argc, char **argv ) playlist = new sequence; - pattern_c = new Canvas(0,0,1,1); - phrase_c = new Canvas(0,0,1,1); - trigger_c = new Canvas(0,0,1,1); - nsm = new NSM_Client; song.filename = NULL; - clear_song(); - - pattern::signal_create_destroy.connect( mem_fun( phrase_c, &Canvas::v_zoom_fit ) ); - pattern::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) ); - phrase::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) ); - - // - song.dirty( false ); - - init_colors(); ui = new UI; + pattern::signal_create_destroy.connect( mem_fun( ui->phrase_canvas_widget, &Canvas::v_zoom_fit ) ); + pattern::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) ); + phrase::signal_create_destroy.connect( mem_fun( song, &song_settings::set_dirty ) ); + + song.dirty( false ); + + clear_song(); + +#ifdef HAVE_XPM + ui->main_window->icon((char *)p); +#endif ui->main_window->show( 0, 0 ); instance_name = strdup( APP_NAME ); diff --git a/sequencer/src/mapping.C b/sequencer/src/mapping.C index a59262a..47c9b8e 100644 --- a/sequencer/src/mapping.C +++ b/sequencer/src/mapping.C @@ -20,7 +20,9 @@ #include "mapping.H" #include "stdlib.h" #include "common.h" +#include +using namespace MIDI; /* Is C++'s dispatching useless or what? */ #define IS_INSTRUMENT ( _type == INSTRUMENT ) diff --git a/sequencer/src/mapping.H b/sequencer/src/mapping.H index 4723eaf..e00cece 100644 --- a/sequencer/src/mapping.H +++ b/sequencer/src/mapping.H @@ -21,6 +21,7 @@ #include "scale.H" #include "instrument.H" +#include /* C++'s inheritance system falls down dead for this application, so we have to do it backwards, using the base class (Mapping) as an interface @@ -64,7 +65,7 @@ public: void key ( int n ); /* inspection */ - bool translate ( midievent *e ) const; + bool translate ( MIDI::midievent *e ) const; const char * note_name ( int n ) const; int velocity ( int n ) const; int key ( void ) const; diff --git a/sequencer/src/midievent.C b/sequencer/src/midievent.C deleted file mode 100644 index e56861a..0000000 --- a/sequencer/src/midievent.C +++ /dev/null @@ -1,218 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -/* raw MIDI events + timestamps. Some support for SysEx */ - -#include "common.h" -#include "midievent.H" - -static const char *opcode_names[] = -{ - "Note Off", - "Note On", - "Aftertouch", - "Control Change", - "Program Change", - "Channel Pressure", - "Pitch Wheel" -}; - -midievent::midievent ( void ) -{ - _sysex = NULL; - _timestamp = 0; - _data.status = NOTE_OFF; - _data.msb = _data.lsb = 0; -} - -midievent::~midievent ( void ) -{ - if ( _sysex ) - delete _sysex; - - _sysex = NULL; -} - -int -midievent::pitch ( void ) const -{ - return ((_data.msb << 7) | _data.lsb) - 0x2000; -} - -void -midievent::pitch ( int n ) -{ - n += 0x2000; - - _data.lsb = n & 0x7F; - _data.msb = (n >> 7) & 0x7F; -} - -void -midievent::data ( byte_t D1, byte_t D2 ) -{ - _data.lsb = D1 & 0x7F; - _data.msb = D2 & 0x7F; -} - -void -midievent::data ( byte_t *D1, byte_t *D2 ) const -{ - *D1 = _data.lsb; - *D2 = _data.msb; -} - -void -midievent::raw ( byte_t *p, int l) const -{ - memcpy( p, &_data, l ); -} - -int -midievent::size ( void ) const -{ - return midievent::event_size( opcode() ); -} - -void -midievent::note_velocity ( int vel ) -{ - _data.msb = vel & 0x7F; -} - -void -midievent::note ( char note ) -{ - _data.lsb = note & 0x7F; -} - - -unsigned char -midievent::note ( void ) const -{ - return _data.lsb; -} - -unsigned char -midievent::note_velocity ( void ) const -{ - return _data.msb; -} - -bool -midievent::is_same_note ( midievent * e ) const -{ - return channel() == e->channel() && note() == e->note(); -} - -/** get name from opcode */ -const char * -midievent::name ( void ) const -{ - return opcode_names[ (opcode() >> 4) - 8 ]; -} - -/** get opcode from name */ -int -midievent::name ( const char *name ) const -{ - for ( unsigned int i = elementsof( opcode_names ); i--; ) - if ( ! strcmp( name, opcode_names[ i ] ) ) - return (i + 8) << 4; - - return -1; -} - -/** print event in hexadecimal */ -void -midievent::print ( void ) const -{ - printf( "[%06ld] %02X %02X %02X\n", - _timestamp, - _data.status, - _data.lsb, - _data.msb ); -} - -/** print event in english/decimal */ -void -midievent::pretty_print ( void ) const -{ - printf( - "[%06ld] %-15s c: %2d d1: %3d d2: %3d\n", - _timestamp, - name(), - channel(), - _data.lsb, - _data.msb ); -} - - -/*********/ -/* Sysex */ -/*********/ - -midievent::sysex::sysex ( void ) -{ - _data = NULL; - _size = 0; - _alloc = 0; -} - -midievent::sysex::~sysex ( void ) -{ - if ( _data ) - free( _data ); - - _data = NULL; -} - -/** add bytes to sysex message */ -void -midievent::sysex::append ( byte_t *data, size_t size ) -{ - if ( _size + size > _alloc ) - _data = (byte_t *)realloc( _data, _alloc += 256 ); - - memcpy( data + _size, data, size ); - - _size += size; -} - -/** return SysEx data */ -const byte_t * -midievent::sysex::data ( void ) const -{ - return _data; -} - -long -midievent::sysex::size ( void ) const -{ - return _size; -} - - - -bool -midievent::operator== ( const midievent &rhs ) const -{ - return _timestamp == rhs._timestamp && - ! bcmp( (void*)&_data, (void*)&rhs._data, size() ); -} diff --git a/sequencer/src/midievent.H b/sequencer/src/midievent.H deleted file mode 100644 index 9b840ba..0000000 --- a/sequencer/src/midievent.H +++ /dev/null @@ -1,232 +0,0 @@ - -/*******************************************************************************/ -/* Copyright (C) 2008 Jonathan Moore Liles */ -/* */ -/* This program is free software; you can redistribute it and/or modify it */ -/* under the terms of the GNU General Public License as published by the */ -/* Free Software Foundation; either version 2 of the License, or (at your */ -/* option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ -/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ -/* more details. */ -/* */ -/* You should have received a copy of the GNU General Public License along */ -/* with This program; see the file COPYING. If not,write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*******************************************************************************/ - -#pragma once - -#include "common.h" - - -/* raw MIDI event + timestamp */ -class midievent -{ - -public: - - class sysex { - size_t _size, _alloc; - byte_t *_data; - - public: - - sysex ( void ); - ~sysex ( void ); - - void append ( byte_t *data, size_t size ); - const byte_t * data ( void ) const; - long size ( void ) const; - - }; - -private: - - sysex *_sysex; - - tick_t _timestamp; /* in ticks */ - struct { - byte_t status, /* full status byte */ - lsb, /* data 1 */ - msb; /* data 2 */ - } _data; - -public: - - static inline int - event_size ( byte_t op ) - { - switch ( op ) - { - case NOTE_ON: case NOTE_OFF: case AFTERTOUCH: - case CONTROL_CHANGE: case PITCH_WHEEL: - return 3; - case PROGRAM_CHANGE: case CHANNEL_PRESSURE: - return 2; - default: - return 1; - } - }; - - /* define MIDI status bytes */ - enum { - STATUS_BIT = 0x80, - NOTE_OFF = 0x80, - NOTE_ON = 0x90, - AFTERTOUCH = 0xA0, - CONTROL_CHANGE = 0xB0, - PROGRAM_CHANGE = 0xC0, - CHANNEL_PRESSURE = 0xD0, - PITCH_WHEEL = 0xE0, - CLEAR_CHAN_MASK = 0xF0, - MIDI_CLOCK = 0xF8, - SYSEX = 0xF0, - SYSEX_END = 0xF7, - META = 0xFF - }; - - midievent ( void ); - virtual ~midievent ( void ); - - tick_t timestamp ( void ) const; - void timestamp ( tick_t time ); - void status ( byte_t status ); - byte_t status ( void ) const; - void channel ( byte_t channel ); - byte_t channel ( void ) const; - byte_t opcode ( void ) const; - void opcode ( byte_t o ); - void lsb ( byte_t n ); - void msb ( byte_t n ); - int lsb ( void ) const; - int msb ( void ) const; - int pitch ( void ) const; - void pitch ( int n ); - void data ( byte_t D1, byte_t D2 ); - void data ( byte_t *D1, byte_t *D2 ) const; - void raw ( byte_t *p, int l) const; - int size ( void ) const; - void note_velocity ( int vel ); - bool is_note_on ( void ) const; - bool is_note_off ( void ) const; - virtual unsigned char note ( void ) const; - virtual void note ( char note ); - unsigned char note_velocity ( void ) const; - bool is_same_note ( midievent * e ) const; - const char * name ( void ) const; - int name ( const char *name ) const; - void print ( void ) const; - void pretty_print ( void ) const; - - bool operator< ( const midievent &rhs ) const; - bool operator>= ( const midievent &rhs ) const; - - bool operator== ( const midievent &rhs ) const; - -}; - - -/**********************/ -/* Inlined accessors */ -/**********************/ - - -inline tick_t -midievent::timestamp ( void ) const -{ - return _timestamp; -} - -inline void -midievent::timestamp ( tick_t time ) -{ - _timestamp = time; -} - -inline void -midievent::status ( byte_t status ) -{ - _data.status = status; -} - -inline byte_t -midievent::status ( void ) const -{ - return _data.status; -} - -inline void -midievent::channel ( byte_t channel ) -{ - _data.status = (_data.status & 0xF0) | (channel & 0x0F); -} - -inline byte_t -midievent::channel ( void ) const -{ - return _data.status & 0x0F; -} - -inline byte_t -midievent::opcode ( void ) const -{ - return _data.status & 0xF0; -} - - -inline void -midievent::opcode ( byte_t opcode ) -{ - _data.status = (_data.status & 0x0F) | (opcode & 0xF0); -} - -inline void -midievent::lsb ( byte_t n ) -{ - _data.lsb = n & 0x7F; -} - -inline void -midievent::msb ( byte_t n ) -{ - _data.msb = n & 0x7F; -} - -inline int -midievent::lsb ( void ) const -{ - return _data.lsb; -} - -inline int -midievent::msb ( void ) const -{ - return _data.msb; -} - -inline bool -midievent::is_note_on ( void ) const -{ - return (opcode() == NOTE_ON); -} - -inline bool -midievent::is_note_off ( void ) const -{ - return (opcode() == NOTE_OFF); -} - -inline bool -midievent::operator< ( const midievent &rhs ) const -{ - return _timestamp < rhs._timestamp; -} - -inline bool -midievent::operator>= ( const midievent &rhs ) const -{ - return _timestamp >= rhs._timestamp; -} diff --git a/sequencer/src/pattern.C b/sequencer/src/pattern.C index b0cd948..7494b41 100644 --- a/sequencer/src/pattern.C +++ b/sequencer/src/pattern.C @@ -26,6 +26,9 @@ #include "transport.H" #include +#include +using namespace MIDI; + event_list pattern::_recorded_events; vector pattern::_patterns; int pattern::_solo; @@ -276,10 +279,26 @@ pattern::by_number ( int n ) const return pattern::pattern_by_number( n ); } +/** what to do when the row name is pressed */ +void +pattern::row_name_press ( int y ) +{ + /* echo note */ + midievent e; + + e.status( event::NOTE_ON ); + e.channel( _channel ); + e.timestamp( default_length() ); + e.note( y ); + e.note_velocity( 64 ); + + midi_output_immediate_event ( _port, &e ); +} + void pattern::put ( int x, int y, tick_t l ) { - l = l ? l : PPQN * 4 / _note; + l = l ? l : default_length(); Grid::put( x, y, l ); diff --git a/sequencer/src/pattern.H b/sequencer/src/pattern.H index 4b2b62c..d818a32 100644 --- a/sequencer/src/pattern.H +++ b/sequencer/src/pattern.H @@ -22,7 +22,6 @@ #include "grid.H" #include "canvas.H" #include "mapping.H" -// #include "event.H" #include "common.h" #include @@ -30,7 +29,7 @@ using std::vector; class pattern : public Grid { - static event_list _recorded_events; + static MIDI::event_list _recorded_events; static vector _patterns; static int _solo; static int _pattern_recording; @@ -68,7 +67,7 @@ public: static pattern * import ( smf *f, int track ); static pattern * recording ( void ); - static void record_event ( const midievent *e ); + static void record_event ( const MIDI::midievent *e ); pattern * create ( void ); pattern * by_number ( int n ) const; @@ -91,6 +90,7 @@ public: int queue ( void ) const; void randomize_row ( int y, int feel, float probability ); + void row_name_press ( int y ); int port ( void ) const; void port ( int p ); @@ -112,4 +112,9 @@ public: int ppqn ( void ) const; void ppqn ( int n ); + virtual tick_t default_length ( void ) const + { + return PPQN * 4 / _note; + } + }; diff --git a/sequencer/src/phrase.C b/sequencer/src/phrase.C index 0db6ae2..1aa7424 100644 --- a/sequencer/src/phrase.C +++ b/sequencer/src/phrase.C @@ -18,12 +18,13 @@ /*******************************************************************************/ #include "phrase.H" -#include "gui/draw.H" #include "pattern.H" #include "smf.H" #include "common.h" #include +using namespace MIDI; + vector phrase::_phrases; signal phrase::signal_create_destroy; diff --git a/sequencer/src/phrase.H b/sequencer/src/phrase.H index c09b319..b2351ab 100644 --- a/sequencer/src/phrase.H +++ b/sequencer/src/phrase.H @@ -46,6 +46,8 @@ public: static phrase * phrase_by_number ( int n ); static void reset ( void ); + virtual bool velocity_sensitive ( void ) const { return false; } + phrase *create ( void ); phrase * by_number ( int n ) const; diff --git a/sequencer/src/scale.C b/sequencer/src/scale.C index 81a6659..a14323b 100644 --- a/sequencer/src/scale.C +++ b/sequencer/src/scale.C @@ -21,6 +21,9 @@ #include "common.h" #include "stdlib.h" +#include + +using namespace MIDI; /* Define some scales. These don't really need to be stored on disk. Scales don't change that often. */ @@ -189,13 +192,20 @@ const char * Scale::note_name ( int k, int n ) const { /* all the magic is here */ + static char s[5]; - n %= 12; + const int mod_n = n % 12; // FIXME: searching is not efficient! for ( int i = _notes; i-- ; ) - if ( n == (_degrees[ i ] + k) % 12 ) - return chromatic_names[ n ]; + if ( mod_n == (_degrees[ i ] + k) % 12 ) + { + snprintf( s, sizeof(s), "%s%i", + chromatic_names[ mod_n ], + n / 12 ); + + return s; + } return NULL; } diff --git a/sequencer/src/scale.H b/sequencer/src/scale.H index c6411ff..61f0ffb 100644 --- a/sequencer/src/scale.H +++ b/sequencer/src/scale.H @@ -18,8 +18,7 @@ /*******************************************************************************/ #pragma once - -#include "event.H" +#include class Scale { @@ -41,7 +40,7 @@ public: static const char * chromatic_name ( int n ); static int octave ( int n ); - bool translate ( int k, midievent *e ) const; + bool translate ( int k, MIDI::midievent *e ) const; int note ( int k, int n ) const; const char * note_name ( int k, int n ) const; const char * name ( void ) const; diff --git a/sequencer/src/smf.C b/sequencer/src/smf.C index e79d3fb..f4fc21d 100644 --- a/sequencer/src/smf.C +++ b/sequencer/src/smf.C @@ -21,6 +21,7 @@ #include "phrase.H" #include "pattern.H" +using namespace MIDI; smf::smf ( void ) { diff --git a/sequencer/src/smf.H b/sequencer/src/smf.H index d44f25c..ad96db9 100644 --- a/sequencer/src/smf.H +++ b/sequencer/src/smf.H @@ -20,7 +20,6 @@ #pragma once #include "grid.H" -#include "event.H" class pattern; class phrase; @@ -104,7 +103,7 @@ public: void write_meta_event ( byte_t type, int n ); void write_meta_event ( byte_t type, const char *s ); - void write_event ( const midievent *e ); + void write_event ( const MIDI::midievent *e ); void write_header ( int tracks ); void open_chunk ( const char *id ); @@ -116,7 +115,7 @@ public: void cue ( bool b ); - list * read_track_events ( tick_t *length ); + list * read_track_events ( tick_t *length ); void write_phrase_info ( const phrase *p ); diff --git a/sequencer/src/transport.H b/sequencer/src/transport.H index 86d3c7e..68d7584 100644 --- a/sequencer/src/transport.H +++ b/sequencer/src/transport.H @@ -23,7 +23,7 @@ using namespace sigc; -#include "event.H" // just for tick_t +#include // just for tick_t #include diff --git a/sequencer/wscript b/sequencer/wscript index bd428df..b7d3ae3 100644 --- a/sequencer/wscript +++ b/sequencer/wscript @@ -46,18 +46,13 @@ src/NSM.C src/NSM/Client.C src/canvas.C src/debug.C -src/event.C -src/event_list.C src/grid.C -src/gui/draw.C src/gui/event_edit.fl -src/gui/input.C src/gui/ui.fl src/instrument.C src/jack.C src/main.C src/mapping.C -src/midievent.C src/pattern.C src/phrase.C src/scale.C