Make normalization code work at all zoom levels.
This commit is contained in:
parent
a032a95fad
commit
3b02169d31
80
Clip.C
80
Clip.C
|
@ -25,30 +25,94 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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 )
|
||||
|
|
6
Clip.H
6
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; }
|
||||
|
|
62
Peaks.C
62
Peaks.C
|
@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
5
Peaks.H
5
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 );
|
||||
|
|
6
Region.C
6
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();
|
||||
|
||||
|
|
26
Track.H
26
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:
|
||||
|
|
12
Waveform.C
12
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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
4
main.C
4
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 );
|
||||
|
|
Loading…
Reference in New Issue