| @@ -25,30 +25,94 @@ | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.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; | 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; | |||||
| } | |||||
| c = new Clip; | |||||
| _length = si.frames; | |||||
| 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 ) | ||||
| @@ -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; } | ||||
| @@ -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 hi = _peaks->data[ j ].max; | |||||
| const float lo = peaks[ j ].min; | |||||
| 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(); | ||||
| } | } | ||||
| /* 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 & | 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; | |||||
| 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 | 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; | 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; | |||||
| } | |||||
| @@ -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 ); | ||||
| @@ -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() ); */ | |||||
| /* 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(); | draw_label(); | ||||
| @@ -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 ) ) | |||||
| 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 ); | 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: | ||||
| @@ -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; | |||||
| _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(); | 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 ); | ||||
| @@ -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 ); | ||||