| @@ -25,30 +25,94 @@ | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| Clip::Clip ( const char *filename ) : _peaks( this ) | |||
| Clip::Clip ( void ) : _peaks( this ) | |||
| { | |||
| _filename = filename; | |||
| _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; | |||
| } | |||
| c = new Clip; | |||
| _length = si.frames; | |||
| 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 ) | |||
| @@ -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; } | |||
| @@ -35,6 +35,8 @@ | |||
| #include "assert.h" | |||
| #include <math.h> | |||
| 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; | |||
| if ( end > peakbuf.len ) | |||
| end = peakbuf.len; | |||
| assert( peakbuf.len > i ); | |||
| // assert( peakbuf.len > start ); | |||
| p = peakbuf.buf->data[ i ]; | |||
| 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; | |||
| } | |||
| @@ -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 ); | |||
| @@ -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(); | |||
| @@ -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: | |||
| @@ -141,20 +141,12 @@ Waveform::draw ( int X, int Y, int W, int H ) | |||
| fl_pop_clip(); | |||
| } | |||
| void | |||
| Waveform::normalize ( void ) | |||
| { | |||
| float mhi, mlo; | |||
| _clip->peaks()->downsample( _start, _end, &mhi, &mlo ); | |||
| _scale = 1.0f / (float)mhi; | |||
| if ( _scale * mlo < -1.0 ) | |||
| _scale = 1 / fabs( mlo ); | |||
| printf( "normalize: start=%lu end=%lu\n", _start, _end ); | |||
| _scale = fabs( _scale ); | |||
| _scale = _clip->peaks()->normalization_factor( _start, _end ); | |||
| redraw(); | |||
| } | |||
| @@ -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 ); | |||
| @@ -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 ); | |||