From b7b361414acc9481be5e5c65bc0812f45131bf58 Mon Sep 17 00:00:00 2001 From: groboclown Date: Wed, 22 Jan 2014 20:05:10 -0600 Subject: [PATCH] Add "stutter" capability to the audio file processor. --- include/SampleBuffer.h | 11 +++ .../audio_file_processor.cpp | 66 ++++++++++++++++-- .../audio_file_processor.h | 5 ++ plugins/audio_file_processor/stutter_off.png | Bin 0 -> 526 bytes plugins/audio_file_processor/stutter_on.png | Bin 0 -> 588 bytes 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 plugins/audio_file_processor/stutter_off.png create mode 100644 plugins/audio_file_processor/stutter_on.png diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 22038b2b3..cd5a243e1 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -52,6 +52,17 @@ public: handleState( bool _varying_pitch = false ); virtual ~handleState(); + inline const f_cnt_t frameIndex() const + { + return m_frameIndex; + } + + inline void setFrameIndex( f_cnt_t _index ) + { + m_frameIndex = _index; + } + + private: f_cnt_t m_frameIndex; diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 337d6e601..2ef2f8a96 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -75,7 +75,9 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : m_startPointModel( 0, 0, 1, 0.0000001f, this, tr( "Start of sample") ), m_endPointModel( 1, 0, 1, 0.0000001f, this, tr( "End of sample" ) ), m_reverseModel( false, this, tr( "Reverse sample" ) ), - m_loopModel( false, this, tr( "Loop") ) + m_loopModel( false, this, tr( "Loop") ), + m_stutterModel( false, this, tr( "Stutter" ) ), + m_nextPlayStartPoint( 0 ) { connect( &m_reverseModel, SIGNAL( dataChanged() ), this, SLOT( reverseModelChanged() ) ); @@ -85,6 +87,8 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : this, SLOT( loopPointChanged() ) ); connect( &m_endPointModel, SIGNAL( dataChanged() ), this, SLOT( loopPointChanged() ) ); + connect( &m_stutterModel, SIGNAL( dataChanged() ), + this, SLOT( stutterModelChanged() ) ); } @@ -102,9 +106,25 @@ void audioFileProcessor::playNote( notePlayHandle * _n, { const fpp_t frames = _n->framesLeftForCurrentPeriod(); + // Magic key - a frequency < 20 (say, the bottom piano note if using + // a A4 base tuning) restarts the start point. The note is not actually + // played. + if( m_stutterModel.value() == true && _n->frequency() < 20.0 ) + { + m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + return; + } + if( !_n->m_pluginData ) { + if( m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sampleBuffer.endFrame() ) + { + // 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(); + } _n->m_pluginData = new handleState( _n->hasDetuningInfo() ); + ((handleState *)_n->m_pluginData)->setFrameIndex(m_nextPlayStartPoint); } if( m_sampleBuffer.play( _working_buffer, @@ -115,12 +135,25 @@ void audioFileProcessor::playNote( notePlayHandle * _n, applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, frames,_n ); - emit isPlaying( _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency() ); + int framesPosition; + if( m_stutterModel.value() == true ) + { + framesPosition = m_nextPlayStartPoint; + } + else + { + framesPosition = _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency(); + } + emit isPlaying( framesPosition ); } else { emit isPlaying( 0 ); } + if( m_stutterModel.value() == true ) + { + m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex(); + } } @@ -246,6 +279,12 @@ void audioFileProcessor::ampModelChanged( void ) } +void audioFileProcessor::stutterModelChanged() +{ + m_nextPlayStartPoint = m_sampleBuffer.startFrame(); +} + + void audioFileProcessor::loopPointChanged( void ) @@ -254,6 +293,7 @@ void audioFileProcessor::loopPointChanged( void ) ( m_sampleBuffer.frames()-1 ) ); const f_cnt_t f2 = static_cast( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + m_nextPlayStartPoint = f1; m_sampleBuffer.setStartFrame( qMin( f1, f2 ) ); m_sampleBuffer.setEndFrame( qMax( f1, f2 ) ); emit dataChanged(); @@ -295,7 +335,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, m_reverseButton = new pixmapButton( this ); m_reverseButton->setCheckable( TRUE ); - m_reverseButton->move( 184, 124 ); + m_reverseButton->move( 174, 124 ); m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "reverse_on" ) ); m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -308,7 +348,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, m_loopButton = new pixmapButton( this ); m_loopButton->setCheckable( TRUE ); - m_loopButton->move( 220, 124 ); + m_loopButton->move( 200, 124 ); m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "loop_on" ) ); m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -322,6 +362,23 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "This is useful for things like string and choir " "samples." ) ); + m_stutterButton = new pixmapButton( this ); + m_stutterButton->setCheckable( true ); + m_stutterButton->move( 226, 124 ); + m_stutterButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "stutter_on" ) ); + m_stutterButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "stutter_off" ) ); + toolTip::add( m_stutterButton, + tr( "Continue sample playback across notes" ) ); + m_stutterButton->setWhatsThis( + tr( "Enabling this option makes the sample continue playing " + "across different notes - if you change pitch, or the note " + "length stops before the end of the sample, then the next " + "note played will continue where it left off. To reset the " + "playback to the start of the sample, insert a note at the bottom " + "of the keyboard (< 20 Hz)") ); + m_ampKnob = new knob( knobStyled, this ); m_ampKnob->setVolumeKnob( TRUE ); m_ampKnob->move( 17, 108 ); @@ -501,6 +558,7 @@ void AudioFileProcessorView::modelChanged( void ) m_endKnob->setModel( &a->m_endPointModel ); m_reverseButton->setModel( &a->m_reverseModel ); m_loopButton->setModel( &a->m_loopModel ); + m_stutterButton->setModel( &a->m_stutterModel ); sampleUpdated(); } diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index fb8c5feec..e9d6a44e6 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -74,6 +74,7 @@ private slots: void reverseModelChanged(); void ampModelChanged(); void loopPointChanged(); + void stutterModelChanged(); signals: @@ -90,6 +91,9 @@ private: FloatModel m_endPointModel; BoolModel m_reverseModel; BoolModel m_loopModel; + BoolModel m_stutterModel; + + f_cnt_t m_nextPlayStartPoint; friend class AudioFileProcessorView; @@ -131,6 +135,7 @@ private: pixmapButton * m_openAudioFileButton; pixmapButton * m_reverseButton; pixmapButton * m_loopButton; + pixmapButton * m_stutterButton; } ; diff --git a/plugins/audio_file_processor/stutter_off.png b/plugins/audio_file_processor/stutter_off.png new file mode 100644 index 0000000000000000000000000000000000000000..8a24d2ada1576d5327fc5f1623f791fa3ceec86c GIT binary patch literal 526 zcmV+p0`dKcP)Oc@hS3jao#DSm@CHf8iiC^l+IEmmpPIM+h z26@#P91@a_@!r7+6ojIBb@i^@tpMQNn&D;qrilTD8yZD>2>)@7#U9fLguMTYiU2Xp7F1wF|;*THL9~Zk@R34%W Q$p8QV07*qoM6N<$g1Q#kV*mgE literal 0 HcmV?d00001 diff --git a/plugins/audio_file_processor/stutter_on.png b/plugins/audio_file_processor/stutter_on.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb9a1575d9a58edd35826432088960503b6286e GIT binary patch literal 588 zcmV-S0<-;zP)u*`gT@6FpWfO3kD z18B8cGZz?RrhZo~$I9Q|G8z8@^QiZ} z4Cb*IodyPEYRX}=O$;IiqdC}uI3*oQp?&~H6vk3c*;SPX&vse3bi(L+jd#y)F+8qu z_1+OTo@}G49KLO`^Ku1MW$DflH=lk$jpyLiGQEvP7Fej=>celS9ngQ1@bhFybNvtz zq4}uK=Pk>@X2RwB`&7-s?KY{RWK{$FHvqc3vz4@V;}K#S4qnm(H@kahK5` zi(#v?#`%ji>Q^fCy4M;0uvtw3ZMWN4YY~ycJIwP99uCBNPnxErX*!vAI-M-#d|%~9 zILXj&am&Q(3pJdDg+q(`Fhn^M^xjXpKHfa9vGZ^N4Cpj$-mjNOOJH1^|M{2W%fWEU a7wm78F;yX6`sO(R0000