Make normalization code work at all zoom levels.
This commit is contained in:
parent
a032a95fad
commit
3b02169d31
80
Clip.C
80
Clip.C
|
@ -25,30 +25,94 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
Clip::Clip ( const char *filename ) : _peaks( this )
|
|
||||||
{
|
|
||||||
_filename = filename;
|
|
||||||
|
|
||||||
|
Clip::Clip ( void ) : _peaks( this )
|
||||||
|
{
|
||||||
|
_filename = NULL;
|
||||||
|
_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Clip::Clip ( const char *filename ) : _peaks( this ) */
|
||||||
|
/* { */
|
||||||
|
/* _filename = filename; */
|
||||||
|
|
||||||
|
/* SNDFILE *in; */
|
||||||
|
/* SF_INFO si; */
|
||||||
|
|
||||||
|
/* memset( &si, 0, sizeof( si ) ); */
|
||||||
|
|
||||||
|
/* if ( ! ( in = sf_open( filename, SFM_READ, &si ) ) ) */
|
||||||
|
/* { */
|
||||||
|
/* printf( "couldn't open file\n" ); */
|
||||||
|
/* return; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* if ( si.channels != 1 ) */
|
||||||
|
/* { */
|
||||||
|
/* printf( "error: incompatible format\n" ); */
|
||||||
|
/* return; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* if ( si.samplerate != timeline.sample_rate ) */
|
||||||
|
/* { */
|
||||||
|
/* printf( "error: samplerate mismatch!\n" ); */
|
||||||
|
/* return; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* _length = si.frames; */
|
||||||
|
|
||||||
|
/* sf_close( in ); */
|
||||||
|
|
||||||
|
/* _peaks.open(); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
|
||||||
|
Clip *
|
||||||
|
Clip::from_file ( const char *filename )
|
||||||
|
{
|
||||||
SNDFILE *in;
|
SNDFILE *in;
|
||||||
SF_INFO si;
|
SF_INFO si;
|
||||||
|
|
||||||
|
Clip *c = NULL;
|
||||||
|
|
||||||
memset( &si, 0, sizeof( si ) );
|
memset( &si, 0, sizeof( si ) );
|
||||||
|
|
||||||
in = sf_open( filename, SFM_READ, &si );
|
if ( ! ( in = sf_open( filename, SFM_READ, &si ) ) )
|
||||||
|
{
|
||||||
|
printf( "couldn't open file\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if ( si.channels != 1 )
|
if ( si.channels != 1 )
|
||||||
printf( "error: incompatible format" );
|
{
|
||||||
|
printf( "error: incompatible format\n" );
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
if ( si.samplerate != timeline.sample_rate )
|
if ( si.samplerate != timeline.sample_rate )
|
||||||
|
{
|
||||||
printf( "error: samplerate mismatch!\n" );
|
printf( "error: samplerate mismatch!\n" );
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
_length = si.frames;
|
c = new Clip;
|
||||||
|
|
||||||
|
c->_filename = filename;
|
||||||
|
c->_length = si.frames;
|
||||||
|
|
||||||
sf_close( in );
|
sf_close( in );
|
||||||
|
|
||||||
_peaks.open();
|
c->_peaks.open();
|
||||||
}
|
|
||||||
|
|
||||||
|
return c;
|
||||||
|
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
|
||||||
|
sf_close( in );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Clip::open ( void )
|
Clip::open ( void )
|
||||||
|
|
6
Clip.H
6
Clip.H
|
@ -38,7 +38,11 @@ class Clip
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Clip ( const char *filename );
|
Clip ( );
|
||||||
|
|
||||||
|
// Clip ( const char *filename );
|
||||||
|
|
||||||
|
static Clip *from_file ( const char *filename );
|
||||||
|
|
||||||
Peaks const * peaks ( void ) { return &_peaks; }
|
Peaks const * peaks ( void ) { return &_peaks; }
|
||||||
const char *name ( void ) { return _filename; }
|
const char *name ( void ) { return _filename; }
|
||||||
|
|
62
Peaks.C
62
Peaks.C
|
@ -35,6 +35,8 @@
|
||||||
|
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
Peaks::peakbuffer Peaks::peakbuf;
|
Peaks::peakbuffer Peaks::peakbuf;
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +60,7 @@ Peaks::fill_buffer ( int s, int e ) const
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Peaks::downsample ( int s, int e, float *mhi, float *mlo ) const
|
Peaks::downsample ( Peak *peaks, int s, int e, float *mhi, float *mlo ) const
|
||||||
{
|
{
|
||||||
*mhi = 0;
|
*mhi = 0;
|
||||||
*mlo = 0;
|
*mlo = 0;
|
||||||
|
@ -68,8 +70,8 @@ Peaks::downsample ( int s, int e, float *mhi, float *mlo ) const
|
||||||
|
|
||||||
for ( int j = s; j < e; j++ )
|
for ( int j = s; j < e; j++ )
|
||||||
{
|
{
|
||||||
const float lo = _peaks->data[ j ].min;
|
const float lo = peaks[ j ].min;
|
||||||
const float hi = _peaks->data[ j ].max;
|
const float hi = peaks[ j ].max;
|
||||||
|
|
||||||
if ( hi > *mhi )
|
if ( hi > *mhi )
|
||||||
*mhi = hi;
|
*mhi = hi;
|
||||||
|
@ -135,12 +137,9 @@ Peaks::read_peaks ( int s, int e, int npeaks, int chunksize ) const
|
||||||
_clip->close();
|
_clip->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return the peak for the range of samples */
|
||||||
/* virtual array. Index is a Pixel value, and it returns the
|
|
||||||
* (resampled) peaks for that pixel based on the current timeline
|
|
||||||
* zoom. */
|
|
||||||
Peak &
|
Peak &
|
||||||
Peaks::operator[] ( int X ) const
|
Peaks::peak ( nframes_t start, nframes_t end ) const
|
||||||
{
|
{
|
||||||
/* Is there a better way to return this? */
|
/* Is there a better way to return this? */
|
||||||
static Peak p;
|
static Peak p;
|
||||||
|
@ -149,24 +148,36 @@ Peaks::operator[] ( int X ) const
|
||||||
{
|
{
|
||||||
assert( timeline.fpp == peakbuf.buf->chunksize );
|
assert( timeline.fpp == peakbuf.buf->chunksize );
|
||||||
|
|
||||||
int start = timeline.x_to_ts( X ) / peakbuf.buf->chunksize;
|
start = (start - peakbuf.offset) / peakbuf.buf->chunksize;
|
||||||
int i = start - (peakbuf.offset / peakbuf.buf->chunksize);
|
end = (end - peakbuf.offset) / peakbuf.buf->chunksize;
|
||||||
|
|
||||||
assert( peakbuf.len > i );
|
if ( end > peakbuf.len )
|
||||||
|
end = peakbuf.len;
|
||||||
|
|
||||||
p = peakbuf.buf->data[ i ];
|
// assert( peakbuf.len > start );
|
||||||
|
|
||||||
|
downsample( peakbuf.buf->data, start, end, &p.max, &p.min );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int start = timeline.x_to_ts( X ) / _peaks->chunksize;
|
start /= _peaks->chunksize;
|
||||||
int end = timeline.x_to_ts( X + 1 ) / _peaks->chunksize;
|
end /= _peaks->chunksize;
|
||||||
|
|
||||||
downsample( start, end, &p.max, &p.min );
|
downsample( _peaks->data, start, end, &p.max, &p.min );
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* virtual array. Index is a Pixel value, and it returns the
|
||||||
|
* (resampled) peaks for that pixel based on the current timeline
|
||||||
|
* zoom. */
|
||||||
|
Peak &
|
||||||
|
Peaks::operator[] ( int X ) const
|
||||||
|
{
|
||||||
|
return peak( timeline.x_to_ts( X ), timeline.x_to_ts( X + 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
const char *
|
const char *
|
||||||
peakname ( const char *filename )
|
peakname ( const char *filename )
|
||||||
|
@ -271,3 +282,24 @@ Peaks::make_peaks ( int chunksize )
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** return normalization factor for range of samples from /start/ to
|
||||||
|
/end/ (uses known peak data if possible */
|
||||||
|
|
||||||
|
float
|
||||||
|
Peaks::normalization_factor( nframes_t start, nframes_t end ) const
|
||||||
|
{
|
||||||
|
float s;
|
||||||
|
|
||||||
|
fill_buffer( start, end );
|
||||||
|
|
||||||
|
Peak p = peak( start, end );
|
||||||
|
|
||||||
|
s = fabs( 1.0f / p.max );
|
||||||
|
|
||||||
|
if ( s * p.min < -1.0 )
|
||||||
|
s = 1 / fabs( p.max );
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
5
Peaks.H
5
Peaks.H
|
@ -65,6 +65,8 @@ class Peaks
|
||||||
void read_peaks ( int s, int e, int npeaks, int chunksize ) const;
|
void read_peaks ( int s, int e, int npeaks, int chunksize ) const;
|
||||||
int clip_read_peaks ( Peak *peaks, int npeaks, int chunksize ) const;
|
int clip_read_peaks ( Peak *peaks, int npeaks, int chunksize ) const;
|
||||||
|
|
||||||
|
Peak & peak ( nframes_t start, nframes_t end ) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Peaks ( Clip *c )
|
Peaks ( Clip *c )
|
||||||
|
@ -78,9 +80,10 @@ public:
|
||||||
|
|
||||||
void fill_buffer ( int s, int e ) const;
|
void fill_buffer ( int s, int e ) const;
|
||||||
|
|
||||||
void downsample ( int s, int e, float *mhi, float *mlo ) const;
|
void downsample ( Peak *peaks, int s, int e, float *mhi, float *mlo ) const;
|
||||||
void read ( int X, float *hi, float *lo ) const;
|
void read ( int X, float *hi, float *lo ) const;
|
||||||
bool open ( void );
|
bool open ( void );
|
||||||
|
float normalization_factor( nframes_t start, nframes_t end ) const;
|
||||||
|
|
||||||
bool current ( void ) const;
|
bool current ( void ) const;
|
||||||
bool make_peaks ( int chunksize );
|
bool make_peaks ( int chunksize );
|
||||||
|
|
6
Region.C
6
Region.C
|
@ -250,8 +250,10 @@ Region::draw ( void )
|
||||||
|
|
||||||
|
|
||||||
/* fl_color( FL_RED ); */
|
/* fl_color( FL_RED ); */
|
||||||
/* fl_line( x() - timeline.ts_to_x( _start ), y(), x() - timeline.ts_to_x( _start ), y() + h() ); */
|
/* int sx = x() - timeline.ts_to_x( _start ); */
|
||||||
/* fl_line( x() + w() - _end, y(), x() + w() - _end, y() + h() ); */
|
/* fl_line( sx, y(), sx, y() + h() ); */
|
||||||
|
/* int ex = x() + timeline.ts_to_x( _end - _start ); */
|
||||||
|
/* fl_line( ex, y(), ex, y() + h() ); */
|
||||||
|
|
||||||
draw_label();
|
draw_label();
|
||||||
|
|
||||||
|
|
26
Track.H
26
Track.H
|
@ -92,16 +92,30 @@ public:
|
||||||
return 1;
|
return 1;
|
||||||
case FL_PASTE:
|
case FL_PASTE:
|
||||||
{
|
{
|
||||||
const char *file, *text = Fl::event_text();
|
const char *text = Fl::event_text();
|
||||||
|
|
||||||
if ( ! strncmp( text, "file://", 7 ) )
|
char *file;
|
||||||
file = text + 7;
|
|
||||||
else
|
if ( ! sscanf( text, "file://%a[^\r\n]\n", &file ) )
|
||||||
// error?
|
{
|
||||||
file = text;
|
printf( "invalid drop \"%s\"\n", text );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
printf( "pasted file \"%s\"\n", file );
|
printf( "pasted file \"%s\"\n", file );
|
||||||
|
|
||||||
|
Clip *c = Clip::from_file( file );
|
||||||
|
|
||||||
|
// free( file );
|
||||||
|
|
||||||
|
if ( ! c )
|
||||||
|
{
|
||||||
|
free( file );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->add( new Region( c ) );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
12
Waveform.C
12
Waveform.C
|
@ -141,20 +141,12 @@ Waveform::draw ( int X, int Y, int W, int H )
|
||||||
fl_pop_clip();
|
fl_pop_clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Waveform::normalize ( void )
|
Waveform::normalize ( void )
|
||||||
{
|
{
|
||||||
float mhi, mlo;
|
printf( "normalize: start=%lu end=%lu\n", _start, _end );
|
||||||
|
|
||||||
_clip->peaks()->downsample( _start, _end, &mhi, &mlo );
|
_scale = _clip->peaks()->normalization_factor( _start, _end );
|
||||||
|
|
||||||
_scale = 1.0f / (float)mhi;
|
|
||||||
|
|
||||||
if ( _scale * mlo < -1.0 )
|
|
||||||
_scale = 1 / fabs( mlo );
|
|
||||||
|
|
||||||
_scale = fabs( _scale );
|
|
||||||
|
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,9 @@ public:
|
||||||
|
|
||||||
void start ( nframes_t s ) { _start = s; }
|
void start ( nframes_t s ) { _start = s; }
|
||||||
void end ( nframes_t e ) { _end = e; }
|
void end ( nframes_t e ) { _end = e; }
|
||||||
|
void scale ( float s ) { _scale = s; }
|
||||||
|
float scale ( void ) { return _scale; }
|
||||||
|
|
||||||
// void peaks ( float *p ) { _peaks = p; }
|
// void peaks ( float *p ) { _peaks = p; }
|
||||||
void normalize ( void );
|
void normalize ( void );
|
||||||
|
|
||||||
|
|
4
main.C
4
main.C
|
@ -103,7 +103,7 @@ main ( int argc, char **argv )
|
||||||
|
|
||||||
// Region *wave = new Region( 0, 0, 5000, 100, "foo" );
|
// Region *wave = new Region( 0, 0, 5000, 100, "foo" );
|
||||||
|
|
||||||
Region *wave = new Region( new Clip( "streambass8.wav" ) );
|
Region *wave = new Region( Clip::from_file( "streambass8.wav" ) );
|
||||||
|
|
||||||
// wave->resize( 0, 0, 500, 100 );
|
// wave->resize( 0, 0, 500, 100 );
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ main ( int argc, char **argv )
|
||||||
timeline.scroll->end();
|
timeline.scroll->end();
|
||||||
|
|
||||||
Fl_Slider *zoom_slider = new Fl_Slider( 0, 0, 800, 24 );
|
Fl_Slider *zoom_slider = new Fl_Slider( 0, 0, 800, 24 );
|
||||||
zoom_slider->type( 1 );
|
zoom_slider->type( FL_HOR_SLIDER );
|
||||||
zoom_slider->callback( cb_zoom, 0 );
|
zoom_slider->callback( cb_zoom, 0 );
|
||||||
zoom_slider->range( 2, 4096 );
|
zoom_slider->range( 2, 4096 );
|
||||||
zoom_slider->step( 1 );
|
zoom_slider->step( 1 );
|
||||||
|
|
Loading…
Reference in New Issue