non/phrase.C

291 lines
6.2 KiB
C++
Raw Normal View History

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 );
Grid::dump( f, 0, false );
f->close_track( length() );
}