|
-
- /*******************************************************************************/
- /* 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 "pattern.H"
- #include "non.H"
- #include "common.h"
- #include "smf.H"
-
- #include "jack.H"
- #include "transport.H"
-
- event_list pattern::_recorded_events;
- vector <pattern*> pattern::_patterns;
- int pattern::_solo;
- int pattern::_pattern_recording;
-
- signal <void> pattern::signal_create_destroy;
-
- pattern::pattern ( void )
- {
-
- viewport.h = 32;
- viewport.w = 32;
-
- _draw_shape = CIRCLE;
- _channel = _port = 0;
-
- _ppqn = 4;
- _bpb = 4;
- _note = 8;
- int _bars = 2;
-
- _triggered = false;
-
- // we need to reinitalize this.
- data *d = const_cast< data * >( _rd );
-
- d->length = x_to_ts( _bpb * _ppqn * _bars );
-
- // mapping.open( Mapping::INSTRUMENT, "Default" );
- mapping.open( Mapping::SCALE, "Major" );
-
- _add();
-
- char *s;
- asprintf( &s, "Pattern %d", number() );
-
- name( s );
- }
-
- void
- pattern::_add ( void )
- {
- // keep track of all the patterns
- pattern::_patterns.push_back( this );
- _number = patterns();
-
- signal_create_destroy();
- }
-
- pattern::~pattern ( void )
- {
- DEBUG( "deleting pattern %d", number() );
- signal_create_destroy();
- }
-
- /* copy constructor */
- pattern::pattern ( const pattern &rhs ) : Grid( rhs )
- {
- _note = rhs._note;
- _port = rhs._port;
- _channel = rhs._channel;
-
- mapping = rhs.mapping;
-
- _add();
- }
-
- pattern *
- pattern::clone ( void )
- {
- return new pattern( *this );
- }
-
- /******************/
- /* Static methods */
- /******************/
-
- int
- pattern::solo ( void )
- {
- return pattern::_solo;
- }
-
- int
- pattern::patterns ( void )
- {
- return pattern::_patterns.size();
- }
-
- // this is the static one
- pattern *
- pattern::pattern_by_number ( int n )
- {
- if ( n <= patterns() && n > 0 )
- {
- return pattern::_patterns[ n - 1 ];
- }
- return NULL;
- }
-
- /** delete all patterns in preparation for a reload */
- void
- pattern::reset ( void )
- {
- for ( int n = pattern::patterns(); n-- ; )
- {
- delete pattern::_patterns.back();
- pattern::_patterns.pop_back();
- }
- }
-
- void
- pattern::record_event ( const midievent *me )
- {
- /* set the events aside in a dedicated list--the recording pattern
- * will decide what to do with them the next time around the
- * loop */
-
- /* FIXME: how does the pattern decide when to loop? It seems
- reasonable that /merge/ and /replace/ modes should be bound to
- the previous pattern length, but what about "NEW" mode? Should it
- just use this entire list as a new pattern (of whatever length)
- when recording is halted? */
-
- event *e = new event;
-
- *e = *me;
-
- pattern::_recorded_events.append( e );
-
- record_mode_e mode = config.record_mode;
-
- if ( mode == OVERWRITE || mode == LAYER )
- {
- pattern *p = pattern::recording();
-
- if ( ! p->_cleared )
- {
-
- if ( mode == LAYER )
- {
- p->record_stop();
-
- p = p->clone();
-
- p->record( 0 );
- }
-
- p->clear();
-
- p->_cleared = true;
- }
-
- mode = MERGE;
- }
-
- /* let's fill in the pattern 'live' in merge mode. looks a little
- complicated because we have to wait for a note-off before it's
- safe to insert */
- if ( mode == MERGE || mode == NEW )
- {
-
- pattern *p = pattern::recording();
-
- p->lock();
-
- event_list *el = &pattern::_recorded_events;
-
- if ( e->is_note_off() )
- {
- event *off = e;
-
- for ( event *on = el->last(); on; on = on->prev() )
- {
- if ( on->is_note_on() &&
- on->is_same_note( off ) )
- // &&
- // *on < *e )
- {
- el->unlink( on );
- el->unlink( off );
-
- tick_t duration = off->timestamp() - on->timestamp();
-
- /* place within loop */
- on->timestamp( ( on->timestamp() - p->_start ) % p->_rw->length );
-
- on->link( off );
- on->note_duration( duration );
-
- p->_rw->events.mix( on );
-
- break;
- }
- }
- }
- else
- if ( ! e->is_note_on() )
- {
-
- // if ( ! filter )
-
- e->timestamp( e->timestamp() % p->_rw->length );
-
- el->unlink( e );
- p->_rw->events.insert( e );
- }
-
- p->unlock();
- }
- }
-
- pattern *
- pattern::recording ( void )
- {
- return pattern::pattern_by_number( pattern::_pattern_recording );
- }
-
-
-
- /*******************/
- /* Virtual Methods */
- /*******************/
-
- /* allows us to create a new pattern/phrase from a base class pointer */
- pattern *
- pattern::create ( void )
- {
- return new pattern;
- }
-
- pattern *
- pattern::by_number ( int n ) const
- {
- return pattern::pattern_by_number( n );
- }
-
- void
- pattern::put ( int x, int y, tick_t l )
- {
- l = l ? l : PPQN * 4 / _note;
-
- Grid::put( x, y, l );
-
- if ( ! transport.rolling )
- {
- /* echo note */
- midievent e;
-
- e.status( event::NOTE_ON );
- e.channel( _channel );
- e.timestamp( l );
- e.note( y_to_note( y ) );
- e.note_velocity( 64 );
-
- midi_output_immediate_event ( _port, &e );
- }
- }
-
- const char *
- pattern::row_name ( int r ) const
- {
- return mapping.note_name( y_to_note( r ) );
- }
-
- void
- pattern::draw_row_names ( Canvas *c ) const
- {
- for ( int y = 128; y-- ; )
- c->draw_row_name( y, mapping.note_name( y_to_note( y ) ), mapping.velocity( y_to_note( y ) ) );
- }
-
- void
- pattern::trigger ( tick_t start, tick_t end )
- {
- if ( start > end )
- ASSERTION( "programming error: invalid loop trigger! (%lu-%lu)", start, end );
-
- _start = start;
- _end = end;
- _index = 0;
- }
-
-
- void
- pattern::stop ( void ) const
- {
- _playing = false;
-
- _start = 0;
- _end = 0;
- _index = 0;
- }
-
-
- void
- pattern::mode ( int n )
- {
- if ( song.play_mode == TRIGGER )
- {
- switch ( n )
- {
- case PLAY:
- _triggered = true;
- break;
- case MUTE:
- _triggered = false;
- break;
- }
-
- return;
- }
-
- if ( n == SOLO )
- {
- if ( pattern::_solo )
- ((Grid*)pattern::pattern_by_number( pattern::_solo ))->mode( PLAY );
- pattern::_solo = _number;
- Grid::mode( SOLO );
- }
- else
- {
- if ( pattern::_solo == _number )
- pattern::_solo = 0;
-
- Grid::mode( n );
- }
- }
-
- int
- pattern::mode ( void ) const
- {
-
- if ( song.play_mode == TRIGGER )
- {
- if ( ! _triggered )
- return MUTE;
- else
- return PLAY;
- }
-
- if ( pattern::_solo )
- {
- if ( pattern::_solo == _number )
- return SOLO;
- else
- return MUTE;
- }
- else
- return Grid::mode();
- }
-
- /* WARNING: runs in the RT thread! */
- // output notes from /start/ to /end/ (absolute)
- void
- pattern::play ( tick_t start, tick_t end ) const
- {
- /* get our own copy of this pointer so UI thread can change it. */
- const data *d = const_cast< const data * >(_rd);
-
- if ( start > _end )
- {
- stop();
- WARNING( "attempt to play a loop (pattern %d) that has ended (%lu, %lu)", number(), start, _end );
- return;
- }
- else
- if ( end < _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);
-
- const event *e;
-
- _index = tick % d->length;
-
- if ( _index < end - start )
- {
- DEBUG( "Triggered pattern %d at tick %lu (ls: %lu, le: %lu, o: %lu)", number(), start, _start, _end, offset );
-
- _cleared = false;
- }
-
- if ( mode() == MUTE )
- return;
-
- try_again:
-
- // pattern is empty
- if ( d->events.empty() )
- goto done;
-
- for ( 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 )
- {
- midievent me = *e;
-
-
- // MESSAGE( "timestamp %d, tick %d, ts - start == %lu", e->timestamp(), start,
- // e->timestamp() - start);
-
- /* set the channel */
- me.channel( _channel );
-
- /* set the in-cycle timestamp */
- me.timestamp ( ts - start );
-
- if ( me.is_note_on() )
- {
- mapping.translate( &me );
- midi_output_event( _port, &me, 1 + e->note_duration() );
- }
- else
- if ( me.is_note_off() )
- midi_output_event( _port, &me, 0 );
- else
- /* any other event type */
- midi_output_event( _port, &me );
- }
- }
-
- // ran out of events, but there's still some loop left to play.
- offset += d->length;
- goto try_again;
-
- DEBUG( "out of events, resetting to satisfy loop" );
-
- done: ;
-
- }
-
- /* Import /track/ of /f/ as new pattern */
- pattern *
- pattern::import ( smf *f, int track )
- {
- if ( ! f->seek_track( track ) )
- return NULL;
-
- pattern *p = new pattern;
-
- p->lock();
-
- p->load( f );
-
- /* file could have any notes in it... Use Chromatic scale to
- ensure all are visible */
- p->mapping.open( Mapping::SCALE, "Chromatic" );
-
- p->unlock();
-
- p->fit();
-
- return p;
- }
-
- /** fill pattern from current track of /f/ */
- void
- pattern::load ( smf *f )
- {
- lock();
-
- f->read_pattern_info( this );
-
- tick_t len;
-
- list <midievent> *e = f->read_track_events( &len );
-
- /* set channel to channel of first event... */
- if ( e->size() )
- _channel = e->front().channel();
-
- /* copy events into pattern */
- _rw->events = *e;
- delete e;
-
- if ( len )
- _rw->length = len;
-
- unlock();
-
- // print();
- }
-
- /** save (export) pattern to file /name/ */
- void
- pattern::save ( const char *name ) const
- {
- smf f;
-
- /* open for writing */
- f.open( name, smf::WRITE );
-
- /* writing SMF 0 track */
- f.write_header( 0 );
-
- f.open_track( _name, _number );
-
- Grid::dump( &f, _channel, true );
-
- f.close_track( length() );
- }
-
- /** dump pattern as a track in an already open MIDI file */
- void
- pattern::dump ( smf *f ) const
- {
- f->open_track( _name, _number );
-
- f->write_pattern_info( this );
-
- Grid::dump( f, _channel, false );
-
- f->close_track( length() );
- }
-
-
- void
- pattern::randomize_row ( int y, int feel, float probability )
- {
- lock();
-
- int l = PPQN * 4 / _note;
-
- int bx = ts_to_x( _rw->length - l );
-
- float *p = (float *)alloca( feel * sizeof( float ) );
-
- float prob = probability;
- for ( int i = 0; i < feel; i++ )
- {
- p[i] = prob;
- // reduce probability as we move away from center
- prob *= 0.5;
- }
-
- for ( int x = 0; x < bx; x++ )
- {
- float r = ((float)rand()) / RAND_MAX;
-
- if ( p[ x % feel ] + r >= 1 )
- put( x, y, l );
- }
-
- unlock();
- }
-
- /*************/
- /* Recording */
- /*************/
-
- void
- pattern::record ( int mode )
- {
- _recording = true;
- pattern::_pattern_recording = _number;
- }
-
- void
- pattern::record_stop ( void )
- {
- if ( ! _recording )
- return;
-
- _recording = false;
-
- if ( config.record_mode == NEW )
- trim();
-
- pattern::_recorded_events.clear();
- }
-
-
-
- /*******************************/
- /* Pattern specific accessors. */
- /*******************************/
-
-
- int
- pattern::port ( void ) const
- {
- return _port;
- }
-
- void
- pattern::port ( int p )
- {
- _port = p;
- }
-
- int
- pattern::channel ( void ) const
- {
- return _channel;
- }
-
- void
- pattern::channel ( int c )
- {
- _channel = c;
- }
-
- int
- pattern::note ( void ) const
- {
- return _note;
- }
-
- void
- pattern::note ( int n )
- {
- _note = n;
- }
-
-
- int
- pattern::ppqn ( void ) const
- {
- return _ppqn;
- }
-
- void
- pattern::ppqn ( int n )
- {
- _ppqn = n;
- }
-
- int
- pattern::key ( void ) const
- {
- return mapping.key();
- }
-
- void
- pattern::key ( int k )
- {
- mapping.key( k );
- }
|