|
|
@@ -24,6 +24,8 @@ |
|
|
|
|
|
|
|
/* Code for peakfile reading, resampling, generation and streaming */ |
|
|
|
|
|
|
|
#include <FL/Fl.H> |
|
|
|
|
|
|
|
#include <sys/mman.h> |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/stat.h> |
|
|
@@ -68,20 +70,21 @@ Peaks::peakbuffer Peaks::_peakbuf; |
|
|
|
|
|
|
|
|
|
|
|
static |
|
|
|
const char * |
|
|
|
char * |
|
|
|
peakname ( const char *filename ) |
|
|
|
{ |
|
|
|
static char file[512]; |
|
|
|
char *file; |
|
|
|
|
|
|
|
snprintf( file, 512, "%s.peak", filename ); |
|
|
|
asprintf( &file, "%s.peak", filename ); |
|
|
|
|
|
|
|
return (const char*)&file; |
|
|
|
return file; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Peaks::Peaks ( Audio_File *c ) |
|
|
|
{ |
|
|
|
_pending = false; |
|
|
|
_clip = c; |
|
|
|
_peak_writer = NULL; |
|
|
|
} |
|
|
@@ -176,7 +179,7 @@ public: |
|
|
|
|
|
|
|
// printf( "chunksize=%lu, skip=%lu\n", (unsigned long)bh.chunksize, (unsigned long) bh.skip ); |
|
|
|
|
|
|
|
ASSERT( bh.chunksize, "Invalid peak file structure!" ); |
|
|
|
ASSERT( bh.chunksize, "Chucksize of zero. Invalid peak file structure!" ); |
|
|
|
|
|
|
|
blocks.push_back( block_descriptor( bh.chunksize, ftell( _fp ) ) ); |
|
|
|
|
|
|
@@ -192,7 +195,7 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
if ( ! blocks.size() ) |
|
|
|
FATAL( "invalid peak file?" ); |
|
|
|
FATAL( "Peak file contains no blocks!" ); |
|
|
|
|
|
|
|
// DMESSAGE( "peakfile has %d blocks.", blocks.size() ); |
|
|
|
|
|
|
@@ -251,8 +254,16 @@ public: |
|
|
|
_chunksize = 0; |
|
|
|
_channels = channels; |
|
|
|
|
|
|
|
if ( ! ( _fp = fopen( peakname( name ), "r" ) ) ) |
|
|
|
char *pn = peakname( name ); |
|
|
|
|
|
|
|
if ( ! ( _fp = fopen( pn, "r" ) ) ) |
|
|
|
{ |
|
|
|
WARNING( "Failed to open peakfile for reading: %s", strerror(errno) ); |
|
|
|
free( pn ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
free( pn ); |
|
|
|
|
|
|
|
scan( chunksize ); |
|
|
|
|
|
|
@@ -297,14 +308,19 @@ public: |
|
|
|
read_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize ) |
|
|
|
{ |
|
|
|
if ( ! _fp ) |
|
|
|
{ |
|
|
|
DMESSAGE( "No peakfile open, WTF?" ); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
const unsigned int ratio = chunksize / _chunksize; |
|
|
|
|
|
|
|
/* locate to start position */ |
|
|
|
if ( fseek( _fp, _offset + ( frame_to_peak( s ) * sizeof( Peak ) ), SEEK_SET ) ) |
|
|
|
/* failed to seek... peaks not ready? */ |
|
|
|
{ |
|
|
|
DMESSAGE( "failed to seek... peaks not ready?" ); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
if ( ratio == 1 ) |
|
|
|
return fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp ); |
|
|
@@ -355,6 +371,9 @@ public: |
|
|
|
bool |
|
|
|
Peaks::ready ( nframes_t s, int npeaks, nframes_t chunksize ) const |
|
|
|
{ |
|
|
|
/* if ( _pending ) */ |
|
|
|
/* return false; */ |
|
|
|
|
|
|
|
Peakfile _peakfile; |
|
|
|
|
|
|
|
if ( ! _peakfile.open( _clip->filename(), _clip->channels(), chunksize ) ) |
|
|
@@ -369,20 +388,25 @@ Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chu |
|
|
|
/* never try to build peaks while recording */ |
|
|
|
if ( ! transport->recording ) |
|
|
|
{ |
|
|
|
if ( ! current() ) |
|
|
|
if ( ! current() && ! _pending ) |
|
|
|
{ |
|
|
|
/* Build peaks asyncronously */ |
|
|
|
if ( ! fork() ) |
|
|
|
exit( make_peaks() ); |
|
|
|
else |
|
|
|
return 0; |
|
|
|
_pending = true; |
|
|
|
_make_peaks_thread.clone( &Peaks::make_peaks, const_cast<Peaks*>(this) ); |
|
|
|
_make_peaks_thread.detach(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* if ( _pending ) */ |
|
|
|
/* return 0; */ |
|
|
|
|
|
|
|
Peakfile _peakfile; |
|
|
|
|
|
|
|
if ( ! _peakfile.open( _clip->filename(), _clip->channels(), chunksize ) ) |
|
|
|
{ |
|
|
|
DMESSAGE( "Failed to open peakfile!" ); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
return _peakfile.read_peaks( peaks, s, npeaks, chunksize ); |
|
|
|
} |
|
|
@@ -458,9 +482,13 @@ Peaks::read_peaks ( nframes_t s, int npeaks, nframes_t chunksize ) const |
|
|
|
|
|
|
|
/* FIXME: use actual minimum chunksize from peakfile! */ |
|
|
|
if ( chunksize < (nframes_t)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 ); |
|
|
|
} |
|
|
|
|
|
|
|
return _peakbuf.len; |
|
|
|
} |
|
|
@@ -469,7 +497,22 @@ Peaks::read_peaks ( nframes_t s, int npeaks, nframes_t chunksize ) const |
|
|
|
bool |
|
|
|
Peaks::current ( void ) const |
|
|
|
{ |
|
|
|
return ! newer( _clip->filename(), peakname( _clip->filename() ) ); |
|
|
|
char *pn = peakname( _clip->filename() ); |
|
|
|
|
|
|
|
bool b = ! newer( _clip->filename(), pn ); |
|
|
|
|
|
|
|
free( pn ); |
|
|
|
|
|
|
|
return b; |
|
|
|
} |
|
|
|
|
|
|
|
/* thread entry point */ |
|
|
|
void * |
|
|
|
Peaks::make_peaks ( void *v ) |
|
|
|
{ |
|
|
|
((Peaks*)v)->make_peaks(); |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
bool |
|
|
@@ -477,7 +520,24 @@ Peaks::make_peaks ( void ) const |
|
|
|
{ |
|
|
|
Peaks::Builder pb( this ); |
|
|
|
|
|
|
|
return pb.make_peaks(); |
|
|
|
int b = pb.make_peaks(); |
|
|
|
|
|
|
|
_pending = false; |
|
|
|
|
|
|
|
Fl::lock(); |
|
|
|
timeline->redraw(); |
|
|
|
Fl::unlock(); |
|
|
|
|
|
|
|
return b; |
|
|
|
} |
|
|
|
|
|
|
|
/* thread entry point */ |
|
|
|
void * |
|
|
|
Peaks::make_peaks_mipmap ( void *v ) |
|
|
|
{ |
|
|
|
((Peaks*)v)->make_peaks_mipmap(); |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
bool |
|
|
@@ -485,7 +545,11 @@ Peaks::make_peaks_mipmap ( void ) const |
|
|
|
{ |
|
|
|
Peaks::Builder pb( this ); |
|
|
|
|
|
|
|
return pb.make_peaks_mipmap(); |
|
|
|
bool b = pb.make_peaks_mipmap(); |
|
|
|
|
|
|
|
_pending = false; |
|
|
|
|
|
|
|
return b; |
|
|
|
} |
|
|
|
|
|
|
|
/** return normalization factor for a single peak, assuming the peak |
|
|
@@ -511,7 +575,11 @@ Peaks::prepare_for_writing ( void ) |
|
|
|
|
|
|
|
assert( ! _peak_writer ); |
|
|
|
|
|
|
|
_peak_writer = new Peaks::Streamer( _clip->filename(), _clip->channels(), cache_minimum ); |
|
|
|
char *pn = peakname( _clip->filename() ); |
|
|
|
|
|
|
|
_peak_writer = new Peaks::Streamer( pn, _clip->channels(), cache_minimum ); |
|
|
|
|
|
|
|
free( pn ); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
@@ -522,10 +590,7 @@ Peaks::finish_writing ( void ) |
|
|
|
delete _peak_writer; |
|
|
|
_peak_writer = NULL; |
|
|
|
|
|
|
|
/* now fill in the rest of the cache */ |
|
|
|
if ( ! fork() ) |
|
|
|
exit( make_peaks_mipmap() ); |
|
|
|
|
|
|
|
make_peaks_mipmap(); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
@@ -556,9 +621,9 @@ Peaks::Streamer::Streamer ( const char *filename, int channels, nframes_t chunks |
|
|
|
_peak = new Peak[ channels ]; |
|
|
|
memset( _peak, 0, sizeof( Peak ) * channels ); |
|
|
|
|
|
|
|
if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) ) |
|
|
|
if ( ! ( _fp = fopen( filename, "w" ) ) ) |
|
|
|
{ |
|
|
|
WARNING( "could not open peakfile for streaming." ); |
|
|
|
FATAL( "could not open peakfile for streaming." ); |
|
|
|
} |
|
|
|
|
|
|
|
peakfile_block_header bh; |
|
|
@@ -575,8 +640,12 @@ Peaks::Streamer::~Streamer ( ) |
|
|
|
{ |
|
|
|
/* fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp ); */ |
|
|
|
|
|
|
|
fflush( _fp ); |
|
|
|
|
|
|
|
touch( fileno( _fp ) ); |
|
|
|
|
|
|
|
// fsync( fileno( _fp ) ); |
|
|
|
|
|
|
|
fclose( _fp ); |
|
|
|
|
|
|
|
delete[] _peak; |
|
|
@@ -595,7 +664,7 @@ Peaks::Streamer::write ( const sample_t *buf, nframes_t nframes ) |
|
|
|
fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp ); |
|
|
|
|
|
|
|
/* FIXME: shouldn't we just use write() instead? */ |
|
|
|
fflush( _fp ); |
|
|
|
// fflush( _fp ); |
|
|
|
|
|
|
|
memset( _peak, 0, sizeof( Peak ) * _channels ); |
|
|
|
|
|
|
@@ -648,13 +717,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", (unsigned long) bh.chunksize, (unsigned long) bh.skip ); |
|
|
|
|
|
|
|
bh.skip = pos - last_block_pos; |
|
|
|
|
|
|
|
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 ); |
|
|
|
// DMESSAGE( "new block header: chunksize=%lu, skip=%lu", (unsigned long) bh.chunksize, (unsigned long) bh.skip ); |
|
|
|
|
|
|
|
fwrite( &bh, sizeof( bh ), 1, fp ); |
|
|
|
|
|
|
@@ -673,6 +742,9 @@ Peaks::Builder::write_block_header ( nframes_t chunksize ) |
|
|
|
fflush( 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 ) |
|
|
@@ -683,22 +755,47 @@ Peaks::Builder::make_peaks_mipmap ( void ) |
|
|
|
Audio_File *_clip = _peaks->_clip; |
|
|
|
|
|
|
|
const char *filename = _clip->filename(); |
|
|
|
char *pn = peakname( filename ); |
|
|
|
|
|
|
|
FILE *rfp; |
|
|
|
|
|
|
|
if ( ! ( rfp = fopen( pn, "r" ) ) ) |
|
|
|
{ |
|
|
|
WARNING( "could not open peakfile for reading: %s.", strerror( errno ) ); |
|
|
|
free( pn ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
{ |
|
|
|
peakfile_block_header bh; |
|
|
|
|
|
|
|
rfp = fopen( peakname( filename ), "r" ); |
|
|
|
fread( &bh, sizeof( peakfile_block_header ), 1, rfp ); |
|
|
|
|
|
|
|
if ( bh.skip ) |
|
|
|
{ |
|
|
|
WARNING( "Peakfile already has multiple blocks..." ); |
|
|
|
fclose( rfp ); |
|
|
|
free( pn ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
last_block_pos = sizeof( peakfile_block_header ); |
|
|
|
|
|
|
|
/* open for reading */ |
|
|
|
// rfp = fopen( peakname( filename ), "r" ); |
|
|
|
|
|
|
|
/* open the file again for appending */ |
|
|
|
if ( ! ( fp = fopen( peakname( filename ), "r+" ) ) ) |
|
|
|
if ( ! ( fp = fopen( pn, "r+" ) ) ) |
|
|
|
{ |
|
|
|
WARNING( "could not open peakfile for appending." ); |
|
|
|
WARNING( "could not open peakfile for appending: %s.", strerror( errno ) ); |
|
|
|
free( pn ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
free( pn ); |
|
|
|
|
|
|
|
if ( fseek( fp, 0, SEEK_END ) ) |
|
|
|
FATAL( "error performing seek: %s", strerror( errno ) ); |
|
|
|
|
|
|
@@ -714,9 +811,10 @@ Peaks::Builder::make_peaks_mipmap ( void ) |
|
|
|
* 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 ); |
|
|
|
DMESSAGE( "building level %d peak cache cs=%i", i + 1, cs ); |
|
|
|
|
|
|
|
/* DMESSAGE( "%lu", _clip->length() / cs ); */ |
|
|
|
|
|
|
@@ -726,10 +824,10 @@ Peaks::Builder::make_peaks_mipmap ( void ) |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Peakfile pf; |
|
|
|
|
|
|
|
/* open the peakfile for the previous cache level */ |
|
|
|
|
|
|
|
pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step ); |
|
|
|
|
|
|
|
// pf.open( _clip->filename(), _clip->channels(), cs >> Peaks::cache_step ); |
|
|
@@ -740,12 +838,16 @@ Peaks::Builder::make_peaks_mipmap ( void ) |
|
|
|
nframes_t s = 0; |
|
|
|
do { |
|
|
|
len = pf.read_peaks( buf, s, 1, cs ); |
|
|
|
|
|
|
|
s += cs; |
|
|
|
|
|
|
|
fwrite( buf, sizeof( buf ), len, fp ); |
|
|
|
} |
|
|
|
while ( len ); |
|
|
|
|
|
|
|
/* fflush( fp ); */ |
|
|
|
/* fsync( fileno( fp ) ); */ |
|
|
|
|
|
|
|
pf.leave_open(); |
|
|
|
} |
|
|
|
|
|
|
@@ -766,8 +868,15 @@ Peaks::Builder::make_peaks ( void ) |
|
|
|
|
|
|
|
DMESSAGE( "building peaks for \"%s\"", filename ); |
|
|
|
|
|
|
|
if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) ) |
|
|
|
char *pn = peakname( filename ); |
|
|
|
|
|
|
|
if ( ! ( fp = fopen( pn, "w+" ) ) ) |
|
|
|
{ |
|
|
|
free( pn ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
free( pn ); |
|
|
|
|
|
|
|
_clip->seek( 0 ); |
|
|
|
|
|
|
@@ -787,6 +896,8 @@ Peaks::Builder::make_peaks ( void ) |
|
|
|
while ( len ); |
|
|
|
|
|
|
|
/* reopen for reading */ |
|
|
|
/* fflush( fp ); */ |
|
|
|
/* fsync( fileno( fp ) ); */ |
|
|
|
fclose( fp ); |
|
|
|
|
|
|
|
make_peaks_mipmap(); |
|
|
|