Fix mipmapping for streamed peaks.
This commit is contained in:
parent
12d65acf25
commit
50fa642321
|
@ -35,6 +35,7 @@ class Audio_File
|
|||
{
|
||||
static std::map <std::string, Audio_File*> _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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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++ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue