2007-12-18 05:09:02 +01:00
|
|
|
|
|
|
|
|
|
/*******************************************************************************/
|
|
|
|
|
/* 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 );
|
|
|
|
|
|
2008-05-20 07:18:10 +02:00
|
|
|
|
Grid::dump( f, 0 );
|
2007-12-18 05:09:02 +01:00
|
|
|
|
|
|
|
|
|
f->close_track( length() );
|
|
|
|
|
}
|