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