From 50fa64232177248d2c1b916344b85f33e9a7bb11 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Sun, 11 May 2008 17:48:19 -0500 Subject: [PATCH] Fix mipmapping for streamed peaks. --- Timeline/Audio_File.H | 4 +- Timeline/Audio_File_SF.C | 1 - Timeline/Audio_File_SF.H | 7 +++ Timeline/Audio_Region.C | 8 ++-- Timeline/Peaks.C | 95 ++++++++++++++++++++++++++++------------ Timeline/Peaks.H | 3 +- 6 files changed, 82 insertions(+), 36 deletions(-) diff --git a/Timeline/Audio_File.H b/Timeline/Audio_File.H index 38847b7..62a2623 100644 --- a/Timeline/Audio_File.H +++ b/Timeline/Audio_File.H @@ -35,6 +35,7 @@ class Audio_File { static std::map _open_files; + /* not permitted */ Audio_File ( const Audio_File &rhs ); const Audio_File & operator= ( const Audio_File &rhs ); @@ -48,7 +49,7 @@ protected: }; const char *_filename; - nframes_t _length; /* length of file in samples */ + volatile nframes_t _length; /* length of file in samples */ nframes_t _samplerate; /* sample rate */ int _channels; @@ -70,6 +71,7 @@ public: Audio_File ( ) : _peaks( this ) { _filename = NULL; + _samplerate = 0; _length = _channels = 0; } diff --git a/Timeline/Audio_File_SF.C b/Timeline/Audio_File_SF.C index 6e7ddc2..ed2d405 100644 --- a/Timeline/Audio_File_SF.C +++ b/Timeline/Audio_File_SF.C @@ -109,7 +109,6 @@ Audio_File_SF::create ( const char *filename, nframes_t samplerate, int channels char *name; asprintf( &name, "%s.%s", filename, fd->extension ); -// if ( ! ( out = sf_open( name, SFM_RDWR, &si ) ) ) if ( ! ( out = sf_open( name, SFM_WRITE, &si ) ) ) { printf( "couldn't create soundfile.\n" ); diff --git a/Timeline/Audio_File_SF.H b/Timeline/Audio_File_SF.H index a97a44c..1fd2137 100644 --- a/Timeline/Audio_File_SF.H +++ b/Timeline/Audio_File_SF.H @@ -31,6 +31,12 @@ class Audio_File_SF : public Audio_File * enough to do this for us */ nframes_t _current_read; + Audio_File_SF ( ) + { + _in = 0; + _current_read = 0; + } + public: static const Audio_File::format_desc supported_formats[]; @@ -38,6 +44,7 @@ public: static Audio_File_SF *from_file ( const char *filename ); static Audio_File_SF *create ( const char *filename, nframes_t samplerate, int channels, const char *format ); + ~Audio_File_SF ( ) { /* stupid C++ */ diff --git a/Timeline/Audio_Region.C b/Timeline/Audio_Region.C index 5d0425e..ee3e878 100644 --- a/Timeline/Audio_Region.C +++ b/Timeline/Audio_Region.C @@ -840,7 +840,8 @@ Audio_Region::write ( nframes_t nframes ) { nframes_t oldl = _clip->length(); - /* FIXME: hack */ + /* get the new size. Remember, this is a read-only handle on the source--not the same + one being written to */ _clip->close(); _clip->open(); @@ -858,6 +859,7 @@ Audio_Region::write ( nframes_t nframes ) return nframes; } +/* THREAD: IO */ /** finalize region capture. Assumes that this *is* a captured region and that no other regions refer to the same source */ bool @@ -865,11 +867,11 @@ Audio_Region::finalize ( nframes_t frame ) { log_end(); + _clip->finalize(); + _clip->close(); _clip->open(); - _clip->finalize(); - /* FIXME: should we attempt to truncate the file? */ _range.length = frame - _range.start - _range.offset; diff --git a/Timeline/Peaks.C b/Timeline/Peaks.C index ee649f5..f649fd5 100644 --- a/Timeline/Peaks.C +++ b/Timeline/Peaks.C @@ -140,7 +140,6 @@ 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 */ - const char *_name; size_t _offset; int _blocks; @@ -169,7 +168,6 @@ public: _offset = 0; _chunksize = 0; _channels = 0; - _name =NULL; } ~Peakfile ( ) @@ -195,11 +193,14 @@ public: fread( &bh, sizeof( bh ), 1, _fp ); +// printf( "chunksize=%lu, skip=%lu\n", (unsigned long)bh.chunksize, (unsigned long) bh.skip ); + ASSERT( bh.chunksize, "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 ) ) @@ -265,11 +266,10 @@ public: /** given soundfile name /name/, try to open the best peakfile for /chunksize/ */ bool - open ( const char *name, nframes_t chunksize, int channels ) + open ( const char *name, int channels, nframes_t chunksize ) { _chunksize = 0; _channels = channels; - _name = name; if ( ! ( _fp = fopen( peakname( name ), "r" ) ) ) return false; @@ -311,7 +311,7 @@ public: * channels. Place the result in buffer /peaks/, which must be * large enough to fit the entire request. Returns the number of * peaks actually read, which may be fewer than were requested. */ - int + nframes_t read_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) { if ( ! _fp ) @@ -320,22 +320,18 @@ public: const unsigned int ratio = chunksize / _chunksize; /* locate to start position */ - if ( fseek( _fp, _offset + frame_to_peak( s ) * sizeof( Peak ), SEEK_SET ) ) + if ( fseek( _fp, _offset + ( frame_to_peak( s ) * sizeof( Peak ) ), SEEK_SET ) ) /* failed to seek... peaks not ready? */ return 0; if ( ratio == 1 ) - { - int len = fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp ); - // close; - return len; - } + return fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp ); Peak *pbuf = new Peak[ ratio * _channels ]; - size_t len = 0; + nframes_t len = 0; - int i; + nframes_t i; for ( i = 0; i < npeaks; ++i ) { /* read in a buffer */ @@ -378,7 +374,7 @@ Peaks::ready ( nframes_t s, int npeaks, nframes_t chunksize ) const { Peakfile _peakfile; - if ( ! _peakfile.open( _clip->name(), chunksize, _clip->channels() ) ) + if ( ! _peakfile.open( _clip->name(), _clip->channels(), chunksize ) ) return false; return _peakfile.ready( s, npeaks ); @@ -391,7 +387,7 @@ Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chu nframes_t ncc = nearest_cached_chunksize( chunksize ); /* never try to build peaks while recording */ - if ( ! ( _peak_writer || transport->recording ) ) + if ( ! transport->recording ) { if ( ! current() ) /* Build peaks asyncronously */ @@ -403,7 +399,7 @@ Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chu Peakfile _peakfile; - if ( ! _peakfile.open( _clip->name(), chunksize, _clip->channels() ) ) + if ( ! _peakfile.open( _clip->name(), _clip->channels(), chunksize ) ) return 0; /* else if ( ! _peakfile.contains( s, npeaks ) ) */ @@ -556,15 +552,17 @@ Peak::normalization_factor( void ) const return s; } +/* THREAD: IO */ /* wrapper for peak writer */ void Peaks::prepare_for_writing ( void ) { assert( ! _peak_writer ); - _peak_writer = new Peaks::Streamer( _clip->name(), cache_minimum, _clip->channels() ); + _peak_writer = new Peaks::Streamer( _clip->name(), _clip->channels(), cache_minimum ); } +/* THREAD: IO */ void Peaks::finish_writing ( void ) { @@ -575,11 +573,14 @@ Peaks::finish_writing ( void ) } /* now fill in the rest of the cache */ + make_peaks_mipmap(); + +/* if ( ! fork() ) */ +/* exit( make_peaks_mipmap() ); */ - if ( ! fork() ) - exit( make_peaks_mipmap() ); } +/* THREAD: IO */ void Peaks::write ( sample_t *buf, nframes_t nframes ) { @@ -596,17 +597,20 @@ Peaks::write ( sample_t *buf, nframes_t nframes ) Streamer has finished. */ -Peaks::Streamer::Streamer ( const char *filename, nframes_t chunksize, int channels ) +Peaks::Streamer::Streamer ( const char *filename, int channels, nframes_t chunksize ) { _channels = channels; _chunksize = chunksize; _index = 0; + _fp = NULL; _peak = new Peak[ channels ]; memset( _peak, 0, sizeof( Peak ) * channels ); if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) ) - /* error! */; + { + WARNING( "could not open peakfile for streaming." ); + } peakfile_block_header bh; @@ -620,6 +624,10 @@ Peaks::Streamer::Streamer ( const char *filename, nframes_t chunksize, int chann Peaks::Streamer::~Streamer ( ) { + fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp ); + + fflush( _fp ); + touch( fileno( _fp ) ); fclose( _fp ); @@ -645,7 +653,11 @@ Peaks::Streamer::write ( sample_t *buf, nframes_t nframes ) if ( _index == _chunksize - 1 ) { - fwrite( _peak, sizeof( Peak ), _channels, _fp ); + fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp ); + + /* FIXME: why the hell is this necessary? */ + fflush( _fp ); + memset( _peak, 0, sizeof( Peak ) * _channels ); _index = 0; } @@ -676,11 +688,13 @@ Peaks::Builder::write_block_header ( nframes_t chunksize ) 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 ); +// 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 ); + ASSERT( bh.skip, "Attempt to create empty block. pos=%lu, last_block_pos=%lu", pos, last_block_pos ); + +// DMESSAGE( "new block header: chunksize=%lu, skip=%lu", bh.chunksize, bh.skip ); fwrite( &bh, sizeof( bh ), 1, fp ); @@ -712,13 +726,27 @@ Peaks::Builder::make_peaks_mipmap ( void ) FILE *rfp; + rfp = fopen( peakname( filename ), "r" ); + last_block_pos = sizeof( peakfile_block_header ); /* open for reading */ - rfp = fopen( peakname( filename ), "r" ); +// rfp = fopen( peakname( filename ), "r" ); /* open the file again for appending */ - fp = fopen( peakname( filename ), "r+" ); - fseek( fp, 0, SEEK_END ); + if ( ! ( fp = fopen( peakname( filename ), "r+" ) ) ) + { + WARNING( "could not open peakfile for appending." ); + return false; + } + + if ( fseek( fp, 0, SEEK_END ) ) + FATAL( "error performing seek: %s", strerror( errno ) ); + + if ( ftell( fp ) == sizeof( peakfile_block_header ) ) + { + DWARNING( "truncated peakfile. Programming error?" ); + return false; + } Peak buf[ _clip->channels() ]; @@ -730,11 +758,22 @@ Peaks::Builder::make_peaks_mipmap ( void ) { DMESSAGE( "building level %d peak cache", i + 1 ); + DMESSAGE( "%lu", _clip->length() / cs ); + + if ( _clip->length() / cs < 1 ) + { + DMESSAGE( "source not long enough for any peaks at chunksize %lu", cs ); + break; + } + + Peakfile pf; /* open the peakfile for the previous cache level */ pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step ); +// pf.open( _clip->name(), _clip->channels(), cs >> Peaks::cache_step ); + write_block_header( cs ); size_t len; @@ -765,8 +804,6 @@ Peaks::Builder::make_peaks ( void ) DMESSAGE( "building peaks for \"%s\"", filename ); - FILE *rfp; - if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) ) return false; diff --git a/Timeline/Peaks.H b/Timeline/Peaks.H index 4e20eb1..ec1bcb2 100644 --- a/Timeline/Peaks.H +++ b/Timeline/Peaks.H @@ -64,7 +64,6 @@ class Peaks Peak *_peak; int _chunksize; int _channels; - int _index; /* not permitted */ @@ -73,7 +72,7 @@ class Peaks public: - Streamer ( const char *filename, nframes_t chunksize, int channels ); + Streamer ( const char *filename, int channels, nframes_t chunksize ); ~Streamer ( ); void write ( sample_t *buf, nframes_t nframes );