diff --git a/timeline/makefile.inc b/timeline/makefile.inc index b70cce4..132b8fd 100644 --- a/timeline/makefile.inc +++ b/timeline/makefile.inc @@ -14,7 +14,7 @@ OBJS:=$(SRCS:.C=.o) LIBS += $(FLTK_LDFLAGS) $(JACK_LIBS) $(SNDFILE_LIBS) $(LIBLO_LIBS) $(SIGCPP_LIBS) $(XPM_LIBS) -CFLAGS += $(SNDFILE_CFLAGS) $(FLTK_CFLAGS) $(JACK_CFLAGS) $(SIGCPP_CFLAGS) $(XPM_CFLAGS) +CFLAGS += $(SNDFILE_CFLAGS) $(FLTK_CFLAGS) $(JACK_CFLAGS) $(SIGCPP_CFLAGS) $(XPM_CFLAGS) -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ifeq ($(USE_UNOPTIMIZED_DRAWING),yes) CFLAGS+=-DUSE_UNOPTIMIZED_DRAWING diff --git a/timeline/src/Audio_Region.C b/timeline/src/Audio_Region.C index 4391acf..a926cd1 100644 --- a/timeline/src/Audio_Region.C +++ b/timeline/src/Audio_Region.C @@ -470,6 +470,15 @@ Audio_Region::draw_box( void ) fl_pop_clip(); } +void +Audio_Region::peaks_ready_callback ( void *v ) +{ + Fl::lock(); + ((Audio_Region*)v)->redraw(); + Fl::unlock(); + Fl::awake(); +} + /** Draw (part of) region. X, Y, W and H are the rectangle we're clipped to. */ void Audio_Region::draw ( void ) @@ -540,12 +549,12 @@ Audio_Region::draw ( void ) // DMESSAGE( "Drawing audio region."); + int channels; + int peaks; + Peak *pbuf = NULL; + do { - int channels; - int peaks; - Peak *pbuf; - nframes_t start = _r->offset; int loop_peaks_needed = _loop ? timeline->ts_to_x( _loop ) : timeline->ts_to_x( _clip->length() ); @@ -577,22 +586,7 @@ Audio_Region::draw ( void ) assert( loop_peaks_needed >= 0 ); - if ( _loop && offset < _loop ) - { - const int x = timeline->ts_to_x( _loop - offset ); - - /* FIXME: is there no way to draw these symbols direclty? */ - fl_color( FL_WHITE ); - - fl_push_matrix(); - - fl_translate( X + x + 2, y() + h() - 14 ); - fl_scale( - 16, 8 ); - - draw_full_arrow_symbol( FL_BLACK ); - - fl_pop_matrix(); - } + } if ( xo + loop_peaks_needed > total_peaks_needed ) @@ -607,15 +601,26 @@ Audio_Region::draw ( void ) if ( start != ostart || end != oend ) { - if ( _clip->read_peaks( timeline->fpp(), - start, - end, - &peaks, &pbuf, &channels ) ) + if ( _clip->peaks()->peakfile_ready() ) { - Waveform::scale( pbuf, peaks * channels, _scale ); + if ( _clip->read_peaks( timeline->fpp(), + start, + end, + &peaks, &pbuf, &channels ) ) + { + Waveform::scale( pbuf, peaks * channels, _scale ); - ostart = start; - oend = end; + ostart = start; + oend = end; + } + } + else + { + if ( ! transport->rolling ) + { + /* create a thread to make the peaks */ + _clip->peaks()->make_peaks_asynchronously( Audio_Region::peaks_ready_callback, this ); + } } } else @@ -640,7 +645,7 @@ Audio_Region::draw ( void ) loop_peaks_needed, ch, pbuf + i, peaks, channels, - c ); + c ); } } @@ -656,6 +661,21 @@ Audio_Region::draw ( void ) timeline->draw_measure_lines( X, Y, W, H ); + if ( _loop && offset < _loop ) + { + const int x = timeline->ts_to_x( _loop - offset ); + + /* FIXME: is there no way to draw these symbols direclty? */ + + fl_push_matrix(); + + fl_translate( X + x + 2, y() + h() - 7 ); + fl_scale( - 8, 8 ); + + draw_full_arrow_symbol( FL_WHITE ); + + fl_pop_matrix(); + } /* fl_color( FL_BLACK ); */ /* fl_line( rx, Y, rx, Y + H ); */ /* fl_line( rx + rw - 1, Y, rx + rw - 1, Y + H ); */ diff --git a/timeline/src/Audio_Region.H b/timeline/src/Audio_Region.H index fd02d06..0cbb220 100644 --- a/timeline/src/Audio_Region.H +++ b/timeline/src/Audio_Region.H @@ -33,6 +33,8 @@ class Audio_Region : public Sequence_Region /* not permitted */ Audio_Region & operator = ( const Audio_Region &rhs ); + static void peaks_ready_callback ( void *v ); + public: static bool inherit_track_color; diff --git a/timeline/src/Engine/Peaks.C b/timeline/src/Engine/Peaks.C index 443af38..e5a436c 100644 --- a/timeline/src/Engine/Peaks.C +++ b/timeline/src/Engine/Peaks.C @@ -35,8 +35,6 @@ #include #include -#include "../Transport.H" // for .recording - #include "Audio_File.H" #include "Peaks.H" @@ -53,8 +51,19 @@ using std::min; using std::max; +#include + #include + +struct peak_thread_data +{ + void(*callback)(void*); + void *userdata; + Peaks *peaks; +}; + + /* whether to cache peaks at multiple resolutions on disk to @@ -82,31 +91,6 @@ peakname ( const char *filename ) -Peaks::Peaks ( Audio_File *c ) -{ - _pending = false; - _clip = c; - _peak_writer = NULL; -} - -Peaks::~Peaks ( ) -{ - if ( _peak_writer ) - delete _peak_writer; -} - - - -/** Prepare a buffer of peaks from /s/ to /e/ for reading. Must be - * called before any calls to operator[] */ -int -Peaks::fill_buffer ( float fpp, nframes_t s, nframes_t e ) const -{ - _fpp = fpp; - - return read_peaks( s, (e - s) / fpp, fpp ); -} - struct peakfile_block_header { @@ -121,15 +105,15 @@ class Peakfile nframes_t _chunksize; int _channels; /* number of channels this peakfile represents */ nframes_t _length; /* length, in frames, of the clip this peakfile represents */ - size_t _offset; - int _blocks; + off_t _offset; +// int _blocks; struct block_descriptor { nframes_t chunksize; - size_t pos; + off_t pos; - block_descriptor ( nframes_t chunksize, size_t pos ) : chunksize( chunksize ), pos( pos ) + block_descriptor ( nframes_t chunksize, off_t pos ) : chunksize( chunksize ), pos( pos ) { } @@ -139,16 +123,18 @@ class Peakfile } }; + std::list blocks; public: Peakfile ( ) { - _blocks = 0; +// _blocks = 0; _fp = NULL; _offset = 0; _chunksize = 0; _channels = 0; + _length = 0; } ~Peakfile ( ) @@ -157,52 +143,59 @@ public: close(); } - int blocks ( void ) const { return _blocks; } + /* int blocks ( void ) const { return blocks.size(); } */ /** find the best block for /chunksize/ */ void scan ( nframes_t chunksize ) { - rewind( _fp ); - clearerr( _fp ); - - std::list blocks; - - /* scan all blocks */ - for ( ;; ) + if ( ! blocks.size() ) { - peakfile_block_header bh; - - fread( &bh, sizeof( bh ), 1, _fp ); - - if ( feof( _fp ) ) - break; - -// printf( "chunksize=%lu, skip=%lu\n", (unsigned long)bh.chunksize, (unsigned long) bh.skip ); - - ASSERT( bh.chunksize, "Chucksize of zero. Invalid peak file structure!" ); - - blocks.push_back( block_descriptor( bh.chunksize, ftell( _fp ) ) ); - - if ( ! bh.skip ) - /* last block */ - break; - - if ( fseek( _fp, bh.skip, SEEK_CUR ) ) + rewind( _fp ); + clearerr( _fp ); + + /* scan all blocks */ + for ( ;; ) { - WARNING( "seek failed: %s (%lu)", strerror( errno ), bh.skip ); - break; + peakfile_block_header bh; + + fread( &bh, sizeof( bh ), 1, _fp ); + + if ( feof( _fp ) ) + break; + + DMESSAGE( "Peakfile: chunksize=%lu, skip=%lu\n", (uint64_t)bh.chunksize, (uint64_t) bh.skip ); + + ASSERT( bh.chunksize, "Chucksize of zero. Invalid peak file structure!" ); + + blocks.push_back( block_descriptor( bh.chunksize, ftello( _fp ) ) ); + + if ( ! bh.skip ) + /* last block */ + break; + + if ( fseeko( _fp, bh.skip, SEEK_CUR ) ) + { + WARNING( "seek failed: %s (%lu)", strerror( errno ), bh.skip ); + break; + } } } if ( ! blocks.size() ) FATAL( "Peak file contains no blocks!" ); + if ( chunksize == _chunksize ) + { + return; + /* already on the right block... */ + } + // DMESSAGE( "peakfile has %d blocks.", blocks.size() ); blocks.sort(); /* fall back on the smallest chunksize */ - fseek( _fp, blocks.front().pos, SEEK_SET ); + fseeko( _fp, blocks.front().pos, SEEK_SET ); _chunksize = blocks.front().chunksize; /* search for the best-fit chunksize */ @@ -211,19 +204,19 @@ public: if ( chunksize >= i->chunksize ) { _chunksize = i->chunksize; - fseek( _fp, i->pos, SEEK_SET ); + fseeko( _fp, i->pos, SEEK_SET ); break; } // DMESSAGE( "using peakfile block for chunksize %lu", _chunksize ); - _blocks = blocks.size(); - _offset = ftell( _fp ); +// _blocks = blocks.size(); + _offset = ftello( _fp ); } /** convert frame number of peak number */ nframes_t frame_to_peak ( nframes_t frame ) { - return frame * _channels / _chunksize; + return ( frame / _chunksize ) * (nframes_t)_channels; } /** return the number of peaks in already open peakfile /fp/ */ @@ -241,7 +234,7 @@ public: bool ready ( nframes_t start, nframes_t npeaks ) { - if ( _blocks > 1 ) + if ( blocks.size() > 1 ) return true; else return this->npeaks() > frame_to_peak( start ) + npeaks; @@ -251,7 +244,7 @@ public: bool open ( const char *name, int channels, nframes_t chunksize ) { - _chunksize = 0; +// _chunksize = 0; _channels = channels; char *pn = peakname( name ); @@ -305,7 +298,7 @@ public: * large enough to fit the entire request. Returns the number of * peaks actually read, which may be fewer than were requested. */ nframes_t - read_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) + read_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) { if ( ! _fp ) { @@ -316,12 +309,19 @@ public: const unsigned int ratio = chunksize / _chunksize; /* locate to start position */ - if ( fseek( _fp, _offset + ( frame_to_peak( s ) * sizeof( Peak ) ), SEEK_SET ) ) + + /* if ( s > _clip->length() ) */ + /* return 0; */ + + if ( fseeko( _fp, _offset + ( frame_to_peak( s ) * sizeof( Peak ) ), SEEK_SET ) ) { DMESSAGE( "failed to seek... peaks not ready?" ); return 0; } + if ( feof( _fp ) ) + return 0; + if ( ratio == 1 ) return fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp ); @@ -329,7 +329,7 @@ public: nframes_t len = 0; - int i; + nframes_t i; for ( i = 0; i < npeaks; ++i ) { @@ -358,8 +358,10 @@ public: } - if ( len < ratio ) + if ( feof( _fp) || len < ratio ) + { break; + } } delete[] pbuf; @@ -368,8 +370,43 @@ public: } }; + + +Peaks::Peaks ( Audio_File *c ) +{ + _pending = false; + _clip = c; + _peak_writer = NULL; + _peakfile = new Peakfile(); +} + +Peaks::~Peaks ( ) +{ + if ( _peak_writer ) + { + delete _peak_writer; + _peak_writer = NULL; + } + + delete _peakfile; + _peakfile = NULL; +} + + + +/** Prepare a buffer of peaks from /s/ to /e/ for reading. Must be + * called before any calls to operator[] */ +int +Peaks::fill_buffer ( float fpp, nframes_t s, nframes_t e ) const +{ + _fpp = fpp; + + return read_peaks( s, (e - s) / fpp, fpp ); +} + + bool -Peaks::ready ( nframes_t s, int npeaks, nframes_t chunksize ) const +Peaks::ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const { /* if ( _pending ) */ /* return false; */ @@ -382,45 +419,65 @@ Peaks::ready ( nframes_t s, int npeaks, nframes_t chunksize ) const return _peakfile.ready( s, npeaks ); } -int -Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const +/** If this returns false, then the peakfile needs to be built */ +bool +Peaks::peakfile_ready ( void ) const { - /* never try to build peaks while recording */ - if ( ! transport->recording ) - { - if ( ! current() && ! _pending ) - { - /* Build peaks asyncronously */ - _pending = true; - _make_peaks_thread.clone( &Peaks::make_peaks, const_cast(this) ); - _make_peaks_thread.detach(); - } - } + return current() && ! _pending; +} +void +Peaks::make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const +{ + /* already working on it... */ + if( _pending ) + return; + +// make_peaks(); + + _pending = true; + + peak_thread_data *pd = new peak_thread_data(); + + pd->callback = callback; + pd->userdata = userdata; + pd->peaks = const_cast(this); + + _make_peaks_thread.clone( &Peaks::make_peaks, pd ); + _make_peaks_thread.detach(); +} + +nframes_t +Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const +{ /* if ( _pending ) */ /* return 0; */ - Peakfile _peakfile; +// Peakfile _peakfile; - if ( ! _peakfile.open( _clip->filename(), _clip->channels(), chunksize ) ) + if ( ! _peakfile->open( _clip->filename(), _clip->channels(), chunksize ) ) { DMESSAGE( "Failed to open peakfile!" ); return 0; } - return _peakfile.read_peaks( peaks, s, npeaks, chunksize ); + nframes_t l = _peakfile->read_peaks( peaks, s, npeaks, chunksize ); + + _peakfile->close(); + + return l; } -int -Peaks::read_source_peaks ( Peak *peaks, int npeaks, nframes_t chunksize ) const +nframes_t +Peaks::read_source_peaks ( Peak *peaks, nframes_t npeaks, nframes_t chunksize ) const { int channels = _clip->channels(); sample_t *fbuf = new sample_t[ chunksize * channels ]; - size_t len; + off_t len; - int i; + nframes_t i; for ( i = 0; i < npeaks; ++i ) { /* read in a buffer */ @@ -455,19 +512,19 @@ Peaks::read_source_peaks ( Peak *peaks, int npeaks, nframes_t chunksize ) const return i; } -int -Peaks::read_source_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const +nframes_t +Peaks::read_source_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const { _clip->seek( s ); - int i = read_source_peaks( peaks, npeaks, chunksize ); - - return i; + return read_source_peaks( peaks, npeaks, chunksize ); } -int -Peaks::read_peaks ( nframes_t s, int npeaks, nframes_t chunksize ) const +nframes_t +Peaks::read_peaks ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const { + THREAD_ASSERT( UI ); /* because _peakbuf cache is static */ + // printf( "reading peaks %d @ %d\n", npeaks, chunksize ); if ( _peakbuf.size < (nframes_t)( npeaks * _clip->channels() ) ) @@ -510,7 +567,14 @@ Peaks::current ( void ) const void * Peaks::make_peaks ( void *v ) { - ((Peaks*)v)->make_peaks(); + peak_thread_data *pd = (peak_thread_data*)v; + + pd->peaks->make_peaks(); + + if ( pd->callback ) + pd->callback( pd->userdata ); + + delete pd; return NULL; } @@ -524,10 +588,6 @@ Peaks::make_peaks ( void ) const _pending = false; - Fl::lock(); - timeline->redraw(); - Fl::unlock(); - return b; } @@ -706,16 +766,16 @@ Peaks::Builder::write_block_header ( nframes_t chunksize ) if ( last_block_pos ) { /* update previous block */ - size_t pos = ftell( fp ); + off_t pos = ftello( fp ); - fseek( fp, last_block_pos - sizeof( peakfile_block_header ), SEEK_SET ); + fseeko( fp, last_block_pos - sizeof( peakfile_block_header ), SEEK_SET ); peakfile_block_header bh; fread( &bh, sizeof( bh ), 1, fp ); - fseek( fp, last_block_pos - sizeof( peakfile_block_header ), SEEK_SET ); -// fseek( fp, 0 - sizeof( bh ), SEEK_CUR ); + fseeko( fp, last_block_pos - sizeof( peakfile_block_header ), SEEK_SET ); +// fseeko( fp, 0 - sizeof( bh ), SEEK_CUR ); // DMESSAGE( "old block header: chunksize=%lu, skip=%lu", (unsigned long) bh.chunksize, (unsigned long) bh.skip ); @@ -727,7 +787,7 @@ Peaks::Builder::write_block_header ( nframes_t chunksize ) fwrite( &bh, sizeof( bh ), 1, fp ); - fseek( fp, pos, SEEK_SET ); + fseeko( fp, pos, SEEK_SET ); } peakfile_block_header bh; @@ -737,7 +797,7 @@ Peaks::Builder::write_block_header ( nframes_t chunksize ) fwrite( &bh, sizeof( bh ), 1, fp ); - last_block_pos = ftell( fp ); + last_block_pos = ftello( fp ); fflush( fp ); } @@ -796,10 +856,10 @@ Peaks::Builder::make_peaks_mipmap ( void ) free( pn ); - if ( fseek( fp, 0, SEEK_END ) ) + if ( fseeko( fp, 0, SEEK_END ) ) FATAL( "error performing seek: %s", strerror( errno ) ); - if ( ftell( fp ) == sizeof( peakfile_block_header ) ) + if ( ftello( fp ) == sizeof( peakfile_block_header ) ) { DWARNING( "truncated peakfile. Programming error?" ); return false; @@ -834,7 +894,7 @@ Peaks::Builder::make_peaks_mipmap ( void ) write_block_header( cs ); - size_t len; + off_t len; nframes_t s = 0; do { len = pf.read_peaks( buf, s, 1, cs ); @@ -843,7 +903,9 @@ Peaks::Builder::make_peaks_mipmap ( void ) fwrite( buf, sizeof( buf ), len, fp ); } - while ( len ); + while ( len > 0 && s < _clip->length() ); + + DMESSAGE( "Last sample was %lu", s ); /* fflush( fp ); */ /* fsync( fileno( fp ) ); */ @@ -887,7 +949,7 @@ Peaks::Builder::make_peaks ( void ) write_block_header( Peaks::cache_minimum ); /* build first level from source */ - size_t len; + off_t len; do { len = _peaks->read_source_peaks( buf, 1, Peaks::cache_minimum ); diff --git a/timeline/src/Engine/Peaks.H b/timeline/src/Engine/Peaks.H index a719902..ead005f 100644 --- a/timeline/src/Engine/Peaks.H +++ b/timeline/src/Engine/Peaks.H @@ -31,6 +31,7 @@ class Audio_File; +class Peakfile; class Peaks { @@ -62,6 +63,8 @@ class Peaks size = len = 0; } }; + + Peakfile *_peakfile; class Streamer { @@ -87,7 +90,7 @@ class Peaks class Builder { FILE *fp; - size_t last_block_pos; + off_t last_block_pos; const Peaks *_peaks; void write_block_header ( nframes_t chunksize ); @@ -100,16 +103,17 @@ class Peaks Builder ( const Peaks *peaks ); }; + /* FIXME: Is this ever accessed by multiple threads? */ static peakbuffer _peakbuf; Audio_File *_clip; mutable float _fpp; - int read_peaks ( nframes_t s, int npeaks, nframes_t chunksize ) const; - int read_source_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const; - int read_source_peaks ( Peak *peaks, int npeaks, nframes_t chunksize ) const; - int read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const; + nframes_t read_peaks ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const; + nframes_t read_source_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const; + nframes_t read_source_peaks ( Peak *peaks, nframes_t npeaks, nframes_t chunksize ) const; + nframes_t read_peakfile_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const; Streamer * volatile _peak_writer; /* exists when streaming peaks to disk */ @@ -117,6 +121,8 @@ class Peaks Peaks ( const Peaks &rhs ); const Peaks &operator= ( const Peaks &rhs ); + bool current ( void ) const; + public: static bool mipmapped_peakfiles; @@ -133,14 +139,17 @@ public: int fill_buffer ( float fpp, nframes_t s, nframes_t e ) const; - void read ( int X, float *hi, float *lo ) const; - bool ready ( nframes_t s, int npeaks, nframes_t chunksize ) const; + bool peakfile_ready ( void ) const; + + void read ( int X, float *hi, float *lo ) const; + bool ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const; - bool current ( void ) const; bool make_peaks ( void ) const; - bool make_peaks_mipmap ( void ) const; + bool make_peaks_mipmap ( void ) const; + void make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const; void prepare_for_writing ( void ); void finish_writing ( void ); void write ( sample_t *buf, nframes_t nframes ); + }; diff --git a/timeline/src/Transport.C b/timeline/src/Transport.C index d578aa5..2866892 100644 --- a/timeline/src/Transport.C +++ b/timeline/src/Transport.C @@ -20,6 +20,7 @@ /* Controls the audio transport */ #include "Transport.H" +#include "Timeline.H" #include "Engine/Engine.H"