Clean up Peaks.C

This commit is contained in:
Jonathan Moore Liles 2008-05-11 09:24:38 -05:00
parent d1a2f52352
commit f12363340e
2 changed files with 249 additions and 261 deletions

View File

@ -25,8 +25,6 @@
#include "Peaks.H" #include "Peaks.H"
// #include "Timeline.H"
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -40,50 +38,25 @@
#include "assert.h" #include "assert.h"
#include <math.h>
#include <FL/Fl.H> // for Fl::check();
#include "debug.h" #include "debug.h"
#include <errno.h> #include <errno.h>
#include "Transport.H" // for .recording #include "Transport.H" // for .recording
#include <list> #include <list>
Peaks::peakbuffer Peaks::_peakbuf; /* whether to cache peaks at multiple resolutions on disk to
* drastically improve performance */
bool Peaks::mipmapped_peakfiles = true; 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_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_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, ... */ const int Peaks::cache_step = 1; /* powers of two between each level. 4 == 256, 2048, 16384, ... */
/* Peaks ( ) */
/* { */
/* _clip = NULL; */
/* } */
Peaks::Peaks ( Audio_File *c ) Peaks::peakbuffer Peaks::_peakbuf;
{
_clip = c;
_peak_writer = NULL;
}
Peaks::~Peaks ( )
{
if ( _peak_writer )
delete _peak_writer;
}
static static
const char * const char *
@ -96,16 +69,18 @@ peakname ( const char *filename )
return (const char*)&file; return (const char*)&file;
} }
/** Prepare a buffer of peaks from /s/ to /e/ for reading. Must be /** update the modification time of file referred to by /fd/ */
* called before any calls to operator[] */ static void
int touch ( int fd )
Peaks::fill_buffer ( float fpp, nframes_t s, nframes_t e ) const
{ {
_fpp = fpp; struct stat st;
return read_peaks( s, e, (e - s) / fpp, fpp ); fstat( fd, &st );
fchmod( fd, st.st_mode );
} }
static int static int
nearest_power_of_two ( int v ) nearest_power_of_two ( int v )
{ {
@ -128,6 +103,30 @@ nearest_cached_chunksize ( nframes_t chunksize )
return 0; 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 struct peakfile_block_header
{ {
unsigned long chunksize; unsigned long chunksize;
@ -230,6 +229,7 @@ public:
break; break;
} }
// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize );
_offset = ftell( _fp ); _offset = ftell( _fp );
} }
@ -364,11 +364,8 @@ public:
delete[] pbuf; delete[] pbuf;
// close();
return i; return i;
} }
}; };
@ -456,13 +453,10 @@ Peaks::read_source_peaks ( Peak *peaks, int npeaks, nframes_t chunksize ) const
int int
Peaks::read_source_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const Peaks::read_source_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) const
{ {
// _clip->open();
_clip->seek( s ); _clip->seek( s );
int i = read_source_peaks( peaks, npeaks, chunksize ); int i = read_source_peaks( peaks, npeaks, chunksize );
// _clip->close();
return i; return i;
} }
@ -481,8 +475,8 @@ Peaks::read_peaks ( nframes_t s, nframes_t e, int npeaks, nframes_t chunksize )
_peakbuf.offset = s; _peakbuf.offset = s;
_peakbuf.buf->chunksize = chunksize; _peakbuf.buf->chunksize = chunksize;
/* FIXME: compart to (minimum) peakfile chunk size */ /* FIXME: use actual minimum chunksize from peakfile! */
if ( chunksize < 256 ) if ( chunksize < cache_minimum )
_peakbuf.len = read_source_peaks( _peakbuf.buf->data, s, npeaks, chunksize ); _peakbuf.len = read_source_peaks( _peakbuf.buf->data, s, npeaks, chunksize );
else else
_peakbuf.len = read_peakfile_peaks( _peakbuf.buf->data, s, npeaks, chunksize ); _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; 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 */ /** returns false if peak file for /filename/ is out of date */
bool bool
Peaks::current ( void ) const Peaks::current ( void ) const
@ -521,29 +508,144 @@ Peaks::current ( void ) const
} }
static void
touch ( int fd ) bool
Peaks::make_peaks ( void ) const
{ {
struct stat st; Peaks::Builder pb( this );
fstat( fd, &st ); return pb.make_peaks();
fchmod( fd, st.st_mode );
} }
bool
/* The Peak_Builder is for generating peaks from imported or updated sources, or when the Peaks::make_peaks_mipmap ( void ) const
peakfile is simply missing */
class Peak_Builder
{ {
FILE *fp; Peaks::Builder pb( this );
size_t last_block_pos;
const Peaks *_peaks;
void return pb.make_peaks_mipmap();
write_block_header ( nframes_t chunksize ) }
/** return normalization factor for a single peak, assuming the peak
* represents a downsampling of the entire range to be normalized. */
float
Peak::normalization_factor( void ) const
{
float s;
s = 1.0f / fabs( this->max );
if ( s * this->min < -1.0 )
s = 1.0f / fabs( this->min );
return s;
}
/* wrapper for peak writer */
void
Peaks::prepare_for_writing ( void )
{
assert( ! _peak_writer );
_peak_writer = new Peaks::Streamer( _clip->name(), cache_minimum, _clip->channels() );
}
void
Peaks::finish_writing ( void )
{
if ( _peak_writer )
{ {
delete _peak_writer;
_peak_writer = NULL;
}
/* now fill in the rest of the cache */
if ( ! fork() )
exit( make_peaks_mipmap() );
}
void
Peaks::write ( sample_t *buf, nframes_t nframes )
{
_peak_writer->write( buf, nframes );
}
/*
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;
_index = 0;
_peak = new Peak[ channels ];
memset( _peak, 0, sizeof( Peak ) * channels );
if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) )
/* error! */;
peakfile_block_header bh;
bh.chunksize = chunksize;
bh.skip = 0;
fwrite( &bh, sizeof( bh ), 1, _fp );
fflush( _fp );
}
Peaks::Streamer::~Streamer ( )
{
touch( fileno( _fp ) );
fclose( _fp );
delete[] _peak;
}
/** append peaks for samples in /buf/ to peakfile */
void
Peaks::Streamer::write ( sample_t *buf, nframes_t nframes )
{
for ( ; nframes--; ++_index, buf += _channels )
{
for ( int j = 0; j < _channels; ++j )
{
Peak *p = _peak + j;
if ( *buf > p->max )
p->max = *buf;
if ( *buf < p->min )
p->min = *buf;
}
if ( _index == _chunksize - 1 )
{
fwrite( _peak, sizeof( Peak ), _channels, _fp );
memset( _peak, 0, sizeof( Peak ) * _channels );
_index = 0;
}
}
}
/*
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 ) if ( last_block_pos )
{ {
/* update previous block */ /* update previous block */
@ -577,14 +679,12 @@ class Peak_Builder
fwrite( &bh, sizeof( bh ), 1, fp ); fwrite( &bh, sizeof( bh ), 1, fp );
last_block_pos = ftell( 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
/** generate additional cache levels for a peakfile with only 1 block (ie. that of a new capture) */ Peaks::Builder::make_peaks_mipmap ( void )
bool {
make_peaks_mipmap ( void )
{
if ( ! Peaks::mipmapped_peakfiles ) if ( ! Peaks::mipmapped_peakfiles )
return true; return true;
@ -638,11 +738,11 @@ public:
fclose( fp ); fclose( fp );
return true; return true;
} }
bool bool
make_peaks ( void ) Peaks::Builder::make_peaks ( void )
{ {
Audio_File *_clip = _peaks->_clip; Audio_File *_clip = _peaks->_clip;
const char *filename = _clip->name(); const char *filename = _clip->name();
@ -679,135 +779,13 @@ public:
DMESSAGE( "done building peaks" ); DMESSAGE( "done building peaks" );
return true; return true;
} }
Peak_Builder ( const Peaks *peaks ) : _peaks( peaks ) Peaks::Builder::Builder ( const Peaks *peaks ) : _peaks( peaks )
{ {
fp = NULL; fp = NULL;
last_block_pos = 0; last_block_pos = 0;
}
};
bool
Peaks::make_peaks ( void ) const
{
Peak_Builder pb( this );
return pb.make_peaks();
} }
bool
Peaks::make_peaks_mipmap ( void ) const
{
Peak_Builder pb( this );
return pb.make_peaks_mipmap();
}
/** return normalization factor for a single peak, assuming the peak
* represents a downsampling of the entire range to be normalized. */
float
Peak::normalization_factor( void ) const
{
float s;
s = 1.0f / fabs( this->max );
if ( s * this->min < -1.0 )
s = 1.0f / fabs( this->min );
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() );
}
void
Peaks::finish_writing ( void )
{
if ( _peak_writer )
{
delete _peak_writer;
_peak_writer = NULL;
}
/* now fill in the rest of the cache */
if ( ! fork() )
exit( make_peaks_mipmap() );
}
void
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 )
{
_channels = channels;
_chunksize = chunksize;
_index = 0;
_peak = new Peak[ channels ];
memset( _peak, 0, sizeof( Peak ) * channels );
if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) )
/* error! */;
peakfile_block_header bh;
bh.chunksize = chunksize;
bh.skip = 0;
fwrite( &bh, sizeof( bh ), 1, _fp );
fflush( _fp );
}
Peak_Writer::~Peak_Writer ( )
{
touch( fileno( _fp ) );
fclose( _fp );
delete[] _peak;
}
/** append peaks for samples in /buf/ to peakfile */
void
Peak_Writer::write ( sample_t *buf, nframes_t nframes )
{
for ( ; nframes--; ++_index, buf += _channels )
{
for ( int j = 0; j < _channels; ++j )
{
Peak *p = _peak + j;
if ( *buf > p->max )
p->max = *buf;
if ( *buf < p->min )
p->min = *buf;
}
if ( _index == _chunksize - 1 )
{
fwrite( _peak, sizeof( Peak ), _channels, _fp );
memset( _peak, 0, sizeof( Peak ) * _channels );
_index = 0;
}
}
}

View File

@ -30,8 +30,9 @@ struct Peak {
float normalization_factor ( void ) const; float normalization_factor ( void ) const;
}; };
#include <stdio.h>
class Audio_File; class Audio_File;
class Peak_Writer;
class Peaks 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; static peakbuffer _peakbuf;
Audio_File *_clip; Audio_File *_clip;
@ -68,16 +107,12 @@ class Peaks
int read_source_peaks ( Peak *peaks, 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; 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 */ Streamer * volatile _peak_writer; /* exists when streaming peaks to disk */
size_t last_block_pos; /* FIXME: only needed in make_peaks! */
/* not permitted */ /* not permitted */
Peaks ( const Peaks &rhs ); Peaks ( const Peaks &rhs );
const Peaks &operator= ( const Peaks &rhs ); const Peaks &operator= ( const Peaks &rhs );
friend class Peak_Builder;
public: public:
static bool mipmapped_peakfiles; static bool mipmapped_peakfiles;
@ -97,7 +132,6 @@ public:
int fill_buffer ( float fpp, nframes_t s, nframes_t e ) const; int fill_buffer ( float fpp, nframes_t s, nframes_t e ) const;
void read ( int X, float *hi, float *lo ) const; void read ( int X, float *hi, float *lo ) const;
bool open ( void );
bool current ( void ) const; bool current ( void ) const;
bool make_peaks ( void ) const; bool make_peaks ( void ) const;
@ -107,27 +141,3 @@ public:
void finish_writing ( void ); void finish_writing ( void );
void write ( sample_t *buf, nframes_t nframes ); void write ( sample_t *buf, nframes_t nframes );
}; };
#include <stdio.h>
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 );
};