diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 0016f6331..eeda62991 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -287,12 +287,13 @@ private: float m_frequency; sample_rate_t m_sampleRate; - sampleFrame * getSampleFragment( f_cnt_t _start, f_cnt_t _frames, + sampleFrame * getSampleFragment( f_cnt_t _index, f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, - bool * _backwards ) const; - f_cnt_t getLoopedIndex( f_cnt_t _index ) const; - f_cnt_t getPingPongIndex( f_cnt_t _index ) const; + bool * _backwards, f_cnt_t _loopstart, f_cnt_t _loopend, + f_cnt_t _end ) const; + f_cnt_t getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; + f_cnt_t getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; signals: diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 16465e508..d285b4eaa 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -2,7 +2,7 @@ * audio_file_processor.cpp - instrument for using audio-files * * Copyright (c) 2004-2014 Tobias Doerffel - * + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -78,7 +78,8 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : m_reverseModel( false, this, tr( "Reverse sample" ) ), m_loopModel( 0, 0, 2, this, tr( "Loop mode" ) ), m_stutterModel( false, this, tr( "Stutter" ) ), - m_nextPlayStartPoint( 0 ) + m_nextPlayStartPoint( 0 ), + m_nextPlayBackwards( false ) { connect( &m_reverseModel, SIGNAL( dataChanged() ), this, SLOT( reverseModelChanged() ) ); @@ -116,6 +117,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_stutterModel.value() == true && _n->frequency() < 20.0 ) { m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; return; } @@ -126,9 +128,11 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, // Restart playing the note if in stutter mode, not in loop mode, // and we're at the end of the sample. m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; } _n->m_pluginData = new handleState( _n->hasDetuningInfo() ); ((handleState *)_n->m_pluginData)->setFrameIndex( m_nextPlayStartPoint ); + ((handleState *)_n->m_pluginData)->setBackwards( m_nextPlayBackwards ); // debug code /* qDebug( "frames %d", m_sampleBuffer.frames() ); @@ -141,7 +145,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_sampleBuffer.play( _working_buffer, (handleState *)_n->m_pluginData, frames, _n->frequency(), - m_loopModel.value() ? SampleBuffer::LoopPingPong : SampleBuffer::LoopOff ) ) + static_cast( m_loopModel.value() ) ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, @@ -161,6 +165,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_stutterModel.value() == true ) { m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex(); + m_nextPlayBackwards = ((handleState *)_n->m_pluginData)->isBackwards(); } } @@ -214,7 +219,7 @@ void audioFileProcessor::loadSettings( const QDomElement & _this ) m_ampModel.loadSettings( _this, "amp" ); m_startPointModel.loadSettings( _this, "sframe" ); m_endPointModel.loadSettings( _this, "eframe" ); - + // compat code for not having a separate loopback point if( _this.hasAttribute( "lframe" ) ) { @@ -307,6 +312,7 @@ void audioFileProcessor::ampModelChanged( void ) void audioFileProcessor::stutterModelChanged() { m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; } @@ -321,28 +327,38 @@ void audioFileProcessor::loopPointChanged( void ) m_endPointModel.setValue( m_startPointModel.value() ); m_startPointModel.setValue( tmp ); } - + // check if start & end overlap and nudge end up if so if( m_startPointModel.value() == m_endPointModel.value() ) { m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) ); } - - const f_cnt_t f_start = static_cast( m_startPointModel.value() * - ( m_sampleBuffer.frames()-1 ) ); - const f_cnt_t f_end = static_cast( m_endPointModel.value() * - ( m_sampleBuffer.frames()-1 ) ); - m_nextPlayStartPoint = f_start; - // check that loop point is between start-end points + // check that loop point is between start-end points and not overlapping with endpoint + // ...and move start/end points ahead if loop point is moved over them + if( m_loopPointModel.value() >= m_endPointModel.value() ) + { + m_endPointModel.setValue( m_loopPointModel.value() + 0.001f ); + if( m_endPointModel.value() == 1.0f ) + { + m_loopPointModel.setValue( 1.0f - 0.001f ); + } + } if( m_loopPointModel.value() < m_startPointModel.value() ) - m_loopPointModel.setValue( m_startPointModel.value() ); - if( m_loopPointModel.value() > m_endPointModel.value() ) - m_loopPointModel.setValue( m_endPointModel.value() ); + { + m_startPointModel.setValue( m_loopPointModel.value() ); + } + + const f_cnt_t f_start = static_cast( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + const f_cnt_t f_end = static_cast( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + const f_cnt_t f_loop = static_cast( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + + m_nextPlayStartPoint = f_start; + m_nextPlayBackwards = false; m_sampleBuffer.setStartFrame( f_start ); m_sampleBuffer.setEndFrame( f_end ); - m_sampleBuffer.setLoopStartFrame( static_cast( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) ) ); + m_sampleBuffer.setLoopStartFrame( f_loop ); m_sampleBuffer.setLoopEndFrame( f_end ); emit dataChanged(); } @@ -377,7 +393,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, m_openAudioFileButton->setWhatsThis( tr( "Click here, if you want to open another audio-file. " "A dialog will appear where you can select your file. " - "Settings like looping-mode, start and end-points, " + "Settings like looping-mode, start and end-points, " "amplify-value, and so on are not reset. So, it may not " "sound like the original sample.") ); @@ -396,7 +412,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, // loop button group - pixmapbutton * m_loopOffButton = new pixmapButton( this ); + pixmapButton * m_loopOffButton = new pixmapButton( this ); m_loopOffButton->setCheckable( TRUE ); m_loopOffButton->move( 174, 144 ); m_loopOffButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -409,7 +425,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "The sample plays only once from start to end. " ) ); - pixmapbutton * m_loopOnButton = new pixmapButton( this ); + pixmapButton * m_loopOnButton = new pixmapButton( this ); m_loopOnButton->setCheckable( TRUE ); m_loopOnButton->move( 200, 144 ); m_loopOnButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -421,7 +437,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, tr( "This button enables forwards-looping. " "The sample loops between the end point and the loop point." ) ); - pixmapbutton * m_loopPingPongButton = new pixmapButton( this ); + pixmapButton * m_loopPingPongButton = new pixmapButton( this ); m_loopPingPongButton->setCheckable( TRUE ); m_loopPingPongButton->move( 226, 144 ); m_loopPingPongButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -433,12 +449,12 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, tr( "This button enables ping-pong-looping. " "The sample loops backwards and forwards between the end point " "and the loop point." ) ); - + m_loopGroup = new automatableButtonGroup( this ); m_loopGroup->addButton( m_loopOffButton ); m_loopGroup->addButton( m_loopOnButton ); m_loopGroup->addButton( m_loopPingPongButton ); - + m_stutterButton = new pixmapButton( this ); m_stutterButton->setCheckable( true ); m_stutterButton->move( 226, 124 ); @@ -708,15 +724,22 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) { m_isDragging = true; m_draggingLastPoint = _me->pos(); - - const int start_dist = qAbs( m_startFrameX - _me->x() ); - const int end_dist = qAbs( m_endFrameX - _me->x() ); - const int loop_dist = qAbs( m_loopFrameX - _me->x() ); - + + const int x = _me->x(); + + const int start_dist = qAbs( m_startFrameX - x ); + const int end_dist = qAbs( m_endFrameX - x ); + const int loop_dist = qAbs( m_loopFrameX - x ); + draggingType dt = sample_loop; int md = loop_dist; if( start_dist < loop_dist ) { dt = sample_start; md = start_dist; } - if( end_dist < start_dist ) { dt = sample_end; md = end_dist; } - + else if( end_dist < loop_dist ) { dt = sample_end; md = end_dist; } + +/* qDebug( "x %d", x ); + qDebug( "loopframex %d", m_loopFrameX ); + qDebug( "dt %d", dt ); + qDebug( "md %d", md );*/ + if( md < 4 ) { m_draggingType = dt; @@ -838,7 +861,7 @@ void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe ) p.drawLine( m_endFrameX, graph_rect.y(), m_endFrameX, graph_rect.height() + graph_rect.y() ); - + if( m_endFrameX - m_startFrameX > 2 ) { @@ -1042,7 +1065,7 @@ void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end, knob * _l m_endKnob->setWaveView( this ); m_endKnob->setRelatedKnob( m_startKnob ); - + m_loopKnob->setWaveView( this ); } @@ -1166,7 +1189,7 @@ bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const { return true; } - + if( ( m_relatedKnob->model()->value() - _v > 0 ) != ( m_relatedKnob->model()->value() - model()->value() >= 0 ) ) return false; diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index 94472f88e..1f0558ab5 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -96,6 +96,7 @@ private: BoolModel m_stutterModel; f_cnt_t m_nextPlayStartPoint; + bool m_nextPlayBackwards; friend class AudioFileProcessorView; diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 33dc7f1f4..9ef094e0f 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -611,12 +611,17 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, const fpp_t _frames, const float _freq, const LoopMode _loopmode ) -{ +{ QMutexLocker ml( &m_varLock ); + + f_cnt_t startFrame = m_startFrame; + f_cnt_t endFrame = m_endFrame; + f_cnt_t loopStartFrame = m_loopStartFrame; + f_cnt_t loopEndFrame = m_loopEndFrame; engine::mixer()->clearAudioBuffer( _ab, _frames ); - if( m_endFrame == 0 || _frames == 0 ) + if( endFrame == 0 || _frames == 0 ) { return false; } @@ -629,7 +634,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // calculate how many frames we have in requested pitch const f_cnt_t total_frames_for_current_pitch = static_cast( ( - m_endFrame - m_startFrame ) / + endFrame - startFrame ) / freq_factor ); if( total_frames_for_current_pitch == 0 ) @@ -637,45 +642,47 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, return false; } + // this holds the number of the first frame to play f_cnt_t play_frame = _state->m_frameIndex; - if( play_frame < m_startFrame ) + if( play_frame < startFrame ) { - play_frame = m_startFrame; + play_frame = startFrame; } // this holds the number of remaining frames in current loop f_cnt_t frames_for_loop; if( _loopmode == LoopOn ) { - play_frame = getLoopedIndex( play_frame ); - frames_for_loop = static_cast( - ( m_loopEndFrame - play_frame ) / - freq_factor ); + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + frames_for_loop = static_cast( ( loopEndFrame - play_frame ) / freq_factor ); } + else if( _loopmode == LoopPingPong ) { - play_frame = getPingPongIndex( play_frame ); + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); if( is_backwards ) - frames_for_loop = static_cast( ( play_frame - m_loopStartFrame ) / freq_factor ); + frames_for_loop = static_cast( ( play_frame - loopStartFrame ) / freq_factor ); else - frames_for_loop = static_cast( ( m_loopEndFrame - play_frame ) / freq_factor ); + frames_for_loop = static_cast( ( loopEndFrame - play_frame ) / freq_factor ); } + else { - if( play_frame >= m_endFrame ) + if( play_frame >= endFrame ) { return false; } - frames_for_loop = static_cast( - ( m_endFrame - play_frame ) / - freq_factor ); + + frames_for_loop = static_cast( ( endFrame - play_frame ) / freq_factor ); + if( frames_for_loop == 0 ) { return false; } } + sampleFrame * tmp = NULL; @@ -685,10 +692,10 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, SRC_DATA src_data; // Generate output const f_cnt_t margin = 64; - f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) - + margin; - src_data.data_in = getSampleFragment( play_frame, - fragment_size, _loopmode, &tmp, &is_backwards )[0]; + f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + margin; + src_data.data_in = + getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards, + loopStartFrame, loopEndFrame, endFrame )[0]; src_data.data_out = _ab[0]; src_data.input_frames = fragment_size; src_data.output_frames = _frames; @@ -714,7 +721,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, break; case LoopOn: play_frame += src_data.input_frames_used; - play_frame = getLoopedIndex( play_frame ); + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); break; case LoopPingPong: { @@ -722,15 +729,15 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, if( _state->isBackwards() ) { play_frame -= src_data.input_frames_used; - if( play_frame < m_loopStartFrame ) + if( play_frame < loopStartFrame ) { - left -= ( m_loopStartFrame - play_frame ); - play_frame = m_loopStartFrame; + left -= ( loopStartFrame - play_frame ); + play_frame = loopStartFrame; } else left = 0; } play_frame += left; - play_frame = getPingPongIndex( play_frame ); + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); break; } } @@ -742,7 +749,8 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // Generate output memcpy( _ab, - getSampleFragment( play_frame, _frames, _loopmode, &tmp, &is_backwards ), + getSampleFragment( play_frame, _frames, _loopmode, &tmp, &is_backwards, + loopStartFrame, loopEndFrame, endFrame ), _frames * BYTES_PER_FRAME ); // Advance switch( _loopmode ) @@ -752,7 +760,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, break; case LoopOn: play_frame += _frames; - play_frame = getLoopedIndex( play_frame ); + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); break; case LoopPingPong: { @@ -760,15 +768,15 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, if( _state->isBackwards() ) { play_frame -= _frames; - if( play_frame < m_loopStartFrame ) + if( play_frame < loopStartFrame ) { - left -= ( m_loopStartFrame - play_frame ); - play_frame = m_loopStartFrame; + left -= ( loopStartFrame - play_frame ); + play_frame = loopStartFrame; } else left = 0; } play_frame += left; - play_frame = getPingPongIndex( play_frame ); + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); break; } } @@ -786,53 +794,61 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, -sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, - f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, bool * _backwards ) const +sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _index, + f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, bool * _backwards, + f_cnt_t _loopstart, f_cnt_t _loopend, f_cnt_t _end ) const { - if( _loopmode == LoopOn ) + + if( _loopmode == LoopOff ) { - if( _start + _frames <= m_loopEndFrame ) + if( _index + _frames <= _end ) { - return m_data + _start; + return m_data + _index; } } - else if( _loopmode == LoopOff ) + else if( _loopmode == LoopOn ) { - if( _start + _frames <= m_endFrame ) + if( _index + _frames <= _loopend ) { - return m_data + _start; + return m_data + _index; } } -/* else + else { - if( ! *_backwards && pos + _frames < m_loopEndFrame ) - *_index = pos + _frames; - return m_data + pos; - }*/ + if( ! *_backwards && _index + _frames < _loopend ) + return m_data + _index; + } *_tmp = new sampleFrame[_frames]; - if( _loopmode == LoopOn ) + if( _loopmode == LoopOff ) { - f_cnt_t copied = qMin( _frames, m_loopEndFrame - _start ); - memcpy( *_tmp, m_data + _start, copied * BYTES_PER_FRAME ); - f_cnt_t loop_frames = m_loopEndFrame - m_loopStartFrame; + f_cnt_t available = _end - _index; + memcpy( *_tmp, m_data + _index, available * BYTES_PER_FRAME ); + memset( *_tmp + available, 0, ( _frames - available ) * + BYTES_PER_FRAME ); + } + else if( _loopmode == LoopOn ) + { + f_cnt_t copied = qMin( _frames, _loopend - _index ); + memcpy( *_tmp, m_data + _index, copied * BYTES_PER_FRAME ); + f_cnt_t loop_frames = _loopend - _loopstart; while( copied < _frames ) { f_cnt_t todo = qMin( _frames - copied, loop_frames ); - memcpy( *_tmp + copied, m_data + m_loopStartFrame, todo * BYTES_PER_FRAME ); + memcpy( *_tmp + copied, m_data + _loopstart, todo * BYTES_PER_FRAME ); copied += todo; } } - else if( _loopmode == LoopPingPong ) + else { - f_cnt_t pos = _start; + f_cnt_t pos = _index; bool backwards = *_backwards; f_cnt_t copied = 0; if( backwards ) { - copied = qMin( _frames, pos - m_loopStartFrame ); + copied = qMin( _frames, pos - _loopstart ); for( int i=0; i < copied; i++ ) { //memcpy( *_tmp + i, m_data + pos - i, BYTES_PER_FRAME ); @@ -840,26 +856,26 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, (*_tmp)[i][1] = m_data[ pos - i ][1]; } pos -= copied; - if( pos == m_loopStartFrame ) backwards = false; + if( pos == _loopstart ) backwards = false; } else { - copied = qMin( _frames, m_loopEndFrame - pos ); + copied = qMin( _frames, _loopend - pos ); memcpy( *_tmp, m_data + pos, copied * BYTES_PER_FRAME ); pos += copied; - if( pos == m_loopEndFrame ) backwards = true; + if( pos == _loopend ) backwards = true; } while( copied < _frames ) { - if( pos >= m_loopEndFrame ) backwards = true; - if( pos <= m_loopStartFrame ) backwards = false; - pos = qBound( m_loopStartFrame, pos, m_loopEndFrame ); + if( pos >= _loopend ) backwards = true; + if( pos <= _loopstart ) backwards = false; + pos = qBound( _loopstart, pos, _loopend ); /*qDebug( backwards ? "backwards" : "forwards" ); qDebug( "pos %d", pos );*/ if( backwards ) { - f_cnt_t todo = qMin( _frames - copied, pos - m_loopStartFrame ); + f_cnt_t todo = qMin( _frames - copied, pos - _loopstart ); for ( int i=0; i < todo; i++ ) { //memcpy( *_tmp + ( copied + i ), m_data + ( pos - i ), BYTES_PER_FRAME ); @@ -868,26 +884,19 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, } pos -= todo; copied += todo; - if( pos == m_loopStartFrame ) backwards = false; + if( pos == _loopstart ) backwards = false; } else { - f_cnt_t todo = qMin( _frames - copied, m_loopEndFrame - pos ); + f_cnt_t todo = qMin( _frames - copied, _loopend - pos ); memcpy( *_tmp + copied, m_data + pos, todo * BYTES_PER_FRAME ); pos += todo; copied += todo; - if( pos == m_loopEndFrame ) backwards = true; + if( pos == _loopend ) backwards = true; } } *_backwards = backwards; } - else - { - f_cnt_t available = m_endFrame - _start; - memcpy( *_tmp, m_data + _start, available * BYTES_PER_FRAME ); - memset( *_tmp + available, 0, ( _frames - available ) * - BYTES_PER_FRAME ); - } return *_tmp; } @@ -895,31 +904,29 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, -f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index ) const +f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const { - if( _index < m_loopEndFrame ) + if( _index < _endf ) { return _index; } - return m_loopStartFrame + ( _index - m_loopStartFrame ) - % ( m_loopEndFrame - m_loopStartFrame ); + return _startf + ( _index - _startf ) + % ( _endf - _startf ); } -f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index ) const +f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const { - if( _index < m_loopEndFrame ) + if( _index < _endf ) { return _index; } - const f_cnt_t looplen = m_loopEndFrame - m_loopStartFrame; - const f_cnt_t looppos = ( _index - m_loopEndFrame ) % ( looplen*2 ); + const f_cnt_t looplen = _endf - _startf; + const f_cnt_t looppos = ( _index - _endf ) % ( looplen*2 ); - f_cnt_t r; - if( looppos < looplen ) r= m_loopEndFrame - looppos; - else r= m_loopStartFrame + ( looppos - looplen ); - qDebug( "index %d --- pingpongindex %d", _index, r ); - return r; + return ( looppos < looplen ) + ? _endf - looppos + : _startf + ( looppos - looplen ); }