|
-
- /*******************************************************************************/
- /* 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 "phrase.H"
- #include "gui/draw.H"
- #include "pattern.H"
- #include "smf.H"
- #include "common.h"
-
- vector <phrase*> phrase::_phrases;
- signal <void> phrase::signal_create_destroy;
-
- phrase::phrase ( void )
- {
-
- viewport.h = 32;
- viewport.w = 32;
-
- _draw_shape = SQUARE;
-
- _add();
-
- char *s;
- asprintf( &s, "Phrase %d", number() );
-
- name( s );
-
- }
-
-
- phrase::~phrase ( void )
- {
- MESSAGE( "deleting phrase %d", number() );
- signal_create_destroy();
- }
-
- void
- phrase::_add ( void )
- {
- // keep track of all the phrases
- phrase::_phrases.push_back( this );
- _number = phrases();
-
- signal_create_destroy();
- }
-
- /* copy constructor */
- phrase::phrase ( const phrase &rhs ) : Grid( rhs )
- {
- _add();
- }
-
- phrase *
- phrase::clone ( void )
- {
- return new phrase( *this );
- }
-
- /******************/
- /* Static methods */
- /******************/
-
- int
- phrase::phrases ( void )
- {
- return phrase::_phrases.size();
- }
-
- phrase *
- phrase::phrase_by_number ( int n )
- {
- if ( n <= phrases() && n > 0 )
- {
- return phrase::_phrases[ n - 1 ];
- }
- return NULL;
- }
-
- void
- phrase::reset ( void )
- {
- for ( int n = phrase::phrases(); n-- ; )
- {
- delete phrase::_phrases.back();
- phrase::_phrases.pop_back();
- }
- }
-
-
-
-
- /*******************/
- /* Virtual Methods */
- /*******************/
-
- phrase *
- phrase::create ( void )
- {
- return new phrase;
- }
-
- phrase *
- phrase::by_number ( int n ) const
- {
- return phrase::phrase_by_number( n );
- }
-
- void
- phrase::put ( int x, int y, tick_t l )
- {
- // FIXME: fix insertion length to the length of the pattern
- // referred to by this row.
-
- l = 4;
-
- // FIXME: use translation here.
- pattern *p = pattern::pattern_by_number( y + 1 );
-
- if ( ! p )
- return;
-
- l = p->length();
-
- Grid::put( x, y, l );
- }
-
- const char *
- phrase::row_name ( int r ) const
- {
- pattern *p = pattern::pattern_by_number( r + 1 );
-
- return p ? p->name() : NULL;
- }
-
- void
- phrase::draw_row_names ( Canvas *c ) const
- {
- for ( int y = 128; y-- ; )
- {
- pattern *p = pattern::pattern_by_number( y + 1 );
-
- if ( p && p->name() )
- c->draw_row_name( y, p->name(), 0 );
- }
- }
-
-
- void
- phrase::trigger ( tick_t start, tick_t end )
- {
- _start = start;
- _end = end;
- }
-
- // FIXME: so much of this is copied from pattern.C, there has
- // to be a way to share more of this code.
- void
- phrase::play ( tick_t start, tick_t end )
- {
- /* get our own copy of this pointer so UI thread can change it. */
- const data *d = const_cast< const data * >(_rd);
-
- if ( start > _end )
- {
- WARNING( "attempt to play a phrase that has ended (%lu, %lu)", start, _end );
- return;
- }
- else
- if ( start < _start )
- // not ready yet
- return;
-
-
- if ( start < _start )
- start = _start;
-
- if ( end > _end )
- end = _end;
-
- _playing = true;
-
- // where we are in the absolute time
- tick_t tick = start - _start;
- int num_played = tick / d->length;
- tick_t offset = _start + (d->length * num_played);
-
- _index = tick % d->length;
-
- if ( _index < end - start )
- MESSAGE( "Triggered phrase %d at tick %lu (ls: %lu, le: %lu, o: %lu)", number(), start, _start, _end, offset );
-
- try_again:
-
- // pattern is empty
- if ( d->events.empty() )
- goto done;
-
- for ( const event *e = d->events.first(); e; e = e->next() )
- {
- // MESSAGE( "s[%ld] -> t[%ld] : %ld, len %ld", start, end, e->timestamp(), _length ); // (*e).print();
-
- tick_t ts = e->timestamp() + offset;
-
- if ( ts >= end )
- goto done;
-
- if ( ts >= start )
- {
- event ne = *e;
- if ( ne.is_note_on() || ne.is_note_off() )
- {
- int ev_note = e->note();
-
- // d->mapping.translate( &ne );
-
- pattern *p = pattern::pattern_by_number( 1 + note_to_y( ev_note ) );
-
- if ( p )
- {
- if ( e->is_note_on() )
- {
- p->trigger( ts, offset + e->link()->timestamp() );
- p->play( ts, end );
- }
- else
- if ( e->is_note_off() )
- p->stop();
- }
- }
- }
- }
- // ran out of events, but there's still some loop left to play.
- offset += d->length;
- goto try_again;
-
- MESSAGE( "out of events, resetting to satisfy loop" );
-
- done: ;
- }
-
-
- void
- phrase::load ( smf *f )
- {
- lock();
-
- f->read_phrase_info( this );
-
- tick_t len;
-
- list <midievent> *me = f->read_track_events( &len );
-
- _rw->events = *me;
- delete me;
-
- if ( len )
- _rw->length = len;
-
- unlock();
- }
-
- void
- phrase::dump ( smf *f )
- {
- f->open_track( _name, -1 );
-
- f->write_phrase_info( this );
-
- f->cue( true );
-
- Grid::dump( f, 0, false );
-
- f->close_track( length() );
- }
|