From e968d8190f3503cbb49f332f8ecf0781061798b8 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Tue, 12 Jun 2012 20:53:21 -0700 Subject: [PATCH] Timeline: Fix waveform drawing during record and cumulative error in looped region drawing. Also, avoid redrawing already displayed parts of waveforms while recording. --- timeline/src/Audio_Region.C | 124 +++++++++++-------------- timeline/src/Engine/Audio_File.C | 16 +--- timeline/src/Engine/Audio_File_Dummy.H | 4 +- timeline/src/Engine/Audio_File_SF.C | 4 +- timeline/src/Engine/Audio_Region.C | 13 +-- timeline/src/Engine/Peaks.C | 40 +++----- timeline/src/Engine/Record_DS.C | 6 ++ timeline/src/Engine/Record_DS.H | 1 + timeline/src/Engine/Track.C | 15 ++- timeline/src/Sequence_Widget.H | 9 +- timeline/src/Track.H | 9 ++ timeline/src/Waveform.C | 4 +- 12 files changed, 123 insertions(+), 122 deletions(-) diff --git a/timeline/src/Audio_Region.C b/timeline/src/Audio_Region.C index 3cc36b7..4f15728 100644 --- a/timeline/src/Audio_Region.C +++ b/timeline/src/Audio_Region.C @@ -492,6 +492,7 @@ Audio_Region::draw ( void ) /* no coverage */ return; + if ( start() > timeline->xoffset + timeline->x_to_ts( sequence()->w() ) || start() + length() < timeline->xoffset ) /* not in viewport */ @@ -499,31 +500,31 @@ Audio_Region::draw ( void ) fl_push_clip( X, Y, W, H ); - /* account for waveform outlines... */ - X -= 2; - W += 4; + /* overdraw a little to avoid artifacts when scrolling */ + W += 2; Fl_Color c = selected() ? fl_invert_color( _color ) : _color; - /* start with region length... */ -// int rw = timeline->ts_to_x( min( length(), timeline->x_to_ts( sequence()->w() ) ) ); - int rw = W; - - /* calculate waveform offset due to scrolling */ - nframes_t offset = 0; - if ( start() < timeline->xoffset ) + if ( sequence()->damage() & FL_DAMAGE_USER1 && this == sequence()->track()->capture_region() ) { - offset = timeline->xoffset - start(); + /* just draw the section with the updated peaks... */ -// rw -= timeline->ts_to_x( offset ); + nframes_t absolute_frame = _r->start + sequence()->track()->capture()->last_frame_drawn; + int nx = sequence()->x() + timeline->ts_to_x( absolute_frame - scroll_ts() ); + + W -= nx - X; + X = nx; } -/* DMESSAGE( "rw = %d", rw ); */ + /* calculate waveform offset due to scrolling */ + /* offset is the number of frames into the waveform the value of X translates to */ + nframes_t x_frame = timeline->xoffset + timeline->x_to_ts( X - _sequence->x() ); + nframes_t offset = x_frame - start(); - const int rx = x(); - -/* fl_color( FL_RED ); */ -/* fl_line( rx + rw, y(), rx + rw, y() + h() ); */ + nframes_t fo = 0; + nframes_t ostart = 0, oend = 0; + const int total_peaks_needed = W; + nframes_t total_frames_needed = timeline->x_to_ts( total_peaks_needed ); { Fl_Color c = fl_color_average( FL_DARK1, @@ -536,38 +537,27 @@ Audio_Region::draw ( void ) draw_fade( _fade_out, Fade::Out, false, X, W ); } - int xo = 0; + int channels = 0; + int peaks = 0; + Peak *pbuf = NULL; - nframes_t ostart = 0, oend = 0; - - const int total_peaks_needed = rw; - - /* compensate for scrolling */ - if ( X - rx > 0 ) - offset += timeline->x_to_ts( X - rx ); - -// DMESSAGE( "Drawing audio region."); - - int channels; - int peaks; - Peak *pbuf = NULL; - do { - nframes_t start = _r->offset; - int loop_peaks_needed = _loop ? timeline->ts_to_x( _loop ) : timeline->ts_to_x( _clip->length() ); + nframes_t loop_frames_needed = _loop ? _loop : total_frames_needed; + int loop_peaks_needed = timeline->ts_to_x( loop_frames_needed ); + + Fl_Color c = Fl::get_color( _color ); if ( this == ((Audio_Sequence*)sequence())->capture_region() ) { - loop_peaks_needed = timeline->ts_to_x( _range.length ); +// loop_peaks_needed = timeline->ts_to_x( _range.length ); c = FL_BLACK; } + + c = fl_color_add_alpha( c, 220 ); - if ( ! loop_peaks_needed ) - break; - - if ( ! xo ) /* first loop... */ + if ( ! fo ) /* first loop... */ { if ( _loop ) start += offset % _loop; @@ -577,38 +567,38 @@ Audio_Region::draw ( void ) /* DMESSAGE( "offset = %lu", (unsigned long) offset ); */ /* DMESSAGE( "loop peaks needed = %d", loop_peaks_needed ); */ - loop_peaks_needed -= timeline->ts_to_x( offset % timeline->x_to_ts( loop_peaks_needed ) ); - - loop_peaks_needed = min( loop_peaks_needed, total_peaks_needed ); - + if ( _loop ) + { + loop_frames_needed -= offset % loop_frames_needed; + loop_peaks_needed = timeline->ts_to_x( loop_frames_needed ); + } /* DMESSAGE( "loop peaks needed = %d", loop_peaks_needed ); */ assert( loop_peaks_needed >= 0 ); - - } - if ( xo + loop_peaks_needed > total_peaks_needed ) + if ( fo + loop_frames_needed > total_frames_needed ) { - loop_peaks_needed -= ( xo + loop_peaks_needed ) - total_peaks_needed; + loop_frames_needed -= ( fo + loop_frames_needed ) - total_frames_needed; + loop_peaks_needed = timeline->ts_to_x( loop_frames_needed ); } - if ( 0 == loop_peaks_needed ) + if ( !loop_peaks_needed ) break; - const nframes_t end = start + timeline->x_to_ts( loop_peaks_needed ); + const nframes_t end = start + loop_frames_needed; if ( start != ostart || end != oend ) { if ( _clip->peaks()->peakfile_ready() ) { if ( _clip->read_peaks( timeline->fpp(), - start, - end, + start, + end, &peaks, &pbuf, &channels ) ) { Waveform::scale( pbuf, peaks * channels, _scale ); - + ostart = start; oend = end; } @@ -626,14 +616,11 @@ Audio_Region::draw ( void ) { // DMESSAGE( "using cached peaks" ); } - - Fl_Color c = Fl::get_color( _color ); - - c = fl_color_add_alpha( c, 220 ); - + if ( peaks && pbuf ) { int ch = (h() - Fl::box_dh( box() )) / channels; + int xo = timeline->ts_to_x( fo ); for ( int i = 0; i < channels; ++i ) { @@ -645,26 +632,31 @@ Audio_Region::draw ( void ) c ); } } + else + WARNING( "Pbuf == %p, peaks = %lu", pbuf, (unsigned long)peaks ); + if ( sequence()->damage() & FL_DAMAGE_USER1 && this == sequence()->track()->capture_region() ) + sequence()->track()->capture()->last_frame_drawn = start + peaks; + if ( peaks < loop_peaks_needed ) { -// DMESSAGE( "Peak read came up %lu peaks short", (unsigned long) loop_peaks_needed - peaks ); + DMESSAGE( "Peak read came up %lu peaks short", (unsigned long)loop_peaks_needed - peaks ); } - xo += loop_peaks_needed; - + fo += loop_frames_needed; } - while ( _loop && xo < W ); + while ( _loop && fo < total_frames_needed ); + if ( _loop && offset < _loop ) { - const int lx = timeline->ts_to_x( _loop - offset ); + const int lx = get_x( start() + _loop ); if ( lx < X + W ) { fl_color( FL_RED ); fl_line_style( FL_DASH, 0 ); - fl_line( X + lx + 2, y(), X + lx + 2, y() + h() ); + fl_line( lx, y(), lx, y() + h() ); fl_line_style( FL_SOLID, 0 ); } } @@ -689,12 +681,6 @@ Audio_Region::draw ( void ) fl_line_style( FL_SOLID, 0 ); } -/* fl_color( FL_BLACK ); */ -/* fl_line( rx, Y, rx, Y + H ); */ -/* fl_line( rx + rw - 1, Y, rx + rw - 1, Y + H ); */ - - - /* if ( current() ) */ /* { */ /* /\* draw length bubble *\/ */ diff --git a/timeline/src/Engine/Audio_File.C b/timeline/src/Engine/Audio_File.C index ffe2b00..eeea756 100644 --- a/timeline/src/Engine/Audio_File.C +++ b/timeline/src/Engine/Audio_File.C @@ -147,22 +147,14 @@ Audio_File::release ( void ) bool Audio_File::read_peaks( float fpp, nframes_t start, nframes_t end, int *peaks, Peak **pbuf, int *channels ) { -// Peaks pk; - + *peaks = 0; + *channels = 0; + *pbuf = NULL; + if ( dummy() ) - { - *peaks = (end - start) / fpp; - *channels = 0; - *pbuf = NULL; - return false; - } else { - *peaks = 0; - *channels = 0; - *pbuf = NULL; - *peaks = _peaks.fill_buffer( fpp, start, end ); *channels = this->channels(); diff --git a/timeline/src/Engine/Audio_File_Dummy.H b/timeline/src/Engine/Audio_File_Dummy.H index 1168ca8..331e3f9 100644 --- a/timeline/src/Engine/Audio_File_Dummy.H +++ b/timeline/src/Engine/Audio_File_Dummy.H @@ -40,7 +40,7 @@ public: bool open ( void ) { return true; } void close ( void ) { } void seek ( nframes_t ) { } - nframes_t read ( sample_t *, int, nframes_t len ) { return len; } - nframes_t read ( sample_t *, int, nframes_t start, nframes_t end ) { return end - start; } + nframes_t read ( sample_t *, int, nframes_t len ) { return 0; } + nframes_t read ( sample_t *, int, nframes_t start, nframes_t end ) { return 0; } nframes_t write ( sample_t *, nframes_t nframes ) { return nframes; } }; diff --git a/timeline/src/Engine/Audio_File_SF.C b/timeline/src/Engine/Audio_File_SF.C index d7f363a..70f9edd 100644 --- a/timeline/src/Engine/Audio_File_SF.C +++ b/timeline/src/Engine/Audio_File_SF.C @@ -242,13 +242,13 @@ Audio_File_SF::write ( sample_t *buf, nframes_t nframes ) { _peaks.write( buf, nframes ); -// lock(); + lock(); nframes_t l = sf_writef_float( _in, buf, nframes ); _length += l; -// unlock(); + unlock(); return l; } diff --git a/timeline/src/Engine/Audio_Region.C b/timeline/src/Engine/Audio_Region.C index 4219922..aebce15 100644 --- a/timeline/src/Engine/Audio_Region.C +++ b/timeline/src/Engine/Audio_Region.C @@ -83,7 +83,7 @@ Audio_Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channe nframes_t sofs, /* offset into source */ ofs, /* offset into buffer */ - cnt; + cnt; /* number of frames to read */ cnt = nframes; @@ -105,8 +105,8 @@ Audio_Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channe return 0; // const nframes_t start = ofs + r.start + sofs; - const nframes_t start = r.offset + sofs; - const nframes_t len = min( cnt, nframes - ofs ); + const nframes_t start = r.offset + sofs; + const nframes_t len = cnt; if ( len == 0 ) return 0; @@ -139,6 +139,9 @@ Audio_Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channe else cnt = _clip->read( buf + ofs, channel, start, len ); + if ( ! cnt ) + return 0; + /* apply gain */ buffer_apply_gain( buf + ofs, cnt, _scale ); @@ -194,9 +197,7 @@ Audio_Region::write ( nframes_t nframes ) if ( W ) { Fl::lock(); -// sequence()->damage( FL_DAMAGE_ALL, ( x() + w() - W ) - 20, y(), W, h() ); - sequence()->damage( FL_DAMAGE_ALL, x(), y(), w(), h() ); - // Fl::awake(); + sequence()->damage(FL_DAMAGE_USER1, x(), y(), w(), h()); Fl::unlock(); } } diff --git a/timeline/src/Engine/Peaks.C b/timeline/src/Engine/Peaks.C index e5a436c..b5ae7c5 100644 --- a/timeline/src/Engine/Peaks.C +++ b/timeline/src/Engine/Peaks.C @@ -104,7 +104,7 @@ class Peakfile FILE *_fp; nframes_t _chunksize; int _channels; /* number of channels this peakfile represents */ - nframes_t _length; /* length, in frames, of the clip this peakfile represents */ +// nframes_t _length; /* length, in frames, of the clip this peakfile represents */ off_t _offset; // int _blocks; @@ -134,7 +134,7 @@ public: _offset = 0; _chunksize = 0; _channels = 0; - _length = 0; +// _length = 0; } ~Peakfile ( ) @@ -163,7 +163,7 @@ public: if ( feof( _fp ) ) break; - DMESSAGE( "Peakfile: chunksize=%lu, skip=%lu\n", (uint64_t)bh.chunksize, (uint64_t) bh.skip ); + DMESSAGE( "Peakfile: chunksize=%lu, skip=%lu", (uint64_t)bh.chunksize, (uint64_t) bh.skip ); ASSERT( bh.chunksize, "Chucksize of zero. Invalid peak file structure!" ); @@ -184,11 +184,6 @@ public: if ( ! blocks.size() ) FATAL( "Peak file contains no blocks!" ); - if ( chunksize == _chunksize ) - { - return; - /* already on the right block... */ - } // DMESSAGE( "peakfile has %d blocks.", blocks.size() ); @@ -208,7 +203,7 @@ public: break; } -// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize ); +// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize ); // _blocks = blocks.size(); _offset = ftello( _fp ); } @@ -310,9 +305,6 @@ public: /* locate to start position */ - /* if ( s > _clip->length() ) */ - /* return 0; */ - if ( fseeko( _fp, _offset + ( frame_to_peak( s ) * sizeof( Peak ) ), SEEK_SET ) ) { DMESSAGE( "failed to seek... peaks not ready?" ); @@ -359,9 +351,7 @@ public: } if ( feof( _fp) || len < ratio ) - { break; - } } delete[] pbuf; @@ -408,15 +398,14 @@ Peaks::fill_buffer ( float fpp, nframes_t s, nframes_t e ) const bool Peaks::ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const { - /* if ( _pending ) */ - /* return false; */ - - Peakfile _peakfile; - - if ( ! _peakfile.open( _clip->filename(), _clip->channels(), chunksize ) ) + if ( ! _peakfile->open( _clip->filename(), _clip->channels(), chunksize ) ) return false; - return _peakfile.ready( s, npeaks ); + int r = _peakfile->ready( s, npeaks ); + + _peakfile->close(); + + return r; } /** If this returns false, then the peakfile needs to be built */ @@ -694,6 +683,7 @@ Peaks::Streamer::Streamer ( const char *filename, int channels, nframes_t chunks fwrite( &bh, sizeof( bh ), 1, _fp ); fflush( _fp ); + fsync( fileno( _fp ) ); } Peaks::Streamer::~Streamer ( ) @@ -704,7 +694,7 @@ Peaks::Streamer::~Streamer ( ) touch( fileno( _fp ) ); -// fsync( fileno( _fp ) ); + fsync( fileno( _fp ) ); fclose( _fp ); @@ -723,9 +713,6 @@ Peaks::Streamer::write ( const sample_t *buf, nframes_t nframes ) { fwrite( _peak, sizeof( Peak ) * _channels, 1, _fp ); - /* FIXME: shouldn't we just use write() instead? */ -// fflush( _fp ); - memset( _peak, 0, sizeof( Peak ) * _channels ); _index = 0; @@ -751,6 +738,9 @@ Peaks::Streamer::write ( const sample_t *buf, nframes_t nframes ) _index += processed; nframes -= processed; } + + /* FIXME: shouldn't we just use write() instead? */ + fflush( _fp ); } diff --git a/timeline/src/Engine/Record_DS.C b/timeline/src/Engine/Record_DS.C index 5d46ec6..db012d1 100644 --- a/timeline/src/Engine/Record_DS.C +++ b/timeline/src/Engine/Record_DS.C @@ -42,6 +42,12 @@ Record_DS::capture_region ( void ) const return NULL; } +Track::Capture * +Record_DS::capture ( void ) +{ + return _capture; +} + /** write /nframes/ from buf to the capture file of the attached track */ void Record_DS::write_block ( sample_t *buf, nframes_t nframes ) diff --git a/timeline/src/Engine/Record_DS.H b/timeline/src/Engine/Record_DS.H index e46f15d..278ada7 100644 --- a/timeline/src/Engine/Record_DS.H +++ b/timeline/src/Engine/Record_DS.H @@ -63,6 +63,7 @@ public: /* bool seek_pending ( void ); */ /* void seek ( nframes_t frame ); */ const Audio_Region * capture_region ( void ) const; + Track::Capture * capture ( void ); void start ( nframes_t frame ); void stop ( nframes_t frame ); diff --git a/timeline/src/Engine/Track.C b/timeline/src/Engine/Track.C index cf7c3a5..e5acb1d 100644 --- a/timeline/src/Engine/Track.C +++ b/timeline/src/Engine/Track.C @@ -40,6 +40,15 @@ Track::capture_region ( void ) const return NULL; } +Track::Capture * +Track::capture ( void ) +{ + if ( record_ds ) + return record_ds->capture(); + else + return NULL; +} + void Track::update_port_names ( void ) { @@ -261,9 +270,9 @@ Track::record ( Capture *c, nframes_t frame ) FATAL( "Could not create file for new capture!" ); /* open it again for reading in the GUI thread */ - Audio_File *af = Audio_File::from_file( c->audio_file->name() ); + // Audio_File *af = Audio_File::from_file( c->audio_file->name() ); - c->region = new Audio_Region( af, sequence(), frame ); + c->region = new Audio_Region( c->audio_file, sequence(), frame ); c->region->prepare(); } @@ -315,5 +324,5 @@ Track::finalize ( Capture *c, nframes_t frame ) c->region->offset( capture_offset ); - delete c->audio_file; +// delete c->audio_file; } diff --git a/timeline/src/Sequence_Widget.H b/timeline/src/Sequence_Widget.H index c65696e..8da354e 100644 --- a/timeline/src/Sequence_Widget.H +++ b/timeline/src/Sequence_Widget.H @@ -193,9 +193,16 @@ public: virtual int h ( void ) const { return _sequence->h(); } /* used by regions */ + + int get_x( nframes_t frame ) const + { + return frame < timeline->xoffset ? _sequence->x() : min( _sequence->x() + _sequence->w(), _sequence->x() + timeline->ts_to_x( frame - timeline->xoffset ) ); + + } + virtual int x ( void ) const { - return _r->start < timeline->xoffset ? _sequence->x() : min( _sequence->x() + _sequence->w(), _sequence->x() + timeline->ts_to_x( _r->start - timeline->xoffset ) ); + return get_x( _r->start ); } /* use this as x() when you need to draw lines between widgets */ diff --git a/timeline/src/Track.H b/timeline/src/Track.H index ebde432..cb4db1c 100644 --- a/timeline/src/Track.H +++ b/timeline/src/Track.H @@ -70,8 +70,16 @@ public: struct Capture { + nframes_t last_frame_drawn; Audio_File *audio_file; Audio_Region *region; + + Capture ( ) + { + last_frame_drawn = 0; + region = 0; + audio_file = 0; + } }; Fl_Color color ( void ) const { return child(0)->color(); } @@ -213,6 +221,7 @@ public: /* Engine */ const Audio_Region *capture_region ( void ) const; + Capture *capture ( void ); void resize_buffers ( nframes_t nframes ); nframes_t process_input ( nframes_t nframes ); diff --git a/timeline/src/Waveform.C b/timeline/src/Waveform.C index 04c5165..5a683e9 100644 --- a/timeline/src/Waveform.C +++ b/timeline/src/Waveform.C @@ -99,7 +99,7 @@ Waveform::draw ( int X, int Y, int W, int H, fl_color( color ); fl_begin_complex_polygon(); - + j = start; for ( int x = X; x <= X + W; x++, j += skip ) @@ -109,7 +109,7 @@ Waveform::draw ( int X, int Y, int W, int H, for ( int x = X + W; x >= X; x--, j -= skip ) fl_vertex( x, ty - ( halfheight * pbuf[ j ].max ) ); - + fl_end_complex_polygon(); } }