@@ -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 ); | ||||