Browse Source

Sequencer: More simplification of pattern drawing.

tags/non-daw-v1.2.0
Jonathan Moore Liles 12 years ago
parent
commit
a872d99e9b
42 changed files with 2777 additions and 2917 deletions
  1. +2
    -2
      FL/util/ntk-perf.C
  2. +162
    -0
      nonlib/MIDI/event.C
  3. +99
    -0
      nonlib/MIDI/event.H
  4. +631
    -0
      nonlib/MIDI/event_list.C
  5. +91
    -0
      nonlib/MIDI/event_list.H
  6. +3
    -0
      nonlib/wscript
  7. +927
    -318
      sequencer/src/canvas.C
  8. +29
    -15
      sequencer/src/canvas.H
  9. +1
    -1
      sequencer/src/const.h
  10. +0
    -10
      sequencer/src/dash.H
  11. +0
    -144
      sequencer/src/event.C
  12. +0
    -83
      sequencer/src/event.H
  13. +0
    -627
      sequencer/src/event_list.C
  14. +0
    -89
      sequencer/src/event_list.H
  15. +137
    -54
      sequencer/src/grid.C
  16. +29
    -17
      sequencer/src/grid.H
  17. +0
    -4
      sequencer/src/gui/Makefile
  18. +0
    -105
      sequencer/src/gui/draw.C
  19. +0
    -34
      sequencer/src/gui/draw.H
  20. +20
    -13
      sequencer/src/gui/event_edit.fl
  21. +0
    -347
      sequencer/src/gui/input.C
  22. +0
    -12
      sequencer/src/gui/input.H
  23. +555
    -533
      sequencer/src/gui/ui.fl
  24. +4
    -0
      sequencer/src/instrument.C
  25. +2
    -2
      sequencer/src/instrument.H
  26. +4
    -1
      sequencer/src/jack.C
  27. +6
    -7
      sequencer/src/jack.H
  28. +20
    -28
      sequencer/src/main.C
  29. +2
    -0
      sequencer/src/mapping.C
  30. +2
    -1
      sequencer/src/mapping.H
  31. +0
    -218
      sequencer/src/midievent.C
  32. +0
    -232
      sequencer/src/midievent.H
  33. +20
    -1
      sequencer/src/pattern.C
  34. +8
    -3
      sequencer/src/pattern.H
  35. +2
    -1
      sequencer/src/phrase.C
  36. +2
    -0
      sequencer/src/phrase.H
  37. +13
    -3
      sequencer/src/scale.C
  38. +2
    -3
      sequencer/src/scale.H
  39. +1
    -0
      sequencer/src/smf.C
  40. +2
    -3
      sequencer/src/smf.H
  41. +1
    -1
      sequencer/src/transport.H
  42. +0
    -5
      sequencer/wscript

+ 2
- 2
FL/util/ntk-perf.C View File

@@ -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 );
{


+ 162
- 0
nonlib/MIDI/event.C View File

@@ -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 );
}
}

+ 99
- 0
nonlib/MIDI/event.H View File

@@ -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;
}
}

+ 631
- 0
nonlib/MIDI/event_list.C View File

@@ -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 );
}
}

+ 91
- 0
nonlib/MIDI/event_list.H View File

@@ -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;
};
}

+ 3
- 0
nonlib/wscript View File

@@ -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'],


+ 927
- 318
sequencer/src/canvas.C
File diff suppressed because it is too large
View File


+ 29
- 15
sequencer/src/canvas.H View File

@@ -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


+ 1
- 1
sequencer/src/const.h View File

@@ -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";


+ 0
- 10
sequencer/src/dash.H View File

@@ -1,10 +0,0 @@

#pragma once

struct dash
{
tick_t timestamp;
tick_t length;
unsigned char color;
};


+ 0
- 144
sequencer/src/event.C View File

@@ -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 );
}

+ 0
- 83
sequencer/src/event.H View File

@@ -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;
}

+ 0
- 627
sequencer/src/event_list.C View File

@@ -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 );
}

+ 0
- 89
sequencer/src/event_list.H View File

@@ -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;
};

+ 137
- 54
sequencer/src/grid.C View File

@@ -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();


+ 29
- 17
sequencer/src/grid.H View File

@@ -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;


+ 0
- 4
sequencer/src/gui/Makefile View File

@@ -1,4 +0,0 @@


all:
@ make -s -C ..

+ 0
- 105
sequencer/src/gui/draw.C View File

@@ -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 );
}

+ 0
- 34
sequencer/src/gui/draw.H View File

@@ -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, ... );

+ 20
- 13
sequencer/src/gui/event_edit.fl View File

@@ -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
}
}


+ 0
- 347
sequencer/src/gui/input.C View File

@@ -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;
}

+ 0
- 12
sequencer/src/gui/input.H View File

@@ -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 );

+ 555
- 533
sequencer/src/gui/ui.fl
File diff suppressed because it is too large
View File


+ 4
- 0
sequencer/src/instrument.C View File

@@ -33,8 +33,12 @@


#include <list>

#include <string>

#include <MIDI/event.H>
using namespace MIDI;

using std::list;
using std::string;



+ 2
- 2
sequencer/src/instrument.H View File

@@ -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;


+ 4
- 1
sequencer/src/jack.C View File

@@ -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 */


+ 6
- 7
sequencer/src/jack.H View File

@@ -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 );

+ 20
- 28
sequencer/src/main.C View File

@@ -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 );


+ 2
- 0
sequencer/src/mapping.C View File

@@ -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 )


+ 2
- 1
sequencer/src/mapping.H View File

@@ -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;


+ 0
- 218
sequencer/src/midievent.C View File

@@ -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() );
}

+ 0
- 232
sequencer/src/midievent.H View File

@@ -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;
}

+ 20
- 1
sequencer/src/pattern.C View File

@@ -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 );



+ 8
- 3
sequencer/src/pattern.H View File

@@ -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;
}

};

+ 2
- 1
sequencer/src/phrase.C View File

@@ -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;



+ 2
- 0
sequencer/src/phrase.H View File

@@ -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;



+ 13
- 3
sequencer/src/scale.C View File

@@ -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;
}


+ 2
- 3
sequencer/src/scale.H View File

@@ -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;


+ 1
- 0
sequencer/src/smf.C View File

@@ -21,6 +21,7 @@
#include "phrase.H"
#include "pattern.H"

using namespace MIDI;

smf::smf ( void )
{


+ 2
- 3
sequencer/src/smf.H View File

@@ -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 );



+ 1
- 1
sequencer/src/transport.H View File

@@ -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>



+ 0
- 5
sequencer/wscript View File

@@ -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


Loading…
Cancel
Save