@@ -20,7 +20,7 @@ | |||
#include <FL/Fl.H> | |||
#include <FL/Fl_Double_Window.H> | |||
#include <FL/Fl_Single_Window.H> | |||
#include <FL/Fl_Scalepack.H> | |||
#include <FL/Fl_Pack.H> | |||
#include <FL/Fl_Choice.H> | |||
#include <FL/fl_draw.H> | |||
#include <sys/time.h> | |||
@@ -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 ); | |||
{ | |||
@@ -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 <stdio.h> | |||
#include <string.h> | |||
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 ); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 <midievent> &rhs ) | |||
{ | |||
clear(); | |||
for ( list <midievent>::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 ); | |||
} | |||
} |
@@ -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 <list> | |||
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 <midievent> &rhs ); | |||
event *operator[] ( unsigned int index ); | |||
// friend class event; | |||
}; | |||
} |
@@ -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'], | |||
@@ -20,7 +20,7 @@ | |||
#pragma once | |||
#include "grid.H" | |||
#include <FL/Fl_Widget.H> | |||
#include <FL/Fl_Group.H> | |||
#include <sigc++/sigc++.h> | |||
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 <void> signal_settings_change; | |||
signal <void> signal_draw; | |||
signal <void> signal_resize; | |||
signal <void> 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 | |||
@@ -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"; | |||
@@ -1,10 +0,0 @@ | |||
#pragma once | |||
struct dash | |||
{ | |||
tick_t timestamp; | |||
tick_t length; | |||
unsigned char color; | |||
}; | |||
@@ -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 <stdio.h> | |||
#include <string.h> | |||
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 ); | |||
} |
@@ -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 <stdio.h> | |||
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; | |||
} |
@@ -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 <midievent> &rhs ) | |||
{ | |||
clear(); | |||
for ( list <midievent>::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 ); | |||
} |
@@ -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 <list> | |||
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 <midievent> &rhs ); | |||
event *operator[] ( unsigned int index ); | |||
// friend class event; | |||
}; |
@@ -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(); | |||
@@ -19,9 +19,8 @@ | |||
#pragma once | |||
#include "event.H" | |||
#include "event_list.H" | |||
#include "dash.H" | |||
#include <MIDI/event.H> | |||
#include <MIDI/event_list.H> | |||
#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 <data *> _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; | |||
@@ -1,4 +0,0 @@ | |||
all: | |||
@ make -s -C .. |
@@ -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 <stdlib.h> | |||
#include <math.h> | |||
#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 ); | |||
} |
@@ -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, ... ); |
@@ -28,6 +28,15 @@ decl {\#include "../grid.H"} {private local | |||
decl {\#include "../scale.H"} {private local | |||
} | |||
decl {\#include <MIDI/event.H>} {public global | |||
} | |||
decl {\#include <MIDI/event_list.H>} {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 | |||
} | |||
} | |||
@@ -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 <cstring> | |||
/* system */ | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#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; | |||
} |
@@ -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 ); |
@@ -33,8 +33,12 @@ | |||
#include <list> | |||
#include <string> | |||
#include <MIDI/event.H> | |||
using namespace MIDI; | |||
using std::list; | |||
using std::string; | |||
@@ -22,7 +22,7 @@ | |||
#include <list> | |||
using std::list; | |||
#include "event.H" | |||
#include <MIDI/midievent.H> | |||
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; | |||
@@ -32,7 +32,10 @@ | |||
#include "transport.H" | |||
#include "pattern.H" | |||
#include "phrase.H" | |||
#include "event_list.H" | |||
#include <MIDI/event_list.H> | |||
#include <MIDI/midievent.H> | |||
using namespace MIDI; | |||
#ifdef JACK_MIDI_PROTO_API | |||
/* correct for prototype version of API */ | |||
@@ -1,18 +1,17 @@ | |||
#include <jack/jack.h> | |||
#include <MIDI/midievent.H> | |||
#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 ); |
@@ -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 ) ); | |||
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 ); | |||
init_colors(); | |||
ui = new UI; | |||
clear_song(); | |||
#ifdef HAVE_XPM | |||
ui->main_window->icon((char *)p); | |||
#endif | |||
ui->main_window->show( 0, 0 ); | |||
instance_name = strdup( APP_NAME ); | |||
@@ -20,7 +20,9 @@ | |||
#include "mapping.H" | |||
#include "stdlib.h" | |||
#include "common.h" | |||
#include <MIDI/midievent.H> | |||
using namespace MIDI; | |||
/* Is C++'s dispatching useless or what? */ | |||
#define IS_INSTRUMENT ( _type == INSTRUMENT ) | |||
@@ -21,6 +21,7 @@ | |||
#include "scale.H" | |||
#include "instrument.H" | |||
#include <MIDI/midievent.H> | |||
/* 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; | |||
@@ -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() ); | |||
} |
@@ -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; | |||
} |
@@ -26,6 +26,9 @@ | |||
#include "transport.H" | |||
#include <math.h> | |||
#include <MIDI/event_list.H> | |||
using namespace MIDI; | |||
event_list pattern::_recorded_events; | |||
vector <pattern*> 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 ); | |||
@@ -22,7 +22,6 @@ | |||
#include "grid.H" | |||
#include "canvas.H" | |||
#include "mapping.H" | |||
// #include "event.H" | |||
#include "common.h" | |||
#include <vector> | |||
@@ -30,7 +29,7 @@ using std::vector; | |||
class pattern : public Grid | |||
{ | |||
static event_list _recorded_events; | |||
static MIDI::event_list _recorded_events; | |||
static vector <pattern *> _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; | |||
} | |||
}; |
@@ -18,12 +18,13 @@ | |||
/*******************************************************************************/ | |||
#include "phrase.H" | |||
#include "gui/draw.H" | |||
#include "pattern.H" | |||
#include "smf.H" | |||
#include "common.h" | |||
#include <math.h> | |||
using namespace MIDI; | |||
vector <phrase*> phrase::_phrases; | |||
signal <void> phrase::signal_create_destroy; | |||
@@ -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; | |||
@@ -21,6 +21,9 @@ | |||
#include "common.h" | |||
#include "stdlib.h" | |||
#include <MIDI/midievent.H> | |||
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; | |||
} | |||
@@ -18,8 +18,7 @@ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include "event.H" | |||
#include <MIDI/midievent.H> | |||
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; | |||
@@ -21,6 +21,7 @@ | |||
#include "phrase.H" | |||
#include "pattern.H" | |||
using namespace MIDI; | |||
smf::smf ( void ) | |||
{ | |||
@@ -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 <midievent> * read_track_events ( tick_t *length ); | |||
list <MIDI::midievent> * read_track_events ( tick_t *length ); | |||
void write_phrase_info ( const phrase *p ); | |||
@@ -23,7 +23,7 @@ | |||
using namespace sigc; | |||
#include "event.H" // just for tick_t | |||
#include <MIDI/types.h> // just for tick_t | |||
#include <jack/transport.h> | |||
@@ -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 | |||