diff --git a/include/knob.h b/include/knob.h index aa2767cb4..a224d097d 100644 --- a/include/knob.h +++ b/include/knob.h @@ -112,6 +112,8 @@ protected: virtual void paintEvent( QPaintEvent * _me ); virtual void wheelEvent( QWheelEvent * _me ); + virtual float getValue( const QPoint & _p ); + private slots: virtual void enterValue(); void displayHelp(); @@ -128,7 +130,6 @@ private: void drawKnob( QPainter * _p ); void setPosition( const QPoint & _p ); - float getValue( const QPoint & _p ); bool updateAngle(); inline float pageSize() const diff --git a/include/sample_buffer.h b/include/sample_buffer.h index 43c36a6bc..7993a23c7 100644 --- a/include/sample_buffer.h +++ b/include/sample_buffer.h @@ -77,10 +77,10 @@ public: const float _freq, const bool _looped = false ); - void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip ); - inline void visualize( QPainter & _p, const QRect & _dr ) + void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ); + inline void visualize( QPainter & _p, const QRect & _dr, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ) { - visualize( _p, _dr, _dr ); + visualize( _p, _dr, _dr, _from_frame, _to_frame ); } inline const QString & audioFile() const @@ -132,6 +132,16 @@ public: return m_frequency; } + sample_rate_t sampleRate() const + { + return m_sampleRate; + } + + int sampleLength() const + { + return double( m_endFrame - m_startFrame ) / m_sampleRate * 1000; + } + inline void setFrequency( float _freq ) { m_varLock.lock(); diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 8e2041e97..c97aa2c7a 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -115,6 +115,11 @@ void audioFileProcessor::playNote( notePlayHandle * _n, applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, frames,_n ); + emit isPlaying( _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency() ); + } + else + { + emit isPlaying( 0 ); } } @@ -260,22 +265,6 @@ void audioFileProcessor::loopPointChanged( void ) - - - -class audioFileKnob : public knob -{ -public: - audioFileKnob( QWidget * _parent ) : - knob( knobStyled, _parent ) - { - setFixedSize( 37, 47 ); - } -}; - - - - QPixmap * AudioFileProcessorView::s_artwork = NULL; @@ -347,7 +336,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "Otherwise it will be amplified up or down (your " "actual sample-file isn't touched!)" ) ); - m_startKnob = new audioFileKnob( this ); + m_startKnob = new AudioFileProcessorWaveView::knob( this ); m_startKnob->move( 68, 108 ); m_startKnob->setHintText( tr( "Startpoint:" )+" ", "" ); m_startKnob->setWhatsThis( @@ -357,7 +346,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "which AudioFileProcessor returns if a note is longer " "than the sample between the start and end-points." ) ); - m_endKnob = new audioFileKnob( this ); + m_endKnob = new AudioFileProcessorWaveView::knob( this ); m_endKnob->move( 119, 108 ); m_endKnob->setHintText( tr( "Endpoint:" )+" ", "" ); m_endKnob->setWhatsThis( @@ -367,6 +356,18 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "AudioFileProcessor returns if a note is longer than " "the sample between the start and end-points." ) ); + m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel()->m_sampleBuffer ); + m_waveView->move( 2, 172 ); + m_waveView->setKnobs( + dynamic_cast( m_startKnob ), + dynamic_cast( m_endKnob ) + ); + + connect( castModel(), SIGNAL( isPlaying( f_cnt_t ) ), + m_waveView, SLOT( isPlaying( f_cnt_t ) ) ); + + qRegisterMetaType( "f_cnt_t" ); + setAcceptDrops( TRUE ); } @@ -464,26 +465,6 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * ) p.setPen( QColor( 255, 255, 255 ) ); p.drawText( 8, 99, file_name ); - - p.drawPixmap( 2, 172, m_graph ); - - - p.setPen( QColor( 0xFF, 0xAA, 0x00 ) ); - const QRect graph_rect( 4, 174, 241, 70 ); - const f_cnt_t frames = qMax( a->m_sampleBuffer.frames(), - static_cast( 1 ) ); - const int start_frame_x = a->m_sampleBuffer.startFrame() * - graph_rect.width() / frames; - const int end_frame_x = a->m_sampleBuffer.endFrame() * - graph_rect.width() / frames; - - p.drawLine( start_frame_x + graph_rect.x(), graph_rect.y(), - start_frame_x + graph_rect.x(), - graph_rect.height() + graph_rect.y() ); - p.drawLine( end_frame_x + graph_rect.x(), graph_rect.y(), - end_frame_x + graph_rect.x(), - graph_rect.height() + graph_rect.y() ); - } @@ -491,13 +472,7 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * ) void AudioFileProcessorView::sampleUpdated( void ) { - m_graph = QPixmap( 245, 75 ); - m_graph.fill( Qt::transparent ); - QPainter p( &m_graph ); - p.setPen( QColor( 64, 255, 160 ) ); - castModel()->m_sampleBuffer. - visualize( p, QRect( 2, 2, m_graph.width() - 4, - m_graph.height() - 4 ) ); + m_waveView->update(); update(); } @@ -535,6 +510,498 @@ void AudioFileProcessorView::modelChanged( void ) +AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf ) : + QWidget( _parent ), + m_sampleBuffer( _buf ), + m_graph( QPixmap( _w - 2 * s_padding, _h - 2 * s_padding ) ), + m_from( 0 ), + m_to( m_sampleBuffer.frames() ), + m_last_from( 0 ), + m_last_to( 0 ), + m_startKnob( 0 ), + m_endKnob( 0 ), + m_isDragging( false ), + m_reversed( false ), + m_framesPlayed( 0 ) +{ + setFixedSize( _w, _h ); + setMouseTracking( true ); + + if( m_sampleBuffer.frames() > 1 ) + { + const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1; + m_from = qMax( 0, m_sampleBuffer.startFrame() - marging ); + m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() ); + } + + update(); +} + + + + +void AudioFileProcessorWaveView::isPlaying( f_cnt_t _frames_played ) +{ + m_framesPlayed = _frames_played % ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ); + update(); +} + + + + +void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) +{ + QApplication::setOverrideCursor( Qt::OpenHandCursor ); +} + + + + +void AudioFileProcessorWaveView::leaveEvent( QEvent * _e ) +{ + while( QApplication::overrideCursor() ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) +{ + m_isDragging = true; + m_draggingLastPoint = _me->pos(); + + if( isCloseTo( _me->x(), m_startFrameX ) ) + { + m_draggingType = sample_start; + } + else if( isCloseTo( _me->x(), m_endFrameX ) ) + { + m_draggingType = sample_end; + } + else + { + m_draggingType = wave; + QApplication::setOverrideCursor( Qt::ClosedHandCursor ); + } +} + + + + +void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me ) +{ + m_isDragging = false; + if( m_draggingType == wave ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) +{ + if( ! m_isDragging ) + { + const bool is_size_cursor = + QApplication::overrideCursor()->shape() == Qt::SizeHorCursor; + + if( isCloseTo( _me->x(), m_startFrameX ) || + isCloseTo( _me->x(), m_endFrameX ) ) + { + if( ! is_size_cursor ) + { + QApplication::setOverrideCursor( Qt::SizeHorCursor ); + } + } + else if( is_size_cursor ) + { + QApplication::restoreOverrideCursor(); + } + return; + } + + const int step = _me->x() - m_draggingLastPoint.x(); + switch( m_draggingType ) + { + case sample_start: + slideSamplePointByPx( start, step ); + break; + case sample_end: + slideSamplePointByPx( end, step ); + break; + default: + if( qAbs( _me->y() - m_draggingLastPoint.y() ) + < 2 * qAbs( _me->x() - m_draggingLastPoint.x() ) ) + { + slide( step ); + } + else + { + zoom( _me->y() < m_draggingLastPoint.y() ); + } + } + + m_draggingLastPoint = _me->pos(); + update(); +} + + + + +void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we ) +{ + zoom( _we->delta() > 0 ); + update(); +} + + + + +void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe ) +{ + QPainter p( this ); + + p.drawPixmap( s_padding, s_padding, m_graph ); + + p.setPen( QColor( 0xFF, 0xFF, 0x00 ) ); + const QRect graph_rect( s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding ); + const f_cnt_t frames = m_to - m_from; + m_startFrameX = graph_rect.x() + ( m_sampleBuffer.startFrame() - m_from ) * + double( graph_rect.width() ) / frames; + m_endFrameX = graph_rect.x() + ( m_sampleBuffer.endFrame() - m_from ) * + double( graph_rect.width() ) / frames; + + p.drawLine( m_startFrameX, graph_rect.y(), + m_startFrameX, + graph_rect.height() + graph_rect.y() ); + p.drawLine( m_endFrameX, graph_rect.y(), + m_endFrameX, + graph_rect.height() + graph_rect.y() ); + + if( m_endFrameX - m_startFrameX > 2 ) + { + p.fillRect( + m_startFrameX + 1, + graph_rect.y(), + m_endFrameX - m_startFrameX - 1, + graph_rect.height() + graph_rect.y(), + QColor( 255, 255, 0, 70 ) + ); + + if( m_framesPlayed ) + { + const int played_width_px = m_framesPlayed + / double( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) + * ( m_endFrameX - m_startFrameX ); + QLinearGradient g( m_startFrameX + 1, 0, m_startFrameX + 1 + played_width_px, 0 ); + const QColor c( 0, 120, 255, 180 ); + g.setColorAt( 0, Qt::transparent ); + g.setColorAt( 0.8, c ); + g.setColorAt( 1, c ); + p.fillRect( + m_startFrameX + 1, + graph_rect.y(), + played_width_px, + graph_rect.height() + graph_rect.y(), + g + ); + p.setPen( QColor( 0, 255, 255 ) ); + p.drawLine( + m_startFrameX + 1 + played_width_px, + graph_rect.y(), + m_startFrameX + 1 + played_width_px, + graph_rect.height() + graph_rect.y() + ); + m_framesPlayed = 0; + } + } + + QLinearGradient g( 0, 0, width() * 0.7, 0 ); + const QColor c( 0, 0, 150, 180 ); + g.setColorAt( 0, c ); + g.setColorAt( 0.4, c ); + g.setColorAt( 1, Qt::transparent ); + p.fillRect( s_padding, s_padding, m_graph.width(), 14, g ); + + p.setPen( QColor( 255, 255, 20 ) ); + p.setFont( pointSize<8>( font() ) ); + + QString length_text; + const int length = m_sampleBuffer.sampleLength(); + + if( length > 20000 ) + { + length_text = QString::number( length / 1000 ) + "s"; + } + else if( length > 2000 ) + { + length_text = QString::number( ( length / 100 ) / 10.0 ) + "s"; + } + else + { + length_text = QString::number( length ) + "ms"; + } + + p.drawText( + s_padding + 2, + s_padding + 10, + tr( "Sample length:" ) + " " + length_text + ); +} + + + + +void AudioFileProcessorWaveView::updateGraph() +{ + if( m_to == 1 ) + { + m_to = m_sampleBuffer.frames() * 0.7; + slideSamplePointToFrames( end, m_to * 0.7 ); + } + + if( m_from > m_sampleBuffer.startFrame() ) + { + m_from = m_sampleBuffer.startFrame(); + } + + if( m_to < m_sampleBuffer.endFrame() ) + { + m_to = m_sampleBuffer.endFrame(); + } + + if( m_sampleBuffer.reversed() != m_reversed ) + { + reverse(); + } + else if( m_last_from == m_from && m_last_to == m_to ) + { + return; + } + + m_last_from = m_from; + m_last_to = m_to; + + m_graph.fill( Qt::transparent ); + QPainter p( &m_graph ); + p.setPen( QColor( 64, 255, 160 ) ); + m_sampleBuffer.visualize( + p, + QRect( 0, 0, m_graph.width(), m_graph.height() ), + m_from, m_to + ); +} + + + + +void AudioFileProcessorWaveView::zoom( const bool _out ) +{ + const f_cnt_t start = m_sampleBuffer.startFrame(); + const f_cnt_t end = m_sampleBuffer.endFrame(); + const f_cnt_t frames = m_sampleBuffer.frames(); + const f_cnt_t d_from = start - m_from; + const f_cnt_t d_to = m_to - end; + + const f_cnt_t step = qMax( 1, qMax( d_from, d_to ) / 10 ); + const f_cnt_t step_from = ( _out ? - step : step ); + const f_cnt_t step_to = ( _out ? step : - step ); + + const double comp_ratio = double( qMin( d_from, d_to ) ) + / qMax( 1, qMax( d_from, d_to ) ); + + f_cnt_t new_from; + f_cnt_t new_to; + + if( ( _out && d_from < d_to ) || ( ! _out && d_to < d_from ) ) + { + new_from = qBound( 0, m_from + step_from, start ); + new_to = qBound( + end, + m_to + f_cnt_t( step_to * ( new_from == m_from ? 1 : comp_ratio ) ), + frames + ); + } + else + { + new_to = qBound( end, m_to + step_to, frames ); + new_from = qBound( + 0, + m_from + f_cnt_t( step_from * ( new_to == m_to ? 1 : comp_ratio ) ), + start + ); + } + + if( double( new_to - new_from ) / m_sampleBuffer.sampleRate() > 0.05 ) + { + m_from = new_from; + m_to = new_to; + } +} + + + + +void AudioFileProcessorWaveView::slide( int _px ) +{ + const double fact = qAbs( double( _px ) / width() ); + f_cnt_t step = ( m_to - m_from ) * fact; + if( _px > 0 ) + { + step = -step; + } + + f_cnt_t step_from = qBound( 0, m_from + step, m_sampleBuffer.frames() ) - m_from; + f_cnt_t step_to = qBound( m_from + 1, m_to + step, m_sampleBuffer.frames() ) - m_to; + + step = qAbs( step_from ) < qAbs( step_to ) ? step_from : step_to; + + m_from += step; + m_to += step; + slideSampleByFrames( step ); +} + + + + +void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end ) +{ + m_startKnob = _start; + m_endKnob = _end; + + m_startKnob->setWaveView( this ); + m_startKnob->setRelatedKnob( m_endKnob ); + + m_endKnob->setWaveView( this ); + m_endKnob->setRelatedKnob( m_startKnob ); +} + + + + +void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px ) +{ + slideSamplePointByFrames( + _point, + f_cnt_t( ( double( _px ) / width() ) * ( m_to - m_from ) ) + ); +} + + + + +void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to ) +{ + knob * knob = _point == start ? m_startKnob : m_endKnob; + if( ! knob ) + { + return; + } + + const double v = double( _frames ) / m_sampleBuffer.frames(); + if( _slide_to ) + { + knob->slideTo( v ); + } + else + { + knob->slideBy( v ); + } +} + + + + +void AudioFileProcessorWaveView::slideSampleByFrames( f_cnt_t _frames ) +{ + const double v = double( _frames ) / m_sampleBuffer.frames(); + m_startKnob->slideBy( v, false ); + m_endKnob->slideBy( v, false ); +} + + + + +void AudioFileProcessorWaveView::reverse() +{ + slideSampleByFrames( + m_sampleBuffer.frames() + - m_sampleBuffer.endFrame() + - m_sampleBuffer.startFrame() + ); + + const f_cnt_t from = m_from; + m_from = m_sampleBuffer.frames() - m_to; + m_to = m_sampleBuffer.frames() - from; + + m_reversed = ! m_reversed; +} + + + + +void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound ) +{ + if( _check_bound && ! checkBound( _v ) ) + { + return; + } + model()->setValue( _v ); + emit sliderMoved( model()->value() ); +} + + + + +float AudioFileProcessorWaveView::knob::getValue( const QPoint & _p ) +{ + const double dec_fact = ! m_waveView ? 1 : + double( m_waveView->m_to - m_waveView->m_from ) + / m_waveView->m_sampleBuffer.frames(); + const float inc = ::knob::getValue( _p ) * dec_fact; + const float next = model()->value() - inc; + + if( ! checkBound( next ) ) + { + return 0; + } + return inc; +} + + + + +bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const +{ + if( ! m_relatedKnob || ! m_waveView ) + { + return true; + } + + if( ( m_relatedKnob->model()->value() - _v > 0 ) != + ( m_relatedKnob->model()->value() - model()->value() > 0 ) ) + return false; + + const double d1 = qAbs( m_relatedKnob->model()->value() - model()->value() ) + * ( m_waveView->m_sampleBuffer.frames() ) + / m_waveView->m_sampleBuffer.sampleRate(); + + const double d2 = qAbs( m_relatedKnob->model()->value() - _v ) + * ( m_waveView->m_sampleBuffer.frames() ) + / m_waveView->m_sampleBuffer.sampleRate(); + + return d1 < d2 || d2 > 0.02; +} + + + + + extern "C" { diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index ae7f75b39..1617e745b 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -76,6 +76,10 @@ private slots: void loopPointChanged(); +signals: + void isPlaying( f_cnt_t _frames_played ); + + private: typedef sampleBuffer::handleState handleState; @@ -94,6 +98,9 @@ private: +class AudioFileProcessorWaveView; + + class AudioFileProcessorView : public InstrumentView { Q_OBJECT @@ -118,7 +125,7 @@ private: static QPixmap * s_artwork; - QPixmap m_graph; + AudioFileProcessorWaveView * m_waveView; knob * m_ampKnob; knob * m_startKnob; knob * m_endKnob; @@ -130,5 +137,134 @@ private: +class AudioFileProcessorWaveView : public QWidget +{ + Q_OBJECT +protected: + virtual void enterEvent( QEvent * _e ); + virtual void leaveEvent( QEvent * _e ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void wheelEvent( QWheelEvent * _we ); + virtual void paintEvent( QPaintEvent * _pe ); + + +public: + enum knobType + { + start, + end, + } ; + + class knob : public ::knob + { + const AudioFileProcessorWaveView * m_waveView; + const knob * m_relatedKnob; + + + public: + knob( QWidget * _parent ) : + ::knob( knobStyled, _parent ), + m_waveView( 0 ), + m_relatedKnob( 0 ) + { + setFixedSize( 37, 47 ); + } + + void setWaveView( const AudioFileProcessorWaveView * _wv ) + { + m_waveView = _wv; + } + + void setRelatedKnob( const knob * _knob ) + { + m_relatedKnob = _knob; + } + + void slideBy( double _v, bool _check_bound = true ) + { + slideTo( model()->value() + _v, _check_bound ); + } + + void slideTo( double _v, bool _check_bound = true ); + + + protected: + float getValue( const QPoint & _p ); + + + private: + bool checkBound( double _v ) const; + + } ; + + +public slots: + void update() + { + updateGraph(); + QWidget::update(); + } + + void isPlaying( f_cnt_t _frames_played ); + + +private: + static const int s_padding = 2; + + enum draggingType + { + wave, + sample_start, + sample_end, + } ; + + sampleBuffer & m_sampleBuffer; + QPixmap m_graph; + f_cnt_t m_from; + f_cnt_t m_to; + f_cnt_t m_last_from; + f_cnt_t m_last_to; + knob * m_startKnob; + knob * m_endKnob; + f_cnt_t m_startFrameX; + f_cnt_t m_endFrameX; + bool m_isDragging; + QPoint m_draggingLastPoint; + draggingType m_draggingType; + bool m_reversed; + f_cnt_t m_framesPlayed; + + +public: + AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf ); + void setKnobs( knob * _start, knob * _end ); + + +private: + void zoom( const bool _out = false ); + void slide( int _px ); + void slideSamplePointByPx( knobType _point, int _px ); + void slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to = false ); + void slideSampleByFrames( f_cnt_t _frames ); + + void slideSamplePointToFrames( knobType _point, f_cnt_t _frames ) + { + slideSamplePointByFrames( _point, _frames, true ); + } + + void updateGraph(); + void reverse(); + + static bool isCloseTo( int _a, int _b ) + { + return qAbs( _a - _b ) < 3; + } + +} ; + + + #endif diff --git a/src/core/sample_buffer.cpp b/src/core/sample_buffer.cpp index df5c56c48..40b0bc036 100644 --- a/src/core/sample_buffer.cpp +++ b/src/core/sample_buffer.cpp @@ -699,8 +699,10 @@ f_cnt_t sampleBuffer::getLoopedIndex( f_cnt_t _index ) const void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, - const QRect & _clip ) + const QRect & _clip, f_cnt_t _from_frame, f_cnt_t _to_frame ) { + const bool focus_on_range = _to_frame <= m_frames + && 0 <= _from_frame && _from_frame < _to_frame; // _p.setClipRect( _clip ); // _p.setPen( QColor( 0x22, 0xFF, 0x44 ) ); //_p.setPen( QColor( 64, 224, 160 ) ); @@ -709,25 +711,28 @@ void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, const int yb = h / 2 + _dr.y(); const float y_space = h*0.25f; + const int nb_frames = focus_on_range ? _to_frame - _from_frame : m_frames; - if( m_frames < 60000 ) + if( nb_frames < 60000 ) { _p.setRenderHint( QPainter::Antialiasing ); QColor c = _p.pen().color(); _p.setPen( QPen( c, 0.7 ) ); } - const int fpp = tLimit( m_frames / w, 1, 20 ); - QPoint * l = new QPoint[m_frames / fpp + 1]; + const int fpp = tLimit( nb_frames / w, 1, 20 ); + QPoint * l = new QPoint[nb_frames / fpp + 1]; int n = 0; const int xb = _dr.x(); - for( int frame = 0; frame < m_frames; frame += fpp ) + const int first = focus_on_range ? _from_frame : 0; + const int last = focus_on_range ? _to_frame : m_frames; + for( int frame = first; frame < last; frame += fpp ) { - l[n] = QPoint( xb + ( frame * w / m_frames ), + l[n] = QPoint( xb + ( (frame - first) * double( w ) / nb_frames ), (int)( yb - ( ( m_data[frame][0]+m_data[frame][1] ) * y_space ) ) ); ++n; } - _p.drawPolyline( l, m_frames / fpp ); + _p.drawPolyline( l, nb_frames / fpp ); delete[] l; }