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;
|
static std::map <std::string, Audio_File*> _open_files;
|
||||||
|
|
||||||
|
/* not permitted */
|
||||||
Audio_File ( const Audio_File &rhs );
|
Audio_File ( const Audio_File &rhs );
|
||||||
const Audio_File & operator= ( const Audio_File &rhs );
|
const Audio_File & operator= ( const Audio_File &rhs );
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *_filename;
|
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 */
|
nframes_t _samplerate; /* sample rate */
|
||||||
int _channels;
|
int _channels;
|
||||||
|
|
||||||
|
@ -70,6 +71,7 @@ public:
|
||||||
Audio_File ( ) : _peaks( this )
|
Audio_File ( ) : _peaks( this )
|
||||||
{
|
{
|
||||||
_filename = NULL;
|
_filename = NULL;
|
||||||
|
_samplerate = 0;
|
||||||
_length = _channels = 0;
|
_length = _channels = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,6 @@ Audio_File_SF::create ( const char *filename, nframes_t samplerate, int channels
|
||||||
char *name;
|
char *name;
|
||||||
asprintf( &name, "%s.%s", filename, fd->extension );
|
asprintf( &name, "%s.%s", filename, fd->extension );
|
||||||
|
|
||||||
// if ( ! ( out = sf_open( name, SFM_RDWR, &si ) ) )
|
|
||||||
if ( ! ( out = sf_open( name, SFM_WRITE, &si ) ) )
|
if ( ! ( out = sf_open( name, SFM_WRITE, &si ) ) )
|
||||||
{
|
{
|
||||||
printf( "couldn't create soundfile.\n" );
|
printf( "couldn't create soundfile.\n" );
|
||||||
|
|
|
@ -31,6 +31,12 @@ class Audio_File_SF : public Audio_File
|
||||||
* enough to do this for us */
|
* enough to do this for us */
|
||||||
nframes_t _current_read;
|
nframes_t _current_read;
|
||||||
|
|
||||||
|
Audio_File_SF ( )
|
||||||
|
{
|
||||||
|
_in = 0;
|
||||||
|
_current_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static const Audio_File::format_desc supported_formats[];
|
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 *from_file ( const char *filename );
|
||||||
static Audio_File_SF *create ( const char *filename, nframes_t samplerate, int channels, const char *format );
|
static Audio_File_SF *create ( const char *filename, nframes_t samplerate, int channels, const char *format );
|
||||||
|
|
||||||
|
|
||||||
~Audio_File_SF ( )
|
~Audio_File_SF ( )
|
||||||
{
|
{
|
||||||
/* stupid C++ */
|
/* stupid C++ */
|
||||||
|
|
|
@ -840,7 +840,8 @@ Audio_Region::write ( nframes_t nframes )
|
||||||
{
|
{
|
||||||
nframes_t oldl = _clip->length();
|
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->close();
|
||||||
_clip->open();
|
_clip->open();
|
||||||
|
|
||||||
|
@ -858,6 +859,7 @@ Audio_Region::write ( nframes_t nframes )
|
||||||
return nframes;
|
return nframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* THREAD: IO */
|
||||||
/** finalize region capture. Assumes that this *is* a captured region
|
/** finalize region capture. Assumes that this *is* a captured region
|
||||||
and that no other regions refer to the same source */
|
and that no other regions refer to the same source */
|
||||||
bool
|
bool
|
||||||
|
@ -865,11 +867,11 @@ Audio_Region::finalize ( nframes_t frame )
|
||||||
{
|
{
|
||||||
log_end();
|
log_end();
|
||||||
|
|
||||||
|
_clip->finalize();
|
||||||
|
|
||||||
_clip->close();
|
_clip->close();
|
||||||
_clip->open();
|
_clip->open();
|
||||||
|
|
||||||
_clip->finalize();
|
|
||||||
|
|
||||||
/* FIXME: should we attempt to truncate the file? */
|
/* FIXME: should we attempt to truncate the file? */
|
||||||
|
|
||||||
_range.length = frame - _range.start - _range.offset;
|
_range.length = frame - _range.start - _range.offset;
|
||||||
|
|
|
@ -140,7 +140,6 @@ class Peakfile
|
||||||
nframes_t _chunksize;
|
nframes_t _chunksize;
|
||||||
int _channels; /* number of channels this peakfile represents */
|
int _channels; /* number of channels this peakfile represents */
|
||||||
nframes_t _length; /* length, in frames, of the clip this peakfile represents */
|
nframes_t _length; /* length, in frames, of the clip this peakfile represents */
|
||||||
const char *_name;
|
|
||||||
size_t _offset;
|
size_t _offset;
|
||||||
int _blocks;
|
int _blocks;
|
||||||
|
|
||||||
|
@ -169,7 +168,6 @@ public:
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
_chunksize = 0;
|
_chunksize = 0;
|
||||||
_channels = 0;
|
_channels = 0;
|
||||||
_name =NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Peakfile ( )
|
~Peakfile ( )
|
||||||
|
@ -195,11 +193,14 @@ public:
|
||||||
|
|
||||||
fread( &bh, sizeof( bh ), 1, _fp );
|
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!" );
|
ASSERT( bh.chunksize, "Invalid peak file structure!" );
|
||||||
|
|
||||||
blocks.push_back( block_descriptor( bh.chunksize, ftell( _fp ) ) );
|
blocks.push_back( block_descriptor( bh.chunksize, ftell( _fp ) ) );
|
||||||
|
|
||||||
if ( ! bh.skip )
|
if ( ! bh.skip )
|
||||||
|
/* last block */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if ( fseek( _fp, bh.skip, SEEK_CUR ) )
|
if ( fseek( _fp, bh.skip, SEEK_CUR ) )
|
||||||
|
@ -265,11 +266,10 @@ public:
|
||||||
|
|
||||||
/** given soundfile name /name/, try to open the best peakfile for /chunksize/ */
|
/** given soundfile name /name/, try to open the best peakfile for /chunksize/ */
|
||||||
bool
|
bool
|
||||||
open ( const char *name, nframes_t chunksize, int channels )
|
open ( const char *name, int channels, nframes_t chunksize )
|
||||||
{
|
{
|
||||||
_chunksize = 0;
|
_chunksize = 0;
|
||||||
_channels = channels;
|
_channels = channels;
|
||||||
_name = name;
|
|
||||||
|
|
||||||
if ( ! ( _fp = fopen( peakname( name ), "r" ) ) )
|
if ( ! ( _fp = fopen( peakname( name ), "r" ) ) )
|
||||||
return false;
|
return false;
|
||||||
|
@ -311,7 +311,7 @@ public:
|
||||||
* channels. Place the result in buffer /peaks/, which must be
|
* channels. Place the result in buffer /peaks/, which must be
|
||||||
* large enough to fit the entire request. Returns the number of
|
* large enough to fit the entire request. Returns the number of
|
||||||
* peaks actually read, which may be fewer than were requested. */
|
* 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 )
|
read_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chunksize )
|
||||||
{
|
{
|
||||||
if ( ! _fp )
|
if ( ! _fp )
|
||||||
|
@ -320,22 +320,18 @@ public:
|
||||||
const unsigned int ratio = chunksize / _chunksize;
|
const unsigned int ratio = chunksize / _chunksize;
|
||||||
|
|
||||||
/* locate to start position */
|
/* 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? */
|
/* failed to seek... peaks not ready? */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( ratio == 1 )
|
if ( ratio == 1 )
|
||||||
{
|
return fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp );
|
||||||
int len = fread( peaks, sizeof( Peak ) * _channels, npeaks, _fp );
|
|
||||||
// close;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
Peak *pbuf = new Peak[ ratio * _channels ];
|
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 )
|
for ( i = 0; i < npeaks; ++i )
|
||||||
{
|
{
|
||||||
/* read in a buffer */
|
/* read in a buffer */
|
||||||
|
@ -378,7 +374,7 @@ Peaks::ready ( nframes_t s, int npeaks, nframes_t chunksize ) const
|
||||||
{
|
{
|
||||||
Peakfile _peakfile;
|
Peakfile _peakfile;
|
||||||
|
|
||||||
if ( ! _peakfile.open( _clip->name(), chunksize, _clip->channels() ) )
|
if ( ! _peakfile.open( _clip->name(), _clip->channels(), chunksize ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return _peakfile.ready( s, npeaks );
|
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 );
|
nframes_t ncc = nearest_cached_chunksize( chunksize );
|
||||||
|
|
||||||
/* never try to build peaks while recording */
|
/* never try to build peaks while recording */
|
||||||
if ( ! ( _peak_writer || transport->recording ) )
|
if ( ! transport->recording )
|
||||||
{
|
{
|
||||||
if ( ! current() )
|
if ( ! current() )
|
||||||
/* Build peaks asyncronously */
|
/* Build peaks asyncronously */
|
||||||
|
@ -403,7 +399,7 @@ Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, int npeaks, nframes_t chu
|
||||||
|
|
||||||
Peakfile _peakfile;
|
Peakfile _peakfile;
|
||||||
|
|
||||||
if ( ! _peakfile.open( _clip->name(), chunksize, _clip->channels() ) )
|
if ( ! _peakfile.open( _clip->name(), _clip->channels(), chunksize ) )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* else if ( ! _peakfile.contains( s, npeaks ) ) */
|
/* else if ( ! _peakfile.contains( s, npeaks ) ) */
|
||||||
|
@ -556,15 +552,17 @@ Peak::normalization_factor( void ) const
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* THREAD: IO */
|
||||||
/* wrapper for peak writer */
|
/* wrapper for peak writer */
|
||||||
void
|
void
|
||||||
Peaks::prepare_for_writing ( void )
|
Peaks::prepare_for_writing ( void )
|
||||||
{
|
{
|
||||||
assert( ! _peak_writer );
|
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
|
void
|
||||||
Peaks::finish_writing ( void )
|
Peaks::finish_writing ( void )
|
||||||
{
|
{
|
||||||
|
@ -575,11 +573,14 @@ Peaks::finish_writing ( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now fill in the rest of the cache */
|
/* 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
|
void
|
||||||
Peaks::write ( sample_t *buf, nframes_t nframes )
|
Peaks::write ( sample_t *buf, nframes_t nframes )
|
||||||
{
|
{
|
||||||
|
@ -596,17 +597,20 @@ Peaks::write ( sample_t *buf, nframes_t nframes )
|
||||||
Streamer has finished.
|
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;
|
_channels = channels;
|
||||||
_chunksize = chunksize;
|
_chunksize = chunksize;
|
||||||
_index = 0;
|
_index = 0;
|
||||||
|
_fp = NULL;
|
||||||
|
|
||||||
_peak = new Peak[ channels ];
|
_peak = new Peak[ channels ];
|
||||||
memset( _peak, 0, sizeof( Peak ) * channels );
|
memset( _peak, 0, sizeof( Peak ) * channels );
|
||||||
|
|
||||||
if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) )
|
if ( ! ( _fp = fopen( peakname( filename ), "w" ) ) )
|
||||||
/* error! */;
|
{
|
||||||
|
WARNING( "could not open peakfile for streaming." );
|
||||||
|
}
|
||||||
|
|
||||||
peakfile_block_header bh;
|
peakfile_block_header bh;
|
||||||
|
|
||||||
|
@ -620,6 +624,10 @@ Peaks::Streamer::Streamer ( const char *filename, nframes_t chunksize, int chann
|
||||||
|
|
||||||
Peaks::Streamer::~Streamer ( )
|
Peaks::Streamer::~Streamer ( )
|
||||||
{
|
{
|
||||||
|
fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp );
|
||||||
|
|
||||||
|
fflush( _fp );
|
||||||
|
|
||||||
touch( fileno( _fp ) );
|
touch( fileno( _fp ) );
|
||||||
|
|
||||||
fclose( _fp );
|
fclose( _fp );
|
||||||
|
@ -645,7 +653,11 @@ Peaks::Streamer::write ( sample_t *buf, nframes_t nframes )
|
||||||
|
|
||||||
if ( _index == _chunksize - 1 )
|
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 );
|
memset( _peak, 0, sizeof( Peak ) * _channels );
|
||||||
_index = 0;
|
_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, last_block_pos - sizeof( peakfile_block_header ), SEEK_SET );
|
||||||
// fseek( fp, 0 - sizeof( bh ), SEEK_CUR );
|
// 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;
|
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 );
|
fwrite( &bh, sizeof( bh ), 1, fp );
|
||||||
|
|
||||||
|
@ -712,13 +726,27 @@ Peaks::Builder::make_peaks_mipmap ( void )
|
||||||
|
|
||||||
FILE *rfp;
|
FILE *rfp;
|
||||||
|
|
||||||
|
rfp = fopen( peakname( filename ), "r" );
|
||||||
|
|
||||||
last_block_pos = sizeof( peakfile_block_header );
|
last_block_pos = sizeof( peakfile_block_header );
|
||||||
|
|
||||||
/* open for reading */
|
/* open for reading */
|
||||||
rfp = fopen( peakname( filename ), "r" );
|
// rfp = fopen( peakname( filename ), "r" );
|
||||||
/* open the file again for appending */
|
/* open the file again for appending */
|
||||||
fp = fopen( peakname( filename ), "r+" );
|
if ( ! ( fp = fopen( peakname( filename ), "r+" ) ) )
|
||||||
fseek( fp, 0, SEEK_END );
|
{
|
||||||
|
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() ];
|
Peak buf[ _clip->channels() ];
|
||||||
|
|
||||||
|
@ -730,11 +758,22 @@ Peaks::Builder::make_peaks_mipmap ( void )
|
||||||
{
|
{
|
||||||
DMESSAGE( "building level %d peak cache", i + 1 );
|
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;
|
Peakfile pf;
|
||||||
|
|
||||||
/* open the peakfile for the previous cache level */
|
/* open the peakfile for the previous cache level */
|
||||||
pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step );
|
pf.open( rfp, _clip->channels(), cs >> Peaks::cache_step );
|
||||||
|
|
||||||
|
// pf.open( _clip->name(), _clip->channels(), cs >> Peaks::cache_step );
|
||||||
|
|
||||||
write_block_header( cs );
|
write_block_header( cs );
|
||||||
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -765,8 +804,6 @@ Peaks::Builder::make_peaks ( void )
|
||||||
|
|
||||||
DMESSAGE( "building peaks for \"%s\"", filename );
|
DMESSAGE( "building peaks for \"%s\"", filename );
|
||||||
|
|
||||||
FILE *rfp;
|
|
||||||
|
|
||||||
if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) )
|
if ( ! ( fp = fopen( peakname( filename ), "w+" ) ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ class Peaks
|
||||||
Peak *_peak;
|
Peak *_peak;
|
||||||
int _chunksize;
|
int _chunksize;
|
||||||
int _channels;
|
int _channels;
|
||||||
|
|
||||||
int _index;
|
int _index;
|
||||||
|
|
||||||
/* not permitted */
|
/* not permitted */
|
||||||
|
@ -73,7 +72,7 @@ class Peaks
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Streamer ( const char *filename, nframes_t chunksize, int channels );
|
Streamer ( const char *filename, int channels, nframes_t chunksize );
|
||||||
~Streamer ( );
|
~Streamer ( );
|
||||||
|
|
||||||
void write ( sample_t *buf, nframes_t nframes );
|
void write ( sample_t *buf, nframes_t nframes );
|
||||||
|
|
Loading…
Reference in New Issue