diff -Nur lmms-0.4.15/include/sample_buffer.h lmms-0.4.15.new/include/sample_buffer.h --- lmms-0.4.15/include/sample_buffer.h 2013-07-02 13:44:59.699599386 +0000 +++ lmms-0.4.15.new/include/sample_buffer.h 2013-07-02 02:13:32.000000000 +0000 @@ -51,6 +51,15 @@ 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: diff -Nur lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.cpp lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.cpp --- lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.cpp 2013-07-02 13:44:59.686266104 +0000 +++ lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.cpp 2013-07-02 02:13:40.000000000 +0000 @@ -75,7 +75,9 @@ 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 @@ this, SLOT( loopPointChanged() ) ); connect( &m_endPointModel, SIGNAL( dataChanged() ), this, SLOT( loopPointChanged() ) ); + connect( &m_stutterModel, SIGNAL( dataChanged() ), + this, SLOT( stutterModelChanged() ) ); } @@ -102,10 +106,27 @@ { 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, (handleState *)_n->m_pluginData, @@ -115,12 +136,25 @@ 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(); + } } @@ -249,6 +283,10 @@ } +void audioFileProcessor::stutterModelChanged() +{ + m_nextPlayStartPoint = m_sampleBuffer.startFrame(); +} void audioFileProcessor::loopPointChanged( void ) @@ -257,6 +295,7 @@ ( 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(); @@ -298,7 +337,7 @@ m_reverseButton = new pixmapButton( this ); m_reverseButton->setCheckable( TRUE ); - m_reverseButton->move( 184, 124 ); + m_reverseButton->move( 176, 124 ); // 184 m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "reverse_on" ) ); m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -311,7 +350,7 @@ m_loopButton = new pixmapButton( this ); m_loopButton->setCheckable( TRUE ); - m_loopButton->move( 220, 124 ); + m_loopButton->move( 202, 124 ); // 220 m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "loop_on" ) ); m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -325,6 +364,23 @@ "This is useful for things like string and choir " "samples." ) ); + m_stutterButton = new pixmapButton( this ); + m_stutterButton->setCheckable( true ); + m_stutterButton->move( 228, 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 ); @@ -504,6 +560,7 @@ 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(); } @@ -522,8 +579,7 @@ m_endKnob( 0 ), m_isDragging( false ), m_reversed( false ), - m_framesPlayed( 0 ), - m_animation(configManager::inst()->value("ui", "animateafp").toInt()) + m_framesPlayed( 0 ) { setFixedSize( _w, _h ); setMouseTracking( true ); @@ -546,11 +602,13 @@ const f_cnt_t nb_frames = m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame(); if( nb_frames < 1 ) { +/* MCA */printf("reset framesPlayed to 0\n"); m_framesPlayed = 0; } else { m_framesPlayed = _frames_played % nb_frames; +/* MCA */printf("isPlaying: framesPlayed = %d\n", m_framesPlayed); } update(); } @@ -702,7 +760,7 @@ QColor( 255, 255, 0, 70 ) ); - if( m_framesPlayed && m_animation) + if( m_framesPlayed ) { const int played_width_px = m_framesPlayed / double( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) @@ -933,12 +991,8 @@ return; } const double v = double( _frames ) / m_sampleBuffer.frames(); - if(m_startKnob) { - m_startKnob->slideBy( v, false ); - } - if(m_endKnob) { - m_endKnob->slideBy( v, false ); - } + m_startKnob->slideBy( v, false ); + m_endKnob->slideBy( v, false ); } diff -Nur lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.h lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.h --- lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.h 2013-07-02 13:44:59.686266104 +0000 +++ lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.h 2013-07-02 02:13:44.000000000 +0000 @@ -74,6 +74,7 @@ void reverseModelChanged(); void ampModelChanged(); void loopPointChanged(); + void stutterModelChanged(); signals: @@ -90,6 +91,10 @@ FloatModel m_endPointModel; BoolModel m_reverseModel; BoolModel m_loopModel; + BoolModel m_stutterModel; + + f_cnt_t m_nextPlayStartPoint; + friend class AudioFileProcessorView; @@ -131,6 +136,7 @@ pixmapButton * m_openAudioFileButton; pixmapButton * m_reverseButton; pixmapButton * m_loopButton; + pixmapButton * m_stutterButton; } ; @@ -234,7 +240,7 @@ draggingType m_draggingType; bool m_reversed; f_cnt_t m_framesPlayed; - bool m_animation; + public: AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf );