From f12363340e14ed0a2c0d245300b94badd4b08fb5 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Sun, 11 May 2008 09:24:38 -0500 Subject: [PATCH] Clean up Peaks.C --- Timeline/Peaks.C | 438 ++++++++++++++++++++++------------------------- Timeline/Peaks.H | 72 ++++---- 2 files changed, 249 insertions(+), 261 deletions(-) diff --git a/Timeline/Peaks.C b/Timeline/Peaks.C index 9abe50f..348c461 100644 --- a/Timeline/Peaks.C +++ b/Timeline/Peaks.C @@ -25,8 +25,6 @@ #include "Peaks.H" -// #include "Timeline.H" - #include #include #include @@ -40,50 +38,25 @@ #include "assert.h" -#include - -#include // for Fl::check(); - #include "debug.h" #include - #include "Transport.H" // for .recording #include -Peaks::peakbuffer Peaks::_peakbuf; - - +/* whether to cache peaks at multiple resolutions on disk to + * drastically improve performance */ bool Peaks::mipmapped_peakfiles = true; -/* chunksizes at which to generate peakfiles (on demand). This should - pretty much cover the usable range. Better performance can be - achieved at high zoom-levels and for compressed sources with a - minimum of 64, but those files are up into the megabytes. */ const int Peaks::cache_minimum = 256; /* minimum chunksize to build peakfiles for */ const int Peaks::cache_levels = 8; /* number of sampling levels in peak cache */ -// const int Peaks::cache_step = 2; /* powers of two between each level. 4 == 256, 2048, 16384, ... */ - const int Peaks::cache_step = 1; /* powers of two between each level. 4 == 256, 2048, 16384, ... */ -/* Peaks ( ) */ -/* { */ -/* _clip = NULL; */ -/* } */ -Peaks::Peaks ( Audio_File *c ) -{ - _clip = c; - _peak_writer = NULL; -} - -Peaks::~Peaks ( ) -{ - if ( _peak_writer ) - delete _peak_writer; -} +Peaks::peakbuffer Peaks::_peakbuf; + static const char * @@ -96,16 +69,18 @@ peakname ( const char *filename ) return (const char*)&file; } -/** 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 +/** update the modification time of file referred to by /fd/ */ +static void +touch ( int fd ) { - _fpp = fpp; + struct stat st; - return read_peaks( s, e, (e - s) / fpp, fpp ); + fstat( fd, &st ); + + fchmod( fd, st.st_mode ); } + static int nearest_power_of_two ( int v ) { @@ -128,6 +103,30 @@ nearest_cached_chunksize ( nframes_t chunksize ) return 0; } + + +Peaks::Peaks ( Audio_File *c ) +{ + _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, (e - s) / fpp, fpp ); +} + struct peakfile_block_header { unsigned long chunksize; @@ -230,6 +229,7 @@ public: break; } +// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize ); _offset = ftell( _fp ); } @@ -364,11 +364,8 @@ public: delete[] pbuf; -// close(); - return i; } - }; @@ -456,13 +453,10 @@ Peaks::read_source_peaks ( Peak *peaks, int npeaks, nframes_t chunksize ) const int Peaks::read_source_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const { -// _clip->open(); _clip->seek( s ); int i = read_source_peaks( peaks, npeaks, chunksize ); -// _clip->close(); - return i; } @@ -481,8 +475,8 @@ Peaks::read_peaks ( nframes_t s, nframes_t e, int npeaks, nframes_t chunksize ) _peakbuf.offset = s; _peakbuf.buf->chunksize = chunksize; - /* FIXME: compart to (minimum) peakfile chunk size */ - if ( chunksize < 256 ) + /* FIXME: use actual minimum chunksize from peakfile! */ + if ( chunksize < cache_minimum ) _peakbuf.len = read_source_peaks( _peakbuf.buf->data, s, npeaks, chunksize ); else _peakbuf.len = read_peakfile_peaks( _peakbuf.buf->data, s, npeaks, chunksize ); @@ -490,13 +484,6 @@ Peaks::read_peaks ( nframes_t s, nframes_t e, int npeaks, nframes_t chunksize ) return _peakbuf.len; } -/* FIXME: what purpose does this serve now? */ -bool -Peaks::open ( void ) -{ -} - - /** returns false if peak file for /filename/ is out of date */ bool Peaks::current ( void ) const @@ -521,179 +508,11 @@ Peaks::current ( void ) const } -static void -touch ( int fd ) -{ - struct stat st; - - fstat( fd, &st ); - - fchmod( fd, st.st_mode ); -} - - -/* The Peak_Builder is for generating peaks from imported or updated sources, or when the - peakfile is simply missing */ - -class Peak_Builder -{ - FILE *fp; - size_t last_block_pos; - const Peaks *_peaks; - - void - write_block_header ( nframes_t chunksize ) - { - if ( last_block_pos ) - { - /* update previous block */ - size_t pos = ftell( fp ); - - fseek( 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 ); - -// DMESSAGE( "old block header: chunksize=%lu, skip=%lu", bh.chunksize, bh.skip ); - - bh.skip = pos - last_block_pos; - -// DMESSAGE( "new block header: chunksize=%lu, skip=%lu", bh.chunksize, bh.skip ); - - fwrite( &bh, sizeof( bh ), 1, fp ); - - fseek( fp, pos, SEEK_SET ); - } - - peakfile_block_header bh; - - bh.chunksize = chunksize; - bh.skip = 0; - - fwrite( &bh, sizeof( bh ), 1, fp ); - - last_block_pos = ftell( fp ); - } - -public: - - /** generate additional cache levels for a peakfile with only 1 block (ie. that of a new capture) */ - bool - make_peaks_mipmap ( void ) - { - if ( ! Peaks::mipmapped_peakfiles ) - return true; - - Audio_File *_clip = _peaks->_clip; - - const char *filename = _clip->name(); - - FILE *rfp; - - last_block_pos = sizeof( peakfile_block_header ); - - /* open for reading */ - rfp = fopen( peakname( filename ), "r" ); - /* open the file again for appending */ - fp = fopen( peakname( filename ), "r+" ); - fseek( fp, 0, SEEK_END ); - - Peak buf[ _clip->channels() ]; - - /* now build the remaining peak levels, each based on the - * preceding level */ - - nframes_t cs = Peaks::cache_minimum << Peaks::cache_step; - for ( int i = 1; i < Peaks::cache_levels; ++i, cs <<= Peaks::cache_step ) - { - DMESSAGE( "building level %d peak cache", i + 1 ); - - Peakfile pf; - - /* open the peakfile for the previous cache level */ - pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step ); - - write_block_header( cs ); - - fflush( fp ); - - size_t len; - nframes_t s = 0; - do { - len = pf.read_peaks( buf, s, 1, cs ); - s += cs; - - fwrite( buf, sizeof( buf ), len, fp ); - } - while ( len ); - - pf.leave_open(); - } - - fclose( rfp ); - fclose( fp ); - - return true; - } - - bool - make_peaks ( void ) - { - Audio_File *_clip = _peaks->_clip; - - const char *filename = _clip->name(); - - DMESSAGE( "building peaks for \"%s\"", filename ); - - FILE *rfp; - - if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) ) - return false; - - _clip->seek( 0 ); - - Peak buf[ _clip->channels() ]; - - DMESSAGE( "building level 1 peak cache" ); - - write_block_header( Peaks::cache_minimum ); - - /* build first level from source */ - size_t len; - do { - len = _peaks->read_source_peaks( buf, 1, Peaks::cache_minimum ); - - fwrite( buf, sizeof( buf ), len, fp ); - } - while ( len ); - - /* reopen for reading */ - fclose( fp ); - - make_peaks_mipmap(); - - DMESSAGE( "done building peaks" ); - - return true; - } - - - Peak_Builder ( const Peaks *peaks ) : _peaks( peaks ) - { - fp = NULL; - last_block_pos = 0; - } -}; - bool Peaks::make_peaks ( void ) const { - Peak_Builder pb( this ); + Peaks::Builder pb( this ); return pb.make_peaks(); } @@ -701,7 +520,7 @@ Peaks::make_peaks ( void ) const bool Peaks::make_peaks_mipmap ( void ) const { - Peak_Builder pb( this ); + Peaks::Builder pb( this ); return pb.make_peaks_mipmap(); } @@ -721,14 +540,13 @@ Peak::normalization_factor( void ) const return s; } - /* wrapper for peak writer */ void Peaks::prepare_for_writing ( void ) { assert( ! _peak_writer ); - _peak_writer = new Peak_Writer( _clip->name(), cache_minimum, _clip->channels() ); + _peak_writer = new Peaks::Streamer( _clip->name(), cache_minimum, _clip->channels() ); } void @@ -752,11 +570,17 @@ Peaks::write ( sample_t *buf, nframes_t nframes ) _peak_writer->write( buf, nframes ); } -/* The Peak_Writer is for streaming peaks from audio buffers to disk - * while capturing. It works by accumulating a peak value across - * write() calls. */ + -Peak_Writer::Peak_Writer ( const char *filename, nframes_t chunksize, int channels ) +/* + The Streamer is for streaming peaks from audio buffers to disk while + capturing. It works by accumulating a peak value across write() + calls. The Streamer can only generate peaks at a single + chunksize--additional cache levels must be appended after the + Streamer has finished. +*/ + +Peaks::Streamer::Streamer ( const char *filename, nframes_t chunksize, int channels ) { _channels = channels; _chunksize = chunksize; @@ -778,7 +602,7 @@ Peak_Writer::Peak_Writer ( const char *filename, nframes_t chunksize, int channe fflush( _fp ); } -Peak_Writer::~Peak_Writer ( ) +Peaks::Streamer::~Streamer ( ) { touch( fileno( _fp ) ); @@ -789,7 +613,7 @@ Peak_Writer::~Peak_Writer ( ) /** append peaks for samples in /buf/ to peakfile */ void -Peak_Writer::write ( sample_t *buf, nframes_t nframes ) +Peaks::Streamer::write ( sample_t *buf, nframes_t nframes ) { for ( ; nframes--; ++_index, buf += _channels ) { @@ -811,3 +635,157 @@ Peak_Writer::write ( sample_t *buf, nframes_t nframes ) } } } + + + +/* + The Builder is for generating peaks from imported or updated + sources, or when the peakfile is simply missing. +*/ + +void +Peaks::Builder::write_block_header ( nframes_t chunksize ) +{ + if ( last_block_pos ) + { + /* update previous block */ + size_t pos = ftell( fp ); + + fseek( 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 ); + +// DMESSAGE( "old block header: chunksize=%lu, skip=%lu", bh.chunksize, bh.skip ); + + bh.skip = pos - last_block_pos; + +// DMESSAGE( "new block header: chunksize=%lu, skip=%lu", bh.chunksize, bh.skip ); + + fwrite( &bh, sizeof( bh ), 1, fp ); + + fseek( fp, pos, SEEK_SET ); + } + + peakfile_block_header bh; + + bh.chunksize = chunksize; + bh.skip = 0; + + fwrite( &bh, sizeof( bh ), 1, fp ); + + last_block_pos = ftell( fp ); +} + +/** generate additional cache levels for a peakfile with only 1 block (ie. that of a new capture) */ +bool +Peaks::Builder::make_peaks_mipmap ( void ) +{ + if ( ! Peaks::mipmapped_peakfiles ) + return true; + + Audio_File *_clip = _peaks->_clip; + + const char *filename = _clip->name(); + + FILE *rfp; + + last_block_pos = sizeof( peakfile_block_header ); + + /* open for reading */ + rfp = fopen( peakname( filename ), "r" ); + /* open the file again for appending */ + fp = fopen( peakname( filename ), "r+" ); + fseek( fp, 0, SEEK_END ); + + Peak buf[ _clip->channels() ]; + + /* now build the remaining peak levels, each based on the + * preceding level */ + + nframes_t cs = Peaks::cache_minimum << Peaks::cache_step; + for ( int i = 1; i < Peaks::cache_levels; ++i, cs <<= Peaks::cache_step ) + { + DMESSAGE( "building level %d peak cache", i + 1 ); + + Peakfile pf; + + /* open the peakfile for the previous cache level */ + pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step ); + + write_block_header( cs ); + + fflush( fp ); + + size_t len; + nframes_t s = 0; + do { + len = pf.read_peaks( buf, s, 1, cs ); + s += cs; + + fwrite( buf, sizeof( buf ), len, fp ); + } + while ( len ); + + pf.leave_open(); + } + + fclose( rfp ); + fclose( fp ); + + return true; +} + +bool +Peaks::Builder::make_peaks ( void ) +{ + Audio_File *_clip = _peaks->_clip; + + const char *filename = _clip->name(); + + DMESSAGE( "building peaks for \"%s\"", filename ); + + FILE *rfp; + + if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) ) + return false; + + _clip->seek( 0 ); + + Peak buf[ _clip->channels() ]; + + DMESSAGE( "building level 1 peak cache" ); + + write_block_header( Peaks::cache_minimum ); + + /* build first level from source */ + size_t len; + do { + len = _peaks->read_source_peaks( buf, 1, Peaks::cache_minimum ); + + fwrite( buf, sizeof( buf ), len, fp ); + } + while ( len ); + + /* reopen for reading */ + fclose( fp ); + + make_peaks_mipmap(); + + DMESSAGE( "done building peaks" ); + + return true; +} + + +Peaks::Builder::Builder ( const Peaks *peaks ) : _peaks( peaks ) +{ + fp = NULL; + last_block_pos = 0; +} + + diff --git a/Timeline/Peaks.H b/Timeline/Peaks.H index 4c5c132..feacdb9 100644 --- a/Timeline/Peaks.H +++ b/Timeline/Peaks.H @@ -30,8 +30,9 @@ struct Peak { float normalization_factor ( void ) const; }; +#include + class Audio_File; -class Peak_Writer; class Peaks { @@ -57,6 +58,44 @@ class Peaks } }; + class Streamer + { + FILE *_fp; + Peak *_peak; + int _chunksize; + int _channels; + + int _index; + + /* not permitted */ + Streamer ( const Streamer &rhs ); + const Streamer &operator= ( const Streamer &rhs ); + + public: + + Streamer ( const char *filename, nframes_t chunksize, int channels ); + ~Streamer ( ); + + void write ( sample_t *buf, nframes_t nframes ); + + }; + + class Builder + { + FILE *fp; + size_t last_block_pos; + const Peaks *_peaks; + + void write_block_header ( nframes_t chunksize ); + + public: + + bool make_peaks_mipmap ( void ); + bool make_peaks ( void ); + + Builder ( const Peaks *peaks ); + }; + static peakbuffer _peakbuf; Audio_File *_clip; @@ -68,16 +107,12 @@ class Peaks 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; - Peak_Writer * volatile _peak_writer; /* exists when streaming peaks to disk */ - - size_t last_block_pos; /* FIXME: only needed in make_peaks! */ + Streamer * volatile _peak_writer; /* exists when streaming peaks to disk */ /* not permitted */ Peaks ( const Peaks &rhs ); const Peaks &operator= ( const Peaks &rhs ); - friend class Peak_Builder; - public: static bool mipmapped_peakfiles; @@ -97,7 +132,6 @@ public: int fill_buffer ( float fpp, nframes_t s, nframes_t e ) const; void read ( int X, float *hi, float *lo ) const; - bool open ( void ); bool current ( void ) const; bool make_peaks ( void ) const; @@ -107,27 +141,3 @@ public: void finish_writing ( void ); void write ( sample_t *buf, nframes_t nframes ); }; - -#include - -class Peak_Writer -{ - FILE *_fp; - Peak *_peak; - int _chunksize; - int _channels; - - int _index; - - /* not permitted */ - Peak_Writer ( const Peak_Writer &rhs ); - const Peak_Writer &operator= ( const Peak_Writer &rhs ); - -public: - - Peak_Writer ( const char *filename, nframes_t chunksize, int channels ); - ~Peak_Writer ( ); - - void write ( sample_t *buf, nframes_t nframes ); - -};