| @@ -90,6 +90,35 @@ unescape ( char *s ) | |||
| *r = '\0'; | |||
| } | |||
| /** return a dynamically allocated string representing this log entry */ | |||
| char * | |||
| Log_Entry::print ( void ) const | |||
| { | |||
| /* FIXME: gross over-allocation */ | |||
| char *r = (char*)malloc( 1024 ); | |||
| r[0] = 0; | |||
| for ( int i = 0; i < size(); ++i ) | |||
| { | |||
| const char *s, *v; | |||
| get( i, &s, &v ); | |||
| /* FIXME: arbitrary limit */ | |||
| char t[1024]; | |||
| snprintf( t, sizeof( t ), "%s %s%s", s, v, size() == i + 1 ? "" : " " ); | |||
| strcat( r, t ); | |||
| } | |||
| char *r2 = (char*)malloc( strlen( r ) + 1 ); | |||
| strcpy( r2, r ); | |||
| return r2; | |||
| } | |||
| /** sigh. parse a string of ":name value :name value" pairs into an | |||
| * array of strings, one per pair */ | |||
| // FIXME: doesn't handle the case of :name ":foo bar", nested quotes | |||
| @@ -67,6 +67,8 @@ public: | |||
| void get ( int n, const char **name, const char **value ) const; | |||
| char **sa ( void ); | |||
| char *print ( void ) const; | |||
| /* #define ADD ( type, format, exp ) \ */ | |||
| /* void add ( const char *name, type v ) \ */ | |||
| /* { \ */ | |||
| @@ -50,6 +50,8 @@ off_t Loggable::_undo_offset = 0; | |||
| size_t Loggable::_loggables_size = 0; | |||
| Loggable ** Loggable::_loggables; | |||
| std::map <unsigned int, Log_Entry *> Loggable::_loggables_unjournaled; | |||
| std::map <std::string, create_func*> Loggable::_class_map; | |||
| std::queue <char *> Loggable::_transaction; | |||
| @@ -61,6 +63,14 @@ void *Loggable::_snapshot_callback_arg = NULL; | |||
| Loggable::~Loggable ( ) | |||
| { | |||
| _loggables[ _id - 1 ] = NULL; | |||
| } | |||
| /** ensure that _loggables array is big enough for /n/ elements */ | |||
| void | |||
| Loggable::ensure_size ( size_t n ) | |||
| @@ -121,6 +131,8 @@ Loggable::open ( const char *filename ) | |||
| return false; | |||
| } | |||
| load_unjournaled_state(); | |||
| if ( newer( "snapshot", filename ) ) | |||
| { | |||
| MESSAGE( "Loading snapshot" ); | |||
| @@ -146,6 +158,33 @@ Loggable::open ( const char *filename ) | |||
| return true; | |||
| } | |||
| bool | |||
| Loggable::load_unjournaled_state ( void ) | |||
| { | |||
| FILE *fp; | |||
| fp = fopen( "unjournaled", "r" ); | |||
| if ( ! fp ) | |||
| return false; | |||
| unsigned int id; | |||
| char buf[BUFSIZ]; | |||
| while ( fscanf( fp, "%X set %[^\n]\n", &id, buf ) == 2 ) | |||
| { | |||
| Log_Entry *e = new Log_Entry( buf ); | |||
| _loggables_unjournaled[ id - 1 ] = e; | |||
| Loggable *l = Loggable::find( id ); | |||
| } | |||
| fclose( fp ); | |||
| return true; | |||
| } | |||
| #include <sys/stat.h> | |||
| #include <unistd.h> | |||
| @@ -214,11 +253,45 @@ Loggable::close ( void ) | |||
| } | |||
| } | |||
| save_unjournaled_state(); | |||
| _log_id = 0; | |||
| return true; | |||
| } | |||
| /** save out unjournaled state for all loggables */ | |||
| bool | |||
| Loggable::save_unjournaled_state ( void ) | |||
| { | |||
| /* FIXME: check for errors */ | |||
| FILE *fp = fopen( "unjournaled", "w" ); | |||
| /* write out the unjournaled state of all currently active | |||
| * loggables */ | |||
| for ( int i = 0; i < _log_id - 1; ++i ) | |||
| { | |||
| Log_Entry *e = _loggables_unjournaled[ i ]; | |||
| if ( e ) | |||
| { | |||
| char *s = e->print(); | |||
| fprintf( fp, "0x%X set %s\n", i + 1, s ); | |||
| free( s ); | |||
| } | |||
| } | |||
| /* write out the remembered state of inactive loggables. */ | |||
| fclose( fp ); | |||
| return true; | |||
| } | |||
| /** must be called after construction in create() methods */ | |||
| void | |||
| Loggable::update_id ( unsigned int id ) | |||
| @@ -338,6 +411,12 @@ Loggable::do_this ( const char *s, bool reverse ) | |||
| /* create */ | |||
| Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | |||
| l->log_create(); | |||
| /* we're now creating a loggable. Apply any unjournaled | |||
| * state it may have had in the past under this log ID */ | |||
| if ( _loggables_unjournaled[ id - 1 ] ) | |||
| l->set( *_loggables_unjournaled[ id - 1 ] ); | |||
| } | |||
| } | |||
| @@ -391,29 +470,6 @@ Loggable::undo ( void ) | |||
| _undo_offset = uo; | |||
| } | |||
| /** Make all loggable ids consecutive. This invalidates any existing | |||
| * journal or snapshot, so you *must* write out a new one after | |||
| * performing this operation*/ | |||
| void | |||
| Loggable::compact_ids ( void ) | |||
| { | |||
| unsigned int id = 0; | |||
| for ( unsigned int i = 0; i < _log_id; ++i ) | |||
| if ( _loggables[ i ] ) | |||
| { | |||
| ++id; | |||
| if ( _loggables[ id - 1 ] ) | |||
| continue; | |||
| _loggables[ id - 1 ] = _loggables[ i ]; | |||
| _loggables[ i ] = NULL; | |||
| _loggables[ id - 1 ]->_id = id; | |||
| } | |||
| _log_id = id; | |||
| } | |||
| /** write a snapshot of the current state of all loggable objects to | |||
| * file handle /fp/ */ | |||
| bool | |||
| @@ -468,7 +524,6 @@ Loggable::compact ( void ) | |||
| fseek( _fp, 0, SEEK_SET ); | |||
| ftruncate( fileno( _fp ), 0 ); | |||
| compact_ids(); | |||
| if ( ! snapshot( _fp ) ) | |||
| FATAL( "Could not write snapshot!" ); | |||
| @@ -654,11 +709,37 @@ Loggable::log_create ( void ) const | |||
| Loggable::flush(); | |||
| } | |||
| /** record this loggable's unjournaled state in memory */ | |||
| void | |||
| Loggable::record_unjournaled ( void ) const | |||
| { | |||
| Log_Entry *e = new Log_Entry(); | |||
| get_unjournaled( *e ); | |||
| Log_Entry **le = &_loggables_unjournaled[ _id - 1 ]; | |||
| if ( *le ) | |||
| delete *le; | |||
| if ( e->size() ) | |||
| { | |||
| *le = e; | |||
| DMESSAGE( "logging %s", (*le)->print() ); | |||
| } | |||
| else | |||
| /* don't waste space on loggables with no unjournaled properties */ | |||
| *le = NULL; | |||
| } | |||
| /** Log object destruction. *Must* be called at the beginning of the | |||
| * destructors of leaf classes */ | |||
| void | |||
| Loggable::log_destroy ( void ) const | |||
| { | |||
| /* the unjournaled state may have changed: make a note of it. */ | |||
| record_unjournaled(); | |||
| if ( ! _fp ) | |||
| /* tearing down... don't bother */ | |||
| return; | |||
| @@ -73,6 +73,7 @@ class Loggable | |||
| static size_t _loggables_size; | |||
| static Loggable ** _loggables; | |||
| static std::map <unsigned int, Log_Entry *> _loggables_unjournaled; | |||
| static std::map <std::string, create_func*> _class_map; | |||
| @@ -94,13 +95,14 @@ private: | |||
| static void ensure_size ( size_t n ); | |||
| void log_print( const Log_Entry *o, const Log_Entry *n ) const; | |||
| void log_print ( const Log_Entry *o, const Log_Entry *n ) const; | |||
| static void log ( const char *fmt, ... ); | |||
| static void flush ( void ); | |||
| static bool snapshot( FILE * fp ); | |||
| static bool snapshot( const char *name ); | |||
| static bool snapshot ( FILE * fp ); | |||
| static bool snapshot ( const char *name ); | |||
| static bool save_unjournaled_state ( void ); | |||
| static bool replay ( FILE *fp ); | |||
| void init ( bool loggable=true ) | |||
| @@ -125,7 +127,8 @@ private: | |||
| /* not implemented */ | |||
| const Loggable & operator= ( const Loggable &rhs ); | |||
| static void compact_ids ( void ); | |||
| void record_unjournaled ( void ) const; | |||
| static bool load_unjournaled_state ( void ); | |||
| public: | |||
| @@ -153,10 +156,7 @@ public: | |||
| void update_id ( unsigned int id ); | |||
| virtual ~Loggable ( ) | |||
| { | |||
| _loggables[ _id - 1 ] = NULL; | |||
| } | |||
| virtual ~Loggable ( ); | |||
| static | |||
| void | |||
| @@ -167,6 +167,10 @@ public: | |||
| /* log messages for journal */ | |||
| virtual void get ( Log_Entry &e ) const = 0; | |||
| virtual void get_unjournaled ( Log_Entry & ) const | |||
| { | |||
| /* implementation optional */ | |||
| } | |||
| virtual void set ( Log_Entry &e ) = 0; | |||
| virtual const char *class_name ( void ) const = 0; | |||
| @@ -176,7 +176,6 @@ Project::close ( void ) | |||
| tle->save_timeline_settings(); | |||
| Loggable::close(); | |||
| // write_info(); | |||
| _is_open = false; | |||
| @@ -73,21 +73,25 @@ Track::Track ( ) : Fl_Group( 0, 0, 1, 1 ) | |||
| timeline->add_track( this ); | |||
| } | |||
| Track::~Track ( ) | |||
| { | |||
| Loggable::block_start(); | |||
| /* must destroy sequences first to preserve proper log order */ | |||
| takes->clear(); | |||
| control->clear(); | |||
| annotation->clear(); | |||
| delete sequence(); | |||
| takes = NULL; | |||
| control = NULL; | |||
| annotation = NULL; | |||
| solo( false ); | |||
| Fl_Group::clear(); | |||
| log_destroy(); | |||
| /* ensure that soloing accounting is performed */ | |||
| solo( false ); | |||
| timeline->remove_track( this ); | |||
| /* give up our ports */ | |||
| @@ -101,6 +105,7 @@ Track::~Track ( ) | |||
| Loggable::block_end(); | |||
| } | |||
| #include "FL/Boxtypes.H" | |||
| void | |||
| @@ -258,6 +263,12 @@ Track::set ( Log_Entry &e ) | |||
| } | |||
| else if ( ! strcmp( s, ":show-all-takes" ) ) | |||
| show_all_takes( atoi( v ) ); | |||
| else if ( ! strcmp( s, ":solo" ) ) | |||
| solo( atoi( v ) ); | |||
| else if ( ! strcmp( s, ":mute" ) ) | |||
| mute( atoi( v ) ); | |||
| else if ( ! strcmp( s, ":arm" ) ) | |||
| armed( atoi( v ) ); | |||
| else if ( ! strcmp( s, ":sequence" ) ) | |||
| { | |||
| int i; | |||
| @@ -290,11 +301,19 @@ Track::get ( Log_Entry &e ) const | |||
| e.add( ":name", _name ); | |||
| e.add( ":sequence", sequence() ); | |||
| e.add( ":selected", _selected ); | |||
| e.add( ":color", (unsigned long)color()); | |||
| } | |||
| void | |||
| Track::get_unjournaled ( Log_Entry &e ) const | |||
| { | |||
| e.add( ":height", size() ); | |||
| e.add( ":inputs", input.size() ); | |||
| e.add( ":outputs", output.size() ); | |||
| e.add( ":color", (unsigned long)color()); | |||
| e.add( ":show-all-takes", _show_all_takes ); | |||
| e.add( ":armed", armed() ); | |||
| e.add( ":mute", mute() ); | |||
| e.add( ":solo", solo() ); | |||
| } | |||
| void | |||
| @@ -104,6 +104,7 @@ private: | |||
| protected: | |||
| void get ( Log_Entry &e ) const; | |||
| void get_unjournaled ( Log_Entry &e ) const; | |||
| void set ( Log_Entry &e ); | |||
| public: | |||
| @@ -177,11 +178,15 @@ public: | |||
| const char * name ( void ) const { return _name; } | |||
| bool mute ( void ) const { return mute_button->value(); } | |||
| void mute ( bool b ) { mute_button->value( b ); } | |||
| bool solo ( void ) const { return solo_button->value(); } | |||
| void solo ( bool b ); | |||
| bool armed ( void ) const { return record_button->value(); } | |||
| void armed ( bool b ) { record_button->value( b ); } | |||
| bool selected ( void ) const { return _selected; } | |||
| void solo ( bool b ); | |||
| static void cb_input_field ( Fl_Widget *w, void *v ); | |||
| void cb_input_field ( void ); | |||