From 2f06f509d7f62525580709b04006c6445c165a18 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Tue, 23 Oct 2012 22:44:19 -0700 Subject: [PATCH] Timeline: Implement new cursor system. This adds new cursor rulers for edit, punch and playback. P1/P2 are now known as the Edit Cursor. Multiple Punch Cursors may be defined. The playback cursor affects Home and End controls. New options are available for looping over the playback cursor (even while recording) and automatically creating new takes at start of recording (or loop). The new cursor objects bump the project file version. Older versions of non-daw will be unable to load new/edited projects. --- timeline/src/Annotation_Point.H | 12 +- timeline/src/Annotation_Region.C | 10 +- timeline/src/Annotation_Region.H | 14 - timeline/src/Cursor_Point.C | 140 ++++++++++ timeline/src/Cursor_Point.H | 65 +++++ timeline/src/Cursor_Region.C | 154 +++++++++++ timeline/src/Cursor_Region.H | 74 +++++ timeline/src/Cursor_Sequence.C | 62 +++++ timeline/src/Cursor_Sequence.H | 54 ++++ timeline/src/Engine/Timeline.C | 72 ++++- timeline/src/Project.C | 9 +- timeline/src/Sequence.C | 24 +- timeline/src/Sequence.H | 4 +- timeline/src/Sequence_Point.H | 6 +- timeline/src/Sequence_Region.C | 1 + timeline/src/Sequence_Widget.C | 9 + timeline/src/Sequence_Widget.H | 26 +- timeline/src/TLE.fl | 17 ++ timeline/src/Timeline.C | 454 +++++++++++++++++++++++++------ timeline/src/Timeline.H | 41 ++- timeline/src/Transport.C | 17 +- timeline/src/main.C | 3 + 22 files changed, 1123 insertions(+), 145 deletions(-) create mode 100644 timeline/src/Cursor_Point.C create mode 100644 timeline/src/Cursor_Point.H create mode 100644 timeline/src/Cursor_Region.C create mode 100644 timeline/src/Cursor_Region.H create mode 100644 timeline/src/Cursor_Sequence.C create mode 100644 timeline/src/Cursor_Sequence.H diff --git a/timeline/src/Annotation_Point.H b/timeline/src/Annotation_Point.H index c7f29db..028732b 100644 --- a/timeline/src/Annotation_Point.H +++ b/timeline/src/Annotation_Point.H @@ -30,7 +30,7 @@ class Annotation_Point : public Sequence_Point protected: -// const char *class_name ( void ) { return "Annotation_Point"; } +// const char *class_label ( void ) { return "Annotation_Point"; } virtual void get ( Log_Entry &e ) const { @@ -51,7 +51,7 @@ protected: e.get( i, &s, &v ); if ( ! strcmp( s, ":label" ) ) - name( v ); + label( v ); } // timeline->redraw(); @@ -67,13 +67,13 @@ public: LOG_CREATE_FUNC( Annotation_Point ); SEQUENCE_WIDGET_CLONE_FUNC( Annotation_Point ); - Annotation_Point ( Sequence *sequence, nframes_t when, const char *name ) + Annotation_Point ( Sequence *sequence, nframes_t when, const char *label ) { _sequence = sequence; _r->start = when; - _label = strdup( name ); + _label = strdup( label ); log_create(); } @@ -96,10 +96,10 @@ public: if ( m == FL_PUSH && Fl::test_shortcut( FL_BUTTON3 ) && ! Fl::event_shift() ) { - const char *s = fl_input( "New name for mark:", name() ); + const char *s = fl_input( "New label for mark:", label() ); if ( s ) - name( s ); + label( s ); return 0; } diff --git a/timeline/src/Annotation_Region.C b/timeline/src/Annotation_Region.C index c80a4b9..73df324 100644 --- a/timeline/src/Annotation_Region.C +++ b/timeline/src/Annotation_Region.C @@ -45,13 +45,13 @@ Annotation_Region::set ( Log_Entry &e ) e.get( i, &s, &v ); if ( ! strcmp( s, ":label" ) ) - name( v ); + label( v ); } // timeline->redraw(); } -Annotation_Region::Annotation_Region ( Sequence *sequence, nframes_t when, const char *name ) +Annotation_Region::Annotation_Region ( Sequence *sequence, nframes_t when, const char *label ) { _sequence = sequence; @@ -60,7 +60,7 @@ Annotation_Region::Annotation_Region ( Sequence *sequence, nframes_t when, const /* FIXME: hack */ _r->length = 400; - _label = strdup( name ); + _label = strdup( label ); log_create(); } @@ -103,10 +103,10 @@ Annotation_Region::handle ( int m ) { if ( test_press( FL_BUTTON3 ) ) { - char *s = fl_text_edit( "Annotation text:", "&Save", name() ); + char *s = fl_text_edit( "Annotation text:", "&Save", label() ); if ( s ) - name( s ); + label( s ); free( s ); diff --git a/timeline/src/Annotation_Region.H b/timeline/src/Annotation_Region.H index cefb6d2..9d56da7 100644 --- a/timeline/src/Annotation_Region.H +++ b/timeline/src/Annotation_Region.H @@ -30,19 +30,6 @@ class Annotation_Region : public Sequence_Region /* not permitted */ Annotation_Region & operator = ( const Annotation_Region &rhs ); - char *_label; - -public: - - const char *name ( void ) const { return _label; } - void name ( const char *s ) - { - if ( _label ) - free( _label ); - _label = strdup( s ); - redraw(); - } - protected: virtual void get ( Log_Entry &e ) const; @@ -50,7 +37,6 @@ protected: Annotation_Region ( ) { - _label = NULL; } Annotation_Region ( const Annotation_Region &rhs ); diff --git a/timeline/src/Cursor_Point.C b/timeline/src/Cursor_Point.C new file mode 100644 index 0000000..771f896 --- /dev/null +++ b/timeline/src/Cursor_Point.C @@ -0,0 +1,140 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 "Cursor_Point.H" +#include "Cursor_Sequence.H" +#include "Timeline.H" // for timeline->time_track + +Cursor_Point::Cursor_Point ( ) +{ +// timeline->->add( this ); + _label = NULL; + _type = NULL; +} + +Cursor_Point::Cursor_Point ( nframes_t when, const char *type, const char *label ) +{ +// _make_label(); + + _label = NULL; + _type = NULL; + + this->label( label ); + this->type( type ); + + timeline->add_cursor( this ); + + start( when ); + + log_create(); +} + +Cursor_Point::Cursor_Point ( const Cursor_Point &rhs ) : Sequence_Point( rhs ) +{ + label( rhs.label() ); + type( rhs.type() ); + + log_create(); +} + +Cursor_Point::~Cursor_Point ( ) +{ +// sequence()->remove( this ); + + log_destroy(); + + label(NULL); + type(NULL); +} + + + +void +Cursor_Point::get ( Log_Entry &e ) const +{ +// Sequence_Point::get( e ); + + e.add( ":start", start() ); + e.add( ":label", label() ); + e.add( ":type", type() ); +} + +void +Cursor_Point::set ( Log_Entry &e ) +{ + + Sequence_Point::set( e ); + + for ( int i = 0; i < e.size(); ++i ) + { + const char *s, *v; + + e.get( i, &s, &v ); + + if ( ! strcmp( s, ":label" ) ) + label( v ); + else if ( ! strcmp( s, ":type" )) + { + type( v ); + + timeline->add_cursor( this ); + } + +/* /\* FIXME: we need to add this to the time track on creation!!! *\/ */ +/* timeline->time_track->add( this ); */ + + } + + sequence()->handle_widget_change( start(), length() ); + +// _make_label(); +} + + + +void +Cursor_Point::log_children ( void ) const +{ + log_create(); +} + + +int +Cursor_Point::handle ( int m ) +{ + Logger log( this ); + + /* if ( m == FL_PUSH && Fl::event_button3() && ! ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) ) ) */ + /* { */ + + /* time_sig t = _time; */ + + /* edit( &t ); */ + + /* time( t.beats_per_bar, t.beat_type ); */ + + /* return 0; */ + + /* } */ + + return Sequence_Point::handle( m ); +} + + diff --git a/timeline/src/Cursor_Point.H b/timeline/src/Cursor_Point.H new file mode 100644 index 0000000..7f2c342 --- /dev/null +++ b/timeline/src/Cursor_Point.H @@ -0,0 +1,65 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 "Sequence_Point.H" + + +class Cursor_Point : public Sequence_Point +{ + char *_type; + +protected: + +// const char *class_name ( void ) { return "Time_Point"; } + + virtual void get ( Log_Entry &e ) const; + void set ( Log_Entry &e ); + void log_children ( void ) const; + + Cursor_Point ( ); + +public: + + LOG_CREATE_FUNC( Cursor_Point ); + SEQUENCE_WIDGET_CLONE_FUNC( Cursor_Point ); + +// static bool edit ( time_sig *sig ); + + Cursor_Point ( nframes_t when, const char *type, const char *label ); + Cursor_Point ( const Cursor_Point &rhs ); + + ~Cursor_Point ( ); + + const char * type ( void ) const { return _type; } + void type ( const char *v ) + { + if ( _type ) + free( _type ); + + _type = NULL; + + if ( v ) + _type = strdup( v ); + } + + int handle ( int m ); + +}; diff --git a/timeline/src/Cursor_Region.C b/timeline/src/Cursor_Region.C new file mode 100644 index 0000000..3fcbcd1 --- /dev/null +++ b/timeline/src/Cursor_Region.C @@ -0,0 +1,154 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 +#include +#include + +#include "Cursor_Region.H" +#include "Cursor_Sequence.H" +#include "Timeline.H" + +Fl_Color Cursor_Region::box_color ( void ) const +{ + return ((Cursor_Sequence*)sequence())->cursor_color(); +} + + +void Cursor_Region::box_color ( Fl_Color c ) +{ + ((Cursor_Sequence*)sequence())->cursor_color( c ); +} + + + +void +Cursor_Region::get ( Log_Entry &e ) const +{ +// Sequence_Region::get( e ); + e.add( ":start", start() ); + e.add( ":length", length() ); + e.add( ":label", label() ); + e.add( ":type", type() ); +} + +void +Cursor_Region::set ( Log_Entry &e ) +{ + Sequence_Region::set( e ); + + for ( int i = 0; i < e.size(); ++i ) + { + const char *s, *v; + + e.get( i, &s, &v ); + + if ( ! strcmp( s, ":label" ) ) + label( v ); + if ( ! strcmp( s, ":type" ) ) + { + type( v ); + timeline->add_cursor( this ); + } + } + +// timeline->redraw(); +} + +Cursor_Region::Cursor_Region ( nframes_t when, nframes_t length, const char *type, const char *label ) +{ + _label = NULL; + _type = NULL; + + this->label( label ); + this->type( type ); + + timeline->add_cursor( this ); + + start( when ); + + this->length( length ); + + log_create(); +} + +Cursor_Region::Cursor_Region ( const Cursor_Region &rhs ) : Sequence_Region( rhs ) +{ + _label = strdup( rhs._label ); + _type = strdup( rhs._type ); + + log_create(); +} + + +Cursor_Region::~Cursor_Region ( ) +{ +// timeline->cursor_track->remove( this ); + + log_destroy(); + + label(NULL); + type(NULL); +} + +void +Cursor_Region::draw_box ( void ) +{ + Sequence_Region::draw_box(); +} + +void +Cursor_Region::draw ( void ) +{ + draw_label( _label, (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP | FL_ALIGN_CLIP ) ); +} + +#include "FL/Fl_Text_Edit_Window.H" +#include "FL/test_press.H" + +int +Cursor_Region::handle ( int m ) +{ + Logger _log( this ); + + if ( m == FL_PUSH ) + { + if ( test_press( FL_BUTTON3 ) ) + { + char *s = fl_text_edit( "Cursor text:", "&Save", label() ); + + if ( s ) + label( s ); + + free( s ); + + return 0; + } + } + + int r = Sequence_Region::handle( m ); + + if ( m == FL_RELEASE ) + { + sequence()->sort(); + timeline->redraw(); + } + + return r; +} diff --git a/timeline/src/Cursor_Region.H b/timeline/src/Cursor_Region.H new file mode 100644 index 0000000..3290867 --- /dev/null +++ b/timeline/src/Cursor_Region.H @@ -0,0 +1,74 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 "Sequence_Region.H" +#include "Cursor_Sequence.H" + +class Cursor_Region : public Sequence_Region +{ + /* not permitted */ + Cursor_Region & operator = ( const Cursor_Region &rhs ); + + char *_type; + +protected: + + virtual void get ( Log_Entry &e ) const; + virtual void set ( Log_Entry &e ); + + Cursor_Region ( ) + { + _label = NULL; + _type = NULL; + } + + Cursor_Region ( const Cursor_Region &rhs ); + +public: + + virtual Fl_Color box_color ( void ) const; + virtual void box_color ( Fl_Color c ); + + /* for loggable */ + LOG_CREATE_FUNC( Cursor_Region ); + SEQUENCE_WIDGET_CLONE_FUNC( Cursor_Region ); + + Cursor_Region ( nframes_t when, nframes_t length, const char *type, const char *label ); + virtual ~Cursor_Region ( ); + + void draw_box ( void ); + void draw ( void ); + int handle ( int m ); + + + const char * type ( void ) const { return _type; } + void type ( const char *v ) + { + if ( _type ) + free( _type ); + + _type = NULL; + + if ( v ) + _type = strdup( v ); + } + +}; diff --git a/timeline/src/Cursor_Sequence.C b/timeline/src/Cursor_Sequence.C new file mode 100644 index 0000000..94bdf98 --- /dev/null +++ b/timeline/src/Cursor_Sequence.C @@ -0,0 +1,62 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 "Cursor_Sequence.H" +#include "Cursor_Point.H" +#include "Timeline.H" + +void +Cursor_Sequence::handle_widget_change ( nframes_t start, nframes_t length ) +{ + sort(); + timeline->redraw(); +} + +Sequence_Widget * +Cursor_Sequence::active_cursor ( void ) +{ + if ( _widgets.size() ) + return _widgets.front(); + else + return 0; +} + +int +Cursor_Sequence::handle ( int m ) +{ + int r = Sequence::handle( m ); + + if ( r ) + return r; + + switch ( m ) + { + case FL_PUSH: + /* if ( Fl::event_button1() ) */ + /* { */ + /* add( new Cursor_Point( timeline->x_to_offset( Fl::event_x() ), "NONE" ) ); */ + /* timeline->redraw(); */ + /* return 0; */ + /* } */ + return 0; + default: + return 0; + + } +} diff --git a/timeline/src/Cursor_Sequence.H b/timeline/src/Cursor_Sequence.H new file mode 100644 index 0000000..472386c --- /dev/null +++ b/timeline/src/Cursor_Sequence.H @@ -0,0 +1,54 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 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 "Sequence.H" +#include "Cursor_Point.H" +#include "Cursor_Region.H" + +class Cursor_Sequence : public Sequence +{ + + Fl_Color _cursor_color; + +protected: + + /* not used */ + void get ( Log_Entry & ) const { } + void set ( Log_Entry & ) { } + +public: + + Sequence_Widget *active_cursor ( void ); + + Fl_Color cursor_color ( void ) const { return _cursor_color; } + void cursor_color ( Fl_Color c ) { _cursor_color = c; } + + Fl_Cursor cursor ( void ) const { return FL_CURSOR_DEFAULT; } + + Cursor_Sequence ( int X, int Y, int W, int H ) : Sequence ( X, Y, W, H ) + { + _cursor_color = FL_CYAN; + } + + + void handle_widget_change ( nframes_t start, nframes_t length ); + int handle ( int m ); +}; diff --git a/timeline/src/Engine/Timeline.C b/timeline/src/Engine/Timeline.C index a4320ad..7f17490 100644 --- a/timeline/src/Engine/Timeline.C +++ b/timeline/src/Engine/Timeline.C @@ -26,6 +26,7 @@ #include "Playback_DS.H" #include "Thread.H" +#include "../Cursor_Sequence.H" #include @@ -34,14 +35,48 @@ bool Timeline::record ( void ) { /* FIXME: right place for this? */ + + if ( Timeline::automatically_create_takes && + ! _created_new_takes ) + { + add_take_for_armed_tracks(); + _created_new_takes = true; + } + transport->recording = true; + deactivate(); + Loggable::block_start(); nframes_t frame = transport->frame; - if ( transport->punch_enabled() && range_start() != range_end() && frame < range_start() ) - frame = range_start(); + if ( transport->punch_enabled() ) + { + const Sequence_Widget *w = punch_cursor_track->next( frame ); + + if ( w && w->start() >= frame ) + { + frame = w->start(); + _punch_out_frame = w->start() + w->length(); + } + } + + _punch_in_frame = frame; + + punch_in( frame ); + + return true; +} + +void +Timeline::punch_in ( nframes_t frame ) +{ + if ( _punched_in ) + { + WARNING( "Programming error. Attempt to punch in twice" ); + return; + } DMESSAGE( "Going to record starting at frame %lu", (unsigned long)frame ); @@ -53,20 +88,12 @@ Timeline::record ( void ) t->record_ds->start( frame ); } - deactivate(); - - return true; + _punched_in = true; } -/** stop recording for all armed tracks */ void -Timeline::stop ( void ) +Timeline::punch_out ( nframes_t frame ) { - nframes_t frame = transport->frame; - - if ( transport->punch_enabled() && range_start() != range_end() && frame > range_end() ) - frame = range_end(); - for ( int i = tracks->children(); i-- ; ) { Track *t = (Track*)tracks->child( i ); @@ -85,6 +112,27 @@ Timeline::stop ( void ) if ( t->armed() && t->record_ds ) t->record_ds->shutdown(); } + + _punched_in = false; + _punch_in_frame = 0; + _punch_out_frame = 0; +} + +/** stop recording for all armed tracks. Does not affect transport. */ +void +Timeline::stop ( void ) +{ + nframes_t frame = transport->frame; + + if ( transport->punch_enabled() ) + { + const Sequence_Widget *w = punch_cursor_track->prev( frame ); + + if ( w && w->start() + w->length() < frame ) + frame = w->start() + w->length(); + } + + punch_out( frame ); Loggable::block_end(); diff --git a/timeline/src/Project.C b/timeline/src/Project.C index f516c99..7cd1f6f 100644 --- a/timeline/src/Project.C +++ b/timeline/src/Project.C @@ -49,7 +49,7 @@ extern Transport *transport; extern TLE *tle; -const int PROJECT_VERSION = 1; +const int PROJECT_VERSION = 2; extern char *instance_name; @@ -287,9 +287,14 @@ Project::open ( const char *name ) strncmp( created_by, APP_NAME, strlen( APP_NAME ) ) ) return E_INVALID; - if ( version != PROJECT_VERSION ) + if ( version > PROJECT_VERSION ) return E_VERSION; + if ( version < 2 ) + { + /* FIXME: Version 1->2 adds Cursor_Sequence and Cursor_Point to default project... */ + } + /* normally, engine will be NULL after a close or on an initial open, but 'new' will have already created it to get the sample rate. */ if ( ! engine ) diff --git a/timeline/src/Sequence.C b/timeline/src/Sequence.C index 40f89d6..9997187 100644 --- a/timeline/src/Sequence.C +++ b/timeline/src/Sequence.C @@ -303,12 +303,20 @@ Sequence::handle ( int m ) case FL_SHORTCUT: if ( Fl::test_shortcut( FL_CTRL + FL_Right ) ) { - transport->locate( next( transport->frame ) ); + const Sequence_Widget *w = next( transport->frame ); + + if ( w ) + transport->locate( w->start() ); + return 1; } else if ( Fl::test_shortcut( FL_CTRL + FL_Left ) ) { - transport->locate( prev( transport->frame ) ); + const Sequence_Widget *w = prev( transport->frame ); + + if ( w ) + transport->locate( w->start() ); + return 1; } else if ( Fl::test_shortcut( FL_CTRL + ' ' ) ) @@ -496,29 +504,29 @@ Sequence::handle ( int m ) } /** return the location of the next widget from frame /from/ */ - nframes_t +const Sequence_Widget * Sequence::next ( nframes_t from ) const { for ( list ::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ ) if ( (*i)->start() > from ) - return (*i)->start(); + return *i; if ( _widgets.size() ) - return _widgets.back()->start(); + return _widgets.back(); else return 0; } /** return the location of the next widget from frame /from/ */ - nframes_t +const Sequence_Widget * Sequence::prev ( nframes_t from ) const { for ( list ::const_reverse_iterator i = _widgets.rbegin(); i != _widgets.rend(); i++ ) if ( (*i)->start() < from ) - return (*i)->start(); + return *i; if ( _widgets.size() ) - return _widgets.front()->start(); + return _widgets.front(); else return 0; } diff --git a/timeline/src/Sequence.H b/timeline/src/Sequence.H index 54e807a..4e6c0ef 100644 --- a/timeline/src/Sequence.H +++ b/timeline/src/Sequence.H @@ -96,8 +96,8 @@ public: void sort ( void ); void clear ( void ); - nframes_t next ( nframes_t from ) const; - nframes_t prev ( nframes_t from ) const; + const Sequence_Widget *next ( nframes_t from ) const; + const Sequence_Widget *prev ( nframes_t from ) const; Track *track ( void ) const { return _track; } void track ( Track *t ) { _track = t; } diff --git a/timeline/src/Sequence_Point.H b/timeline/src/Sequence_Point.H index f87a06e..715a0b9 100644 --- a/timeline/src/Sequence_Point.H +++ b/timeline/src/Sequence_Point.H @@ -28,8 +28,6 @@ class Sequence_Point : public Sequence_Widget protected: - char *_label; - void get ( Log_Entry &e ) const; void set ( Log_Entry &e ); @@ -43,8 +41,8 @@ protected: public: - const char *name ( void ) const { return _label; } - void name ( const char *s ) + const char *label ( void ) const { return _label; } + void label ( const char *s ) { if ( _label ) free( _label ); diff --git a/timeline/src/Sequence_Region.C b/timeline/src/Sequence_Region.C index 8ecd82f..8f8bc5d 100644 --- a/timeline/src/Sequence_Region.C +++ b/timeline/src/Sequence_Region.C @@ -248,6 +248,7 @@ void Sequence_Region::draw_box ( void ) { Fl_Color c = selected() ? selection_color() : box_color(); + fl_draw_box( box(), line_x(), y(), abs_w(), h(), fl_color_add_alpha( c, 127 ) ); } diff --git a/timeline/src/Sequence_Widget.C b/timeline/src/Sequence_Widget.C index b8b3722..87bb4b4 100644 --- a/timeline/src/Sequence_Widget.C +++ b/timeline/src/Sequence_Widget.C @@ -39,6 +39,7 @@ Fl_Color Sequence_Widget::_selection_color = FL_MAGENTA; Sequence_Widget::Sequence_Widget ( ) { + _label = 0; _sequence = NULL; _r = &_range; @@ -56,6 +57,9 @@ Sequence_Widget::Sequence_Widget ( const Sequence_Widget &rhs ) : Loggable( rhs { _drag = NULL; + if ( rhs._label ) + _label = strdup( rhs._label ); + _sequence = rhs._sequence; _range = rhs._range; @@ -77,6 +81,9 @@ Sequence_Widget::operator= ( const Sequence_Widget &rhs ) _box_color = rhs._box_color; _color = rhs._color; + if ( rhs._label ) + _label = strdup( rhs._label ); + return *this; } @@ -90,6 +97,8 @@ Sequence_Widget::~Sequence_Widget ( ) if ( this == _belowmouse ) _belowmouse = NULL; + label( NULL ); + _sequence->remove( this ); _selection.remove( this ); diff --git a/timeline/src/Sequence_Widget.H b/timeline/src/Sequence_Widget.H index 8da354e..dc5d511 100644 --- a/timeline/src/Sequence_Widget.H +++ b/timeline/src/Sequence_Widget.H @@ -136,6 +136,8 @@ class Sequence_Widget : public Loggable protected: + + char *_label; Sequence *_sequence; /* track this region belongs to */ @@ -208,7 +210,7 @@ public: /* use this as x() when you need to draw lines between widgets */ int line_x ( void ) const { - return _r->start < timeline->xoffset ? max( -32768, _sequence->x() - timeline->ts_to_x( timeline->xoffset - _r->start )) : min( 32767, _sequence->x() + timeline->ts_to_x( _r->start - timeline->xoffset ) ); + return _r->start < timeline->xoffset ? max( -32767, _sequence->x() - timeline->ts_to_x( timeline->xoffset - _r->start )) : min( 32767, _sequence->x() + timeline->ts_to_x( _r->start - timeline->xoffset ) ); } virtual int w ( void ) const @@ -229,8 +231,8 @@ public: Fl_Color color ( void ) const { return _color; } void color ( Fl_Color v ) { _color = v; } - Fl_Color box_color ( void ) const { return _box_color; } - void box_color ( Fl_Color v ) { _box_color = v; } + virtual Fl_Color box_color ( void ) const { return _box_color; } + virtual void box_color ( Fl_Color v ) { _box_color = v; } virtual Fl_Color selection_color ( void ) const { return _selection_color; } virtual void selection_color ( Fl_Color v ) { _selection_color = v; } @@ -242,12 +244,28 @@ public: /* void start ( nframes_t o ) { _r->start = o; } */ void start ( nframes_t where ); - void length ( nframes_t v ) { _r->length = v; } virtual nframes_t length ( void ) const { return _r->length; } void offset ( nframes_t v ) { _r->offset = v; } nframes_t offset ( void ) const { return _r->offset; } + void set_left ( nframes_t v ) { _r->set_left( v ); } + void set_right ( nframes_t v ) { _r->set_right( v ); } + + const char *label ( void ) const { return _label; } + void label ( const char *s ) + { + if ( _label ) + free( _label ); + + _label = NULL; + + if ( s ) + _label = strdup( s ); + + redraw(); + } + /** convert a screen x coord into an start into the region */ nframes_t x_to_offset ( int X ) { diff --git a/timeline/src/TLE.fl b/timeline/src/TLE.fl index 73b12f7..43e3dcb 100644 --- a/timeline/src/TLE.fl +++ b/timeline/src/TLE.fl @@ -504,6 +504,13 @@ Project::compact();} timeline->redraw();} xywh {15 15 40 25} type Toggle value 1 } + MenuItem {} { + label {&Cursor Overlay} + callback {Timeline::draw_with_cursor_overlay = menu_picked_value( o ); + +timeline->redraw();} + xywh {15 14 40 25} type Toggle value 1 + } } Submenu {} { label {&Waveform} open @@ -594,6 +601,16 @@ timeline->redraw();} callback {transport->stop_disables_record( ((Fl_Menu_*)o)->mvalue()->flags & FL_MENU_VALUE );} xywh {10 10 40 25} type Toggle value 1 } + MenuItem {} { + label {Loop Playback} + callback {timeline->loop_playback = ( ((Fl_Menu_*)o)->mvalue()->flags & FL_MENU_VALUE );} + xywh {10 10 40 25} type Toggle value 0 + } + MenuItem {} { + label {Automatically Create Takes} + callback {timeline->automatically_create_takes = ( ((Fl_Menu_*)o)->mvalue()->flags & FL_MENU_VALUE );} + xywh {10 10 40 25} type Toggle value 0 + } } } } diff --git a/timeline/src/Timeline.C b/timeline/src/Timeline.C index 085f1ed..b488c10 100644 --- a/timeline/src/Timeline.C +++ b/timeline/src/Timeline.C @@ -32,6 +32,7 @@ #include "Timeline.H" #include "Tempo_Sequence.H" #include "Time_Sequence.H" +#include "Cursor_Sequence.H" #include "Audio_Sequence.H" #include "Control_Sequence.H" #include "Scalebar.H" @@ -55,6 +56,8 @@ #include "OSC_Thread.H" #include "OSC/Endpoint.H" +#include + #include extern nsm_client_t *nsm; @@ -78,11 +81,14 @@ extern nsm_client_t *nsm; bool Timeline::draw_with_measure_lines = true; +bool Timeline::draw_with_cursor_overlay = true; Timeline::snap_e Timeline::snap_to = Bars; bool Timeline::snapping_on_hold = false; bool Timeline::snap_magnetic = true; bool Timeline::follow_playhead = true; bool Timeline::center_playhead = true; +bool Timeline::loop_playback = false; +bool Timeline::automatically_create_takes = false; const float UPDATE_FREQ = 1.0 / 18.0f; @@ -138,6 +144,72 @@ draw_full_arrow_symbol ( Fl_Color color ) +nframes_t +Timeline::range_start ( void ) const +{ + if ( edit_cursor_track->active_cursor() ) + return edit_cursor_track->active_cursor()->start(); + else + return 0; +} + +nframes_t +Timeline::range_end ( void ) const +{ + if ( edit_cursor_track->active_cursor() ) + return edit_cursor_track->active_cursor()->start() + edit_cursor_track->active_cursor()->length(); + else + return 0; +} + +void +Timeline::range_start ( nframes_t n ) +{ + if ( ! edit_cursor_track->active_cursor() ) + new Cursor_Region( 0, 0, "Edit", NULL ); + + Logger log( edit_cursor_track->active_cursor() ); + + edit_cursor_track->active_cursor()->set_left( n ); +} + +void +Timeline::range_end ( nframes_t n ) +{ + if ( ! edit_cursor_track->active_cursor() ) + new Cursor_Region( 0, 0, "Edit", NULL ); + + Logger log( edit_cursor_track->active_cursor() ); + + edit_cursor_track->active_cursor()->set_right( n ); +} + +/** return first frame of playback (might not be 0) */ +nframes_t +Timeline::playback_home ( void ) const +{ + if ( play_cursor_track->active_cursor() ) + return play_cursor_track->active_cursor()->start(); + else + return 0; +} + +/** return last frame of playback */ +nframes_t +Timeline::playback_end ( void ) const +{ + if ( play_cursor_track->active_cursor() ) + return play_cursor_track->active_cursor()->start() + play_cursor_track->active_cursor()->length(); + else + return length(); +} + +void +Timeline::reset_range ( void ) +{ + delete edit_cursor_track->active_cursor(); +} + /** callback used by Loggable class to create a snapshot of system * state. */ void @@ -145,6 +217,9 @@ Timeline::snapshot ( void ) { tempo_track->log_children(); time_track->log_children(); + edit_cursor_track->log_children(); + punch_cursor_track->log_children(); + play_cursor_track->log_children(); for ( int i = 0; i < tracks->children(); ++i ) { @@ -211,15 +286,15 @@ Timeline::menu_cb ( Fl_Widget *w, void *v ) ((Timeline*)v)->menu_cb( (Fl_Menu_*)w ); } -/** ensure that p1 is less than p2 */ +/** ensure that p1 is less than range_end() */ void Timeline::fix_range ( void ) { - if ( p1 > p2 ) + if ( range_start() > range_end() ) { - nframes_t t = p2; - p2 = p1; - p1 = t; + nframes_t t = range_end(); + range_end( range_start() ); + range_start( t ); } } @@ -227,12 +302,25 @@ Timeline::fix_range ( void ) void Timeline::range ( nframes_t start, nframes_t length ) { - p1 = start; - p2 = start + length; + range_start( start ); + range_end( start + length ); redraw(); } +/** create a new take for every armed track */ +void +Timeline::add_take_for_armed_tracks ( void ) +{ + for ( int i = tracks->children(); i-- ; ) + { + Track *t = (Track*)tracks->child( i ); + + if ( t->armed() && t->sequence()->_widgets.size() ) + t->sequence( new Audio_Sequence( t ) ); + } +} + void Timeline::menu_cb ( Fl_Menu_ *m ) { @@ -245,7 +333,7 @@ Timeline::menu_cb ( Fl_Menu_ *m ) DMESSAGE( "%s", picked ); - if ( ! strcmp( picked, "Add Audio Track" ) ) + if ( ! strcmp( picked, "Add audio track" ) ) { /* FIXME: prompt for I/O config? */ @@ -266,28 +354,28 @@ Timeline::menu_cb ( Fl_Menu_ *m ) Loggable::block_end(); } - else if ( ! strcmp( picked, "Tempo from range (beat)" ) ) + else if ( ! strcmp( picked, "Tempo from edit (beat)" ) ) { - if ( p1 != p2 ) + if ( range_start() != range_end() ) { fix_range(); - beats_per_minute( p1, sample_rate() * 60 / (float)( p2 - p1 ) ); + beats_per_minute( range_start(), sample_rate() * 60 / (float)( range_end() - range_start() ) ); - p2 = p1; + range_end( range_start() ); } } - else if ( ! strcmp( picked, "Tempo from range (bar)" ) ) + else if ( ! strcmp( picked, "Tempo from edit (bar)" ) ) { - if ( p1 != p2 ) + if ( range_start() != range_end() ) { fix_range(); - position_info pi = solve_tempomap( p1 ); + position_info pi = solve_tempomap( range_start() ); - beats_per_minute( p1, sample_rate() * 60 / (float)( ( p2 - p1 ) / pi.beats_per_bar ) ); + beats_per_minute( range_start(), sample_rate() * 60 / (float)( ( range_end() - range_start() ) / pi.beats_per_bar ) ); - p2 = p1; + range_end( range_start() ); } } else if ( ! strcmp( picked, "Playhead to mouse" ) ) @@ -299,13 +387,13 @@ Timeline::menu_cb ( Fl_Menu_ *m ) transport->locate( xoffset + x_to_ts( X ) ); } } - else if ( ! strcmp( picked, "P1 to mouse" ) ) + else if ( ! strcmp( picked, "Edit start to mouse" ) ) { int X = Fl::event_x() - Track::width(); if ( X > 0 ) { - p1 = xoffset + x_to_ts( X ); + range_start( xoffset + x_to_ts( X ) ); } fix_range(); @@ -313,13 +401,13 @@ Timeline::menu_cb ( Fl_Menu_ *m ) /* FIXME: only needs to damage the location of the old cursor! */ redraw(); } - else if ( ! strcmp( picked, "P2 to mouse" ) ) + else if ( ! strcmp( picked, "Edit end to mouse" ) ) { int X = Fl::event_x() - Track::width(); if ( X > 0 ) { - p2 = xoffset + x_to_ts( X ); + range_end( xoffset + x_to_ts( X ) ); } fix_range(); @@ -355,35 +443,72 @@ Timeline::menu_cb ( Fl_Menu_ *m ) if ( next_line( &f, true ) ) transport->locate( f ); } - else if ( ! strcmp( picked, "Swap P1 and playhead" ) ) + else if ( ! strcmp( picked, "Swap edit start and playhead" ) ) { nframes_t t = transport->frame; - transport->locate( p1 ); + transport->locate( range_start() ); - p1 = t; + range_start( t ); redraw(); } - else if ( ! strcmp( picked, "Swap P2 and playhead" ) ) + else if ( ! strcmp( picked, "Swap edit end and playhead" ) ) { nframes_t t = transport->frame; - transport->locate( p2 ); + transport->locate( range_end() ); + + range_end( t ); + + redraw(); + } + else if ( ! strcmp( picked, "Edit start to playhead" ) ) + { + range_start( transport->frame ); - p2 = t; + redraw(); + } + else if ( ! strcmp( picked, "Edit end to playhead" ) ) + { + range_end( transport->frame ); redraw(); } - else if ( ! strcmp( picked, "P1 to playhead" ) ) + else if ( ! strcmp( picked, "Punch from edit" ) ) { - p1 = transport->frame; + if ( range_start() != range_end() ) + { + Loggable::block_start(); + + Cursor_Region *o = new Cursor_Region( range_start(), range_end() - range_start(), "Punch", NULL ); + reset_range(); + + Loggable::block_end(); + } redraw(); } - else if ( ! strcmp( picked, "P2 to playhead" ) ) + else if ( ! strcmp( picked, "Playback from edit" ) ) { - p2 = transport->frame; + if ( range_start() != range_end() ) + { + Loggable::block_start(); + + if ( play_cursor_track->active_cursor() ) + { + play_cursor_track->active_cursor()->start( range_start() ); + play_cursor_track->active_cursor()->set_right( range_end() ); + } + else + { + Cursor_Region *o = new Cursor_Region( range_start(), range_end() - range_start(), "Playback", NULL ); + } + + reset_range(); + + Loggable::block_end(); + } redraw(); } @@ -412,6 +537,14 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W { Loggable::snapshot_callback( &Timeline::snapshot, this ); + edit_cursor_track = NULL; + punch_cursor_track = NULL; + play_cursor_track = NULL; + + _created_new_takes = 0; + _punched_in = 0; + _punch_in_frame = 0; + _punch_out_frame = 0; osc_thread = 0; _sample_rate = 44100; @@ -425,26 +558,28 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W X = Y = 0; #endif - p1 = p2 = 0; +// range_start( range_end( 0 ) ); menu = new Fl_Menu_Button( 0, 0, 0, 0, "Timeline" ); /* menu->add( "Add Track", 0, 0, 0 ); */ - menu->add( "Add Audio Track", 'a', 0, 0 ); - menu->add( "Tempo from range (beat)", 't', 0, 0 ); - menu->add( "Tempo from range (bar)", FL_CTRL + 't', 0, 0 ); + menu->add( "Add audio track", 'a', 0, 0 ); + menu->add( "Tempo from edit (beat)", 't', 0, 0 ); + menu->add( "Tempo from edit (bar)", FL_CTRL + 't', 0, 0 ); menu->add( "Playhead to mouse", 'p', 0, 0 ); - menu->add( "P1 to mouse", '[', 0, 0 ); - menu->add( "P2 to mouse", ']', 0, 0 ); + menu->add( "Edit start to mouse", '[', 0, 0 ); + menu->add( "Edit end to mouse", ']', 0, 0 ); menu->add( "Playhead left beat", FL_SHIFT + FL_Left, 0, 0 ); menu->add( "Playhead right beat", FL_SHIFT + FL_Right, 0, 0 ); menu->add( "Playhead left bar", FL_CTRL + FL_SHIFT + FL_Left, 0, 0 ); menu->add( "Playhead right bar", FL_CTRL + FL_SHIFT + FL_Right, 0, 0 ); - menu->add( "Swap P1 and playhead", FL_CTRL + FL_SHIFT + '[', 0, 0 ); - menu->add( "Swap P2 and playhead", FL_CTRL + FL_SHIFT + ']', 0, 0 ); - menu->add( "P1 to playhead", FL_CTRL + '[', 0, 0 ); - menu->add( "P2 to playhead", FL_CTRL + ']', 0, 0 ); + menu->add( "Swap edit start and playhead", FL_CTRL + FL_SHIFT + '[', 0, 0 ); + menu->add( "Swap edit end and playhead", FL_CTRL + FL_SHIFT + ']', 0, 0 ); + menu->add( "Edit start to playhead", FL_CTRL + '[', 0, 0 ); + menu->add( "Edit end to playhead", FL_CTRL + ']', 0, 0 ); + menu->add( "Punch from edit", FL_CTRL + FL_SHIFT + 'p', 0, 0 ); + menu->add( "Playback from edit", FL_CTRL + FL_SHIFT + 'l', 0, 0 ); menu->add( "Redraw", FL_CTRL + 'l', 0, 0 ); menu_set_callback( const_cast(menu->menu()), &Timeline::menu_cb, (void*)this ); @@ -478,10 +613,11 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W o->type( Fl_Pack::VERTICAL ); { - Tempo_Sequence *o = new Tempo_Sequence( 0, 0, 800, 24 ); + Tempo_Sequence *o = new Tempo_Sequence( 0, 0, 800, 18 ); - o->color( fl_gray_ramp( 18 ) ); + o->color( FL_GRAY ); + o->labelsize( 12 ); o->label( "Tempo" ); o->align( FL_ALIGN_LEFT ); @@ -489,16 +625,55 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W } { - Time_Sequence *o = new Time_Sequence( 0, 24, 800, 24 ); + Time_Sequence *o = new Time_Sequence( 0, 24, 800, 18 ); - o->color( fl_gray_ramp( 16 ) ); + o->color( fl_lighter( FL_GRAY ) ); + o->labelsize( 12 ); o->label( "Time" ); o->align( FL_ALIGN_LEFT ); time_track = o; } + { + Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 ); + + o->color( FL_GRAY ); + + o->labelsize( 12 ); + o->label( "Edit" ); + o->align( FL_ALIGN_LEFT ); + o->cursor_color( FL_YELLOW ); + + edit_cursor_track = o; + } + + { + Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 ); + + o->color( fl_lighter( FL_GRAY ) ); + + o->labelsize( 12 ); + o->label( "Punch" ); + o->align( FL_ALIGN_LEFT ); + o->cursor_color( FL_RED ); + + punch_cursor_track = o; + } + + { + Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 ); + + o->color( FL_GRAY ); + + o->labelsize( 12 ); + o->label( "Playback" ); + o->align( FL_ALIGN_LEFT ); + o->cursor_color( FL_GREEN ); + + play_cursor_track = o; + } /* { */ /* Annotation_Sequence *o = new Annotation_Sequence( 0, 24, 800, 24 ); */ @@ -511,8 +686,8 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W /* ruler_track = o; */ /* } */ - o->size( o->w(), o->child( 0 )->h() * o->children() ); rulers = o; + o->size( o->w(), o->child( 0 )->h() * o->children() ); o->end(); } @@ -893,6 +1068,8 @@ Timeline::draw_clip ( void * v, int X, int Y, int W, int H ) fl_push_clip( tl->tracks->x(), tl->rulers->y() + tl->rulers->h(), tl->tracks->w(), tl->h() - tl->rulers->h() - tl->hscroll->h() ); tl->draw_child( *tl->tracks ); + tl->draw_cursors(); + fl_pop_clip(); fl_pop_clip(); @@ -914,20 +1091,83 @@ Timeline::resize ( int X, int Y, int W, int H ) tracks->resize( BX, BY + rulers->h(), W - vscroll->w(), H - vscroll->h() ); } -/** draw ancillary cursors (not necessarily in the overlay plane) */ + void -Timeline::draw_cursors ( void ) const +Timeline::add_cursor ( Cursor_Region *o ) +{ + if ( !strcmp( o->type(), "Edit" ) ) + { + DMESSAGE( "Adding cursor to edit track" ); + edit_cursor_track->add( o ); + } + else if ( !strcmp( o->type(), "Punch" ) ) + { + DMESSAGE( "Adding cursor to punch track" ); + punch_cursor_track->add( o ); + } + else if ( !strcmp( o->type(), "Playback" ) ) + { + DMESSAGE( "Adding cursor to punch track" ); + play_cursor_track->add( o ); + } + +} + +void +Timeline::add_cursor ( Cursor_Point *o ) { - if ( p1 != p2 ) + if ( !strcmp( o->type(), "Edit" ) ) + edit_cursor_track->add( o ); + else if ( !strcmp( o->type(), "Punch" ) ) + punch_cursor_track->add( o ); +} + +void +Timeline::draw_cursors ( Cursor_Sequence *o ) const +{ + fl_push_clip( tracks->x() + Track::width(), rulers->y() + rulers->h(), tracks->w() - Track::width(), h() - rulers->h() - hscroll->h() ); + + if ( o && o->_widgets.size() > 0 ) { - draw_cursor( p1, FL_BLUE, draw_full_arrow_symbol ); - draw_cursor( p2, FL_GREEN, draw_full_arrow_symbol ); + for ( std::list::const_iterator i = o->_widgets.begin(); + i != o->_widgets.end(); + i++ ) + { + if ( Timeline::draw_with_cursor_overlay ) + { + fl_color( fl_color_add_alpha( (*i)->box_color(), 50 ) ); + + fl_rectf( (*i)->line_x(), tracks->y(), (*i)->abs_w(), tracks->h() ); + } + + fl_color( fl_color_add_alpha( (*i)->box_color(), 127 )); + + fl_line( (*i)->line_x(), tracks->y(), (*i)->line_x(), 9000 ); + + fl_line( (*i)->line_x() + (*i)->abs_w(), tracks->y(), (*i)->line_x() + (*i)->abs_w(), tracks->h() ); + } } + + fl_pop_clip(); +} + +/** draw ancillary cursors (not necessarily in the overlay plane) */ +void +Timeline::draw_cursors ( void ) const +{ + draw_cursors( edit_cursor_track ); + + if ( transport->punch_enabled() ) + draw_cursors( punch_cursor_track ); } + void Timeline::draw ( void ) { + +// resize_rulers(); + int X, Y, W, H; int bdx = 0; @@ -943,7 +1183,7 @@ Timeline::draw ( void ) #ifndef USE_UNOPTIMIZED_DRAWING if ( ( damage() & FL_DAMAGE_ALL ) ) #else - #warning Optimized drawing of timeline disabled. This will waste your CPU. +#warning Optimized drawing of timeline disabled. This will waste your CPU. #endif { DMESSAGE( "complete redraw" ); @@ -957,12 +1197,13 @@ Timeline::draw ( void ) fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), hscroll->y() - (rulers->y() + rulers->h()) ); draw_child( *tracks ); + draw_cursors(); + fl_pop_clip(); draw_child( *hscroll ); draw_child( *vscroll ); - draw_cursors(); redraw_overlay(); @@ -998,13 +1239,14 @@ Timeline::draw ( void ) { fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), h() - rulers->h() - hscroll->h() ); update_child( *tracks ); + + draw_cursors(); + fl_pop_clip(); } update_child( *hscroll ); update_child( *vscroll ); - - draw_cursors(); } done: @@ -1063,13 +1305,63 @@ Timeline::redraw_playhead ( void ) static nframes_t last_playhead = -1; static int last_playhead_x = -1; - - /* FIXME: kind of a hackish way to invoke punch stop from the UI thread... */ + /* FIXME: kind of a hackish way to invoke punch / looping stuff from the UI thread... */ if ( transport->rolling && transport->rec_enabled() && - ( ( transport->punch_enabled() && range_start() != range_end() ) && transport->frame > range_end() ) ) - transport->stop(); + transport->punch_enabled() ) + { + if ( _punched_in && + transport->frame > _punch_in_frame && + transport->frame > _punch_out_frame ) + { + punch_out( _punch_out_frame ); + } + else if ( ! _punched_in ) + { + /* we've passed one or more punch regions... punch in for the next, if available. */ + const Sequence_Widget *w = punch_cursor_track->next( transport->frame ); + + if ( w && + w->start() > transport->frame ) + { + _punch_in_frame = w->start(); + _punch_out_frame = w->start() + w->length(); + + punch_in( w->start() ); + } + } + } + + + if ( transport->rolling ) + { + if ( play_cursor_track->active_cursor() ) + { + if ( Timeline::loop_playback ) + { + if ( transport->frame > playback_end() ) + { + if ( ! seek_pending() ) + { + if ( transport->recording ) + { + stop(); + transport->locate( playback_home() ); + record(); + } + else + { + transport->locate( playback_home() ); + } + } + } + } + else + if ( transport->frame > playback_end() ) + transport->stop(); + } + } int playhead_x = ts_to_x( transport->frame ); @@ -1314,8 +1606,8 @@ Timeline::handle ( int m ) if ( range ) { - p1 = x_to_offset( _selection.x ); - p2 = x_to_offset( _selection.x + _selection.w ); + range_start( x_to_offset( _selection.x ) ); + range_end( x_to_offset( _selection.x + _selection.w ) ); redraw(); } @@ -1331,8 +1623,8 @@ Timeline::handle ( int m ) if ( range ) { - p1 = x_to_offset( _selection.x ); - p2 = x_to_offset( _selection.x + _selection.w ); + range_start( x_to_offset( _selection.x ) ); + range_end( x_to_offset( _selection.x + _selection.w ) ); redraw(); } else @@ -1608,35 +1900,35 @@ Timeline::remove_track ( Track *track ) void Timeline::command_quit ( ) { - Project::close(); + Project::close(); - command_save(); + command_save(); - while ( Fl::first_window() ) Fl::first_window()->hide(); + while ( Fl::first_window() ) Fl::first_window()->hide(); } bool Timeline::command_load ( const char *name, const char *display_name ) { - if ( ! name ) - return false; + if ( ! name ) + return false; - int r = Project::open( name ); + int r = Project::open( name ); - if ( r < 0 ) - { + if ( r < 0 ) + { const char *s = Project::errstr( r ); fl_alert( "Could not open project \"%s\":\n\n\t%s", name, s ); return false; - } + } - Project::set_name ( display_name ? display_name : name ); + Project::set_name ( display_name ? display_name : name ); - apply_track_order(); + apply_track_order(); - return true; + return true; } bool @@ -1659,7 +1951,7 @@ Timeline::command_new ( const char *name, const char *display_name ) /* tle->main_window->redraw(); */ - return b; + return b; } const char * @@ -1748,11 +2040,11 @@ Timeline::reply_to_finger ( lo_message msg ) lo_address reply = lo_address_new_from_url( &argv[0]->s ); osc->send( reply, - "/non/hello", - osc->url(), - APP_NAME, - VERSION, - instance_name ); + "/non/hello", + osc->url(), + APP_NAME, + VERSION, + instance_name ); osc->hello( &argv[0]->s ); diff --git a/timeline/src/Timeline.H b/timeline/src/Timeline.H index 6c50256..1f2c269 100644 --- a/timeline/src/Timeline.H +++ b/timeline/src/Timeline.H @@ -48,10 +48,13 @@ struct BBT; class Tempo_Sequence; class Time_Sequence; class Annotation_Sequence; +class Cursor_Sequence; class Track; class Scalebar; class Sequence; class Sequence_Widget; +class Cursor_Region; +class Cursor_Point; namespace OSC { class Endpoint; } @@ -116,7 +119,7 @@ class Timeline : public Fl_Single_Window, public RWLock int _fpp; /* frames per pixel, power of two */ - nframes_t p1, p2; /* cursors */ +// nframes_t p1, p2; /* cursors */ nframes_t _playhead; /* not permitted */ @@ -147,19 +150,34 @@ public: None }; + /* configuration values */ static bool draw_with_measure_lines; + static bool draw_with_cursor_overlay; static snap_e snap_to; static bool snapping_on_hold; static bool snap_magnetic; static bool follow_playhead; static bool center_playhead; + static bool loop_playback; + static bool automatically_create_takes; + + + Tempo_Sequence *tempo_track; Time_Sequence *time_track; Annotation_Sequence *ruler_track; + Cursor_Sequence *edit_cursor_track; + Cursor_Sequence *punch_cursor_track; + Cursor_Sequence *play_cursor_track; Fl_Menu_Button *menu; + int _punched_in; + nframes_t _punch_out_frame; + nframes_t _punch_in_frame; + bool _created_new_takes; + nframes_t xoffset; int _yposition; @@ -177,8 +195,15 @@ public: nframes_t fpp ( void ) const { return 1 << _fpp; } void range ( nframes_t start, nframes_t length ); - nframes_t range_start ( void ) const { return p1; } - nframes_t range_end ( void ) const { return p2; } + + nframes_t range_start ( void ) const; + nframes_t range_end ( void ) const; + void range_start ( nframes_t n ); + void range_end ( nframes_t n ); + void reset_range ( void ); + nframes_t playback_home ( void ) const; + nframes_t playback_end ( void ) const; + // nframes_t playhead ( void ) const { return transport->frame; } nframes_t length ( void ) const; void sample_rate ( nframes_t r ) { _sample_rate = r; } @@ -205,6 +230,7 @@ public: void xposition ( int X ); void yposition ( int Y ); void draw_cursor ( nframes_t frame, Fl_Color color, void (*symbol)(Fl_Color) ) const; + void draw_cursors ( Cursor_Sequence *o ) const; void draw_cursors ( void ) const; void draw_playhead ( void ); void redraw_playhead ( void ); @@ -249,6 +275,8 @@ public: bool record ( void ); void stop ( void ); + void punch_in ( nframes_t frame ); + void punch_out ( nframes_t frame ); void wait_for_buffers ( void ); bool seek_pending ( void ); @@ -274,8 +302,13 @@ public: void reply_to_finger ( lo_message msg ); -private: + void add_cursor ( Cursor_Region *o ); + void add_cursor ( Cursor_Point *o ); +private: + + void add_take_for_armed_tracks(); + void resize_rulers ( void ); static void snapshot ( void *v ) { ((Timeline*)v)->snapshot(); } void snapshot ( void ); diff --git a/timeline/src/Transport.C b/timeline/src/Transport.C index 7490271..ea531a5 100644 --- a/timeline/src/Transport.C +++ b/timeline/src/Transport.C @@ -84,7 +84,7 @@ Transport::Transport ( int X, int Y, int W, int H, const char *L ) o->shortcut( 'P' ); o->callback( cb_button, this ); o->when( FL_WHEN_CHANGED ); - o->color2( FL_GREEN ); + o->color2( FL_RED ); o->box( FL_UP_BOX ); end(); @@ -135,13 +135,15 @@ void Transport::cb_button ( Fl_Widget *w ) { if ( w == _home_button ) - locate( 0 ); + locate( timeline->playback_home() ); else if ( w == _end_button ) - locate( timeline->length() ); + locate( timeline->playback_end() ); else if ( w == _play_button ) toggle(); else if ( w == _record_button ) update_record_state(); + else if ( w == _punch_button ) + timeline->redraw(); } void @@ -196,8 +198,15 @@ Transport::locate ( nframes_t frame ) return; if ( ! recording ) + { // don't allow seeking while record is in progress engine->transport_locate( frame ); + + /* so there isn't a race waiting for the transport to sync */ + this->frame = frame; + } + + timeline->_created_new_takes = false; } @@ -224,6 +233,8 @@ Transport::stop ( void ) if ( _stop_disables_record ) _record_button->value( 0 ); + timeline->_created_new_takes = false; + update_record_state(); } diff --git a/timeline/src/main.C b/timeline/src/main.C index 00d4868..61457e6 100644 --- a/timeline/src/main.C +++ b/timeline/src/main.C @@ -37,6 +37,7 @@ #include "Time_Sequence.H" #include "Annotation_Sequence.H" #include "Control_Sequence.H" +#include "Cursor_Sequence.H" #include "Track.H" #include "TLE.H" @@ -184,6 +185,8 @@ main ( int argc, char **argv ) LOG_REGISTER_CREATE( Control_Sequence ); LOG_REGISTER_CREATE( Tempo_Point ); LOG_REGISTER_CREATE( Time_Point ); + LOG_REGISTER_CREATE( Cursor_Point ); + LOG_REGISTER_CREATE( Cursor_Region ); LOG_REGISTER_CREATE( Track ); signal( SIGPIPE, SIG_IGN );