Finish ping pong loop implementation in AFP

This commit is contained in:
Vesa
2014-04-04 00:24:23 +03:00
parent 7350f983b0
commit 0442be3729
4 changed files with 150 additions and 118 deletions

View File

@@ -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:

View File

@@ -2,7 +2,7 @@
* audio_file_processor.cpp - instrument for using audio-files
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
*
* 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<SampleBuffer::LoopMode>( 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<f_cnt_t>( m_startPointModel.value() *
( m_sampleBuffer.frames()-1 ) );
const f_cnt_t f_end = static_cast<f_cnt_t>( 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<f_cnt_t>( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
const f_cnt_t f_end = static_cast<f_cnt_t>( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
const f_cnt_t f_loop = static_cast<f_cnt_t>( 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<f_cnt_t>( 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;

View File

@@ -96,6 +96,7 @@ private:
BoolModel m_stutterModel;
f_cnt_t m_nextPlayStartPoint;
bool m_nextPlayBackwards;
friend class AudioFileProcessorView;

View File

@@ -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<f_cnt_t>( (
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<f_cnt_t>(
( m_loopEndFrame - play_frame ) /
freq_factor );
play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame );
frames_for_loop = static_cast<f_cnt_t>( ( 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<f_cnt_t>( ( play_frame - m_loopStartFrame ) / freq_factor );
frames_for_loop = static_cast<f_cnt_t>( ( play_frame - loopStartFrame ) / freq_factor );
else
frames_for_loop = static_cast<f_cnt_t>( ( m_loopEndFrame - play_frame ) / freq_factor );
frames_for_loop = static_cast<f_cnt_t>( ( loopEndFrame - play_frame ) / freq_factor );
}
else
{
if( play_frame >= m_endFrame )
if( play_frame >= endFrame )
{
return false;
}
frames_for_loop = static_cast<f_cnt_t>(
( m_endFrame - play_frame ) /
freq_factor );
frames_for_loop = static_cast<f_cnt_t>( ( 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 );
}