From 3b02169d31d3623ca71200ecf550997a79b5bbcd Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Sun, 17 Feb 2008 21:37:26 -0600 Subject: [PATCH] Make normalization code work at all zoom levels. --- Clip.C | 80 ++++++++++++++++++++++++++++++++++++++++++++++++------ Clip.H | 6 +++- Peaks.C | 62 ++++++++++++++++++++++++++++++++---------- Peaks.H | 5 +++- Region.C | 6 ++-- Track.H | 26 ++++++++++++++---- Waveform.C | 12 ++------ Waveform.H | 3 ++ main.C | 4 +-- 9 files changed, 159 insertions(+), 45 deletions(-) diff --git a/Clip.C b/Clip.C index 491e101..15e150d 100644 --- a/Clip.C +++ b/Clip.C @@ -25,30 +25,94 @@ #include #include -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; SF_INFO si; + Clip *c = NULL; + 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 ) - printf( "error: incompatible format" ); + { + printf( "error: incompatible format\n" ); + goto invalid; + } if ( si.samplerate != timeline.sample_rate ) + { printf( "error: samplerate mismatch!\n" ); + goto invalid; + } - _length = si.frames; + c = new Clip; + + c->_filename = filename; + c->_length = si.frames; sf_close( in ); - _peaks.open(); -} + c->_peaks.open(); + return c; + + +invalid: + + sf_close( in ); + return NULL; +} bool Clip::open ( void ) diff --git a/Clip.H b/Clip.H index 29a91a6..6d043ee 100644 --- a/Clip.H +++ b/Clip.H @@ -38,7 +38,11 @@ class Clip public: - Clip ( const char *filename ); + Clip ( ); + +// Clip ( const char *filename ); + + static Clip *from_file ( const char *filename ); Peaks const * peaks ( void ) { return &_peaks; } const char *name ( void ) { return _filename; } diff --git a/Peaks.C b/Peaks.C index c163970..5a5d1a4 100644 --- a/Peaks.C +++ b/Peaks.C @@ -35,6 +35,8 @@ #include "assert.h" +#include + Peaks::peakbuffer Peaks::peakbuf; @@ -58,7 +60,7 @@ Peaks::fill_buffer ( int s, int e ) const 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; *mlo = 0; @@ -68,8 +70,8 @@ Peaks::downsample ( int s, int e, float *mhi, float *mlo ) const for ( int j = s; j < e; j++ ) { - const float lo = _peaks->data[ j ].min; - const float hi = _peaks->data[ j ].max; + const float lo = peaks[ j ].min; + const float hi = peaks[ j ].max; if ( hi > *mhi ) *mhi = hi; @@ -135,12 +137,9 @@ Peaks::read_peaks ( int s, int e, int npeaks, int chunksize ) const _clip->close(); } - -/* virtual array. Index is a Pixel value, and it returns the - * (resampled) peaks for that pixel based on the current timeline - * zoom. */ +/** Return the peak for the range of samples */ Peak & -Peaks::operator[] ( int X ) const +Peaks::peak ( nframes_t start, nframes_t end ) const { /* Is there a better way to return this? */ static Peak p; @@ -149,24 +148,36 @@ Peaks::operator[] ( int X ) const { assert( timeline.fpp == peakbuf.buf->chunksize ); - int start = timeline.x_to_ts( X ) / peakbuf.buf->chunksize; - int i = start - (peakbuf.offset / peakbuf.buf->chunksize); + start = (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 { - int start = timeline.x_to_ts( X ) / _peaks->chunksize; - int end = timeline.x_to_ts( X + 1 ) / _peaks->chunksize; + start /= _peaks->chunksize; + end /= _peaks->chunksize; - downsample( start, end, &p.max, &p.min ); + downsample( _peaks->data, start, end, &p.max, &p.min ); } 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 const char * peakname ( const char *filename ) @@ -271,3 +282,24 @@ Peaks::make_peaks ( int chunksize ) 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; +} diff --git a/Peaks.H b/Peaks.H index 42e030d..2c0dd7f 100644 --- a/Peaks.H +++ b/Peaks.H @@ -65,6 +65,8 @@ class Peaks void read_peaks ( int s, int e, 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: Peaks ( Clip *c ) @@ -78,9 +80,10 @@ public: 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; bool open ( void ); + float normalization_factor( nframes_t start, nframes_t end ) const; bool current ( void ) const; bool make_peaks ( int chunksize ); diff --git a/Region.C b/Region.C index c4fd2c3..006dae7 100644 --- a/Region.C +++ b/Region.C @@ -250,8 +250,10 @@ Region::draw ( void ) /* fl_color( FL_RED ); */ -/* fl_line( x() - timeline.ts_to_x( _start ), y(), x() - timeline.ts_to_x( _start ), y() + h() ); */ -/* fl_line( x() + w() - _end, y(), x() + w() - _end, y() + h() ); */ +/* int sx = x() - timeline.ts_to_x( _start ); */ +/* 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(); diff --git a/Track.H b/Track.H index f7e5ca4..4c01131 100644 --- a/Track.H +++ b/Track.H @@ -92,16 +92,30 @@ public: return 1; case FL_PASTE: { - const char *file, *text = Fl::event_text(); + const char *text = Fl::event_text(); - if ( ! strncmp( text, "file://", 7 ) ) - file = text + 7; - else - // error? - file = text; + char *file; + + if ( ! sscanf( text, "file://%a[^\r\n]\n", &file ) ) + { + printf( "invalid drop \"%s\"\n", text ); + return 0; + } 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; } default: diff --git a/Waveform.C b/Waveform.C index 8e49084..c8a152d 100644 --- a/Waveform.C +++ b/Waveform.C @@ -141,20 +141,12 @@ Waveform::draw ( int X, int Y, int W, int H ) fl_pop_clip(); } - void Waveform::normalize ( void ) { - float mhi, mlo; + printf( "normalize: start=%lu end=%lu\n", _start, _end ); - _clip->peaks()->downsample( _start, _end, &mhi, &mlo ); - - _scale = 1.0f / (float)mhi; - - if ( _scale * mlo < -1.0 ) - _scale = 1 / fabs( mlo ); - - _scale = fabs( _scale ); + _scale = _clip->peaks()->normalization_factor( _start, _end ); redraw(); } diff --git a/Waveform.H b/Waveform.H index e1107fd..563c879 100644 --- a/Waveform.H +++ b/Waveform.H @@ -69,6 +69,9 @@ public: void start ( nframes_t s ) { _start = s; } 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 normalize ( void ); diff --git a/main.C b/main.C index 1a0a157..3a34653 100644 --- a/main.C +++ b/main.C @@ -103,7 +103,7 @@ main ( int argc, char **argv ) // 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 ); @@ -141,7 +141,7 @@ main ( int argc, char **argv ) timeline.scroll->end(); 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->range( 2, 4096 ); zoom_slider->step( 1 );