diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 2e942a6ab..01beac525 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -1,6 +1,7 @@ /* * sfxr.cpp - port of sfxr to LMMS - * The original readme file of sfxr can be found in readme.txt in this directory. + * Originally written by Tomas Pettersson. For the original license, + * please read readme.txt in this directory * * Copyright (c) 2014 Wong Cho Ching * @@ -38,10 +39,8 @@ float frnd(float range) #include "sfxr.h" #include "engine.h" -#include "graph.h" #include "InstrumentTrack.h" #include "knob.h" -#include "led_checkbox.h" #include "note_play_handle.h" #include "pixmap_button.h" #include "song_editor.h" @@ -71,73 +70,252 @@ Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor = } -SfxrSynth::SfxrSynth( float * _shape, int _length, notePlayHandle * _nph, bool _interpolation, - float _factor, const sample_rate_t _sample_rate ) : - sample_index( 0 ), - sample_realindex( 0 ), - nph( _nph ), - sample_length( _length ), - sample_rate( _sample_rate ), - interpolation( _interpolation) + + +SfxrSynth::SfxrSynth( const sfxrInstrument * s ): + s(s), + playing_sample( true ) { - sample_shape = new float[sample_length]; - for (int i=0; i < _length; ++i) - { - sample_shape[i] = _shape[i] * _factor; - } + resetSample( false ); } + + SfxrSynth::~SfxrSynth() { - delete[] sample_shape; + } -sample_t SfxrSynth::nextStringSample() + + +void SfxrSynth::resetSample( bool restart ) { - float sample_step = - static_cast( sample_length / ( sample_rate / nph->frequency() ) ); - - - // check overflow - while (sample_realindex >= sample_length) { - sample_realindex -= sample_length; + if(!restart) + { + phase=0; } + fperiod=100.0/(s->m_startFreqModel.value()*s->m_startFreqModel.value()+0.001); + period=(int)fperiod; + fmaxperiod=100.0/(s->m_minFreqModel.value()*s->m_minFreqModel.value()+0.001); + fslide=1.0-pow((double)s->m_slideModel.value(), 3.0)*0.01; + fdslide=-pow((double)s->m_dSlideModel.value(), 3.0)*0.000001; + square_duty=0.5f-s->m_sqrDutyModel.value()*0.5f; + square_slide=-s->m_sqrSweepModel.value()*0.00005f; + if(s->m_changeAmtModel.value()>=0.0f) + arp_mod=1.0-pow((double)s->m_changeAmtModel.value(), 2.0)*0.9; + else + arp_mod=1.0+pow((double)s->m_changeAmtModel.value(), 2.0)*10.0; + arp_time=0; + arp_limit=(int)(pow(1.0f-s->m_changeSpeedModel.value(), 2.0f)*20000+32); + if(s->m_changeSpeedModel.value()==1.0f) + arp_limit=0; + if(!restart) + { + // reset filter + fltp=0.0f; + fltdp=0.0f; + fltw=pow(s->m_lpFilCutModel.value(), 3.0f)*0.1f; + fltw_d=1.0f+s->m_lpFilCutSweepModel.value()*0.0001f; + fltdmp=5.0f/(1.0f+pow(s->m_lpFilResoModel.value(), 2.0f)*20.0f)*(0.01f+fltw); + if(fltdmp>0.8f) fltdmp=0.8f; + fltphp=0.0f; + flthp=pow(s->m_hpFilCutModel.value(), 2.0f)*0.1f; + flthp_d=1.0+s->m_hpFilCutSweepModel.value()*0.0003f; + // reset vibrato + vib_phase=0.0f; + vib_speed=pow(s->m_vibSpeedModel.value(), 2.0f)*0.01f; + vib_amp=s->m_vibDepthModel.value()*0.5f; + // reset envelope + env_vol=0.0f; + env_stage=0; + env_time=0; + env_length[0]=(int)(s->m_attModel.value()*s->m_attModel.value()*100000.0f); + env_length[1]=(int)(s->m_holdModel.value()*s->m_holdModel.value()*100000.0f); + env_length[2]=(int)(s->m_decModel.value()*s->m_decModel.value()*100000.0f); - sample_t sample; + fphase=pow(s->m_phaserOffsetModel.value(), 2.0f)*1020.0f; + if(s->m_phaserOffsetModel.value()<0.0f) fphase=-fphase; + fdphase=pow(s->m_phaserSweepModel.value(), 2.0f)*1.0f; + if(s->m_phaserSweepModel.value()<0.0f) fdphase=-fdphase; + iphase=abs((int)fphase); + ipp=0; + for(int i=0;i<1024;i++) + phaser_buffer[i]=0.0f; - if (interpolation) { + for(int i=0;i<32;i++) + noise_buffer[i]=frnd(2.0f)-1.0f; - // find position in shape - int a = static_cast(sample_realindex); - int b; - if (a < (sample_length-1)) { - b = static_cast(sample_realindex+1); - } else { - b = 0; + rep_time=0; + rep_limit=(int)(pow(1.0f-s->m_repeatSpeedModel.value(), 2.0f)*20000+32); + if(s->m_repeatSpeedModel.value()==0.0f) + rep_limit=0; + } +} + + + + +void SfxrSynth::update( sampleFrame * buffer, const fpp_t frameNum ) +{ + for(int i=0;i=rep_limit) + { + rep_limit=0; + resetSample(true); } - // Nachkommaanteil - float frac = sample_realindex - static_cast(sample_realindex); + // frequency envelopes/arpeggios + arp_time++; + if(arp_limit!=0 && arp_time>=arp_limit) + { + arp_limit=0; + fperiod*=arp_mod; + } + fslide+=fdslide; + fperiod*=fslide; + if(fperiod>fmaxperiod) + { + fperiod=fmaxperiod; + if(s->m_minFreqModel.value()>0.0f) + playing_sample=false; + } + float rfperiod=fperiod; + if(vib_amp>0.0f) + { + vib_phase+=vib_speed; + rfperiod=fperiod*(1.0+sin(vib_phase)*vib_amp); + } + period=(int)rfperiod; + if(period<8) period=8; + square_duty+=square_slide; + if(square_duty<0.0f) square_duty=0.0f; + if(square_duty>0.5f) square_duty=0.5f; + // volume envelope + env_time++; + if(env_time>env_length[env_stage]) + { + env_time=0; + env_stage++; + if(env_stage==3) + playing_sample=false; + } + if(env_stage==0) + env_vol=(float)env_time/env_length[0]; + if(env_stage==1) + env_vol=1.0f+pow(1.0f-(float)env_time/env_length[1], 1.0f)*2.0f*s->m_susModel.value(); + if(env_stage==2) + env_vol=1.0f-(float)env_time/env_length[2]; - sample = sample_shape[a]*(1-frac) + sample_shape[b]*(frac); + // phaser step + fphase+=fdphase; + iphase=abs((int)fphase); + if(iphase>1023) iphase=1023; - } else { - // No interpolation - sample_index = static_cast(sample_realindex); - sample = sample_shape[sample_index]; + if(flthp_d!=0.0f) + { + flthp*=flthp_d; + if(flthp<0.00001f) flthp=0.00001f; + if(flthp>0.1f) flthp=0.1f; + } + + float ssample=0.0f; + for(int si=0;si<8;si++) // 8x supersampling + { + float sample=0.0f; + phase++; + if(phase>=period) + { +// phase=0; + phase%=period; + if(s->m_waveFormModel.value()==3) + for(int i=0;i<32;i++) + noise_buffer[i]=frnd(2.0f)-1.0f; + } + // base waveform + float fp=(float)phase/period; + switch(s->m_waveFormModel.value()) + { + case 0: // square + if(fp0.1f) fltw=0.1f; + if(s->m_lpFilCutModel.value()!=1.0f) + { + fltdp+=(sample-fltp)*fltw; + fltdp-=fltdp*fltdmp; + } + else + { + fltp=sample; + fltdp=0.0f; + } + fltp+=fltdp; + // hp filter + fltphp+=fltp-pp; + fltphp-=fltphp*flthp; + sample=fltphp; + // phaser + phaser_buffer[ipp&1023]=sample; + sample+=phaser_buffer[(ipp-iphase+1024)&1023]; + ipp=(ipp+1)&1023; + // final accumulation and envelope application + ssample+=sample*env_vol; + } + //ssample=ssample/8*master_vol; + + //ssample*=2.0f*sound_vol; + ssample*=0.05f; + + if(buffer!=NULL) + { + if(ssample>1.0f) ssample=1.0f; + if(ssample<-1.0f) ssample=-1.0f; + for( ch_cnt_t j=0; j < DEFAULT_CHANNELS; j++ ) + { + buffer[i][j]=ssample; + } + } } - - // progress in shape - sample_realindex += sample_step; - - return sample; } +bool SfxrSynth::isPlaying() const +{ + return playing_sample; +} + + sfxrInstrument::sfxrInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &sfxr_plugin_descriptor ), m_attModel(0.0f, this), @@ -170,14 +348,6 @@ sfxrInstrument::sfxrInstrument( InstrumentTrack * _instrument_track ) : m_hpFilCutSweepModel(0.0f, this), m_waveFormModel( SQR_WAVE, 0, WAVES_NUM-1, this, tr( "Wave Form" ) ) { - //TODO - /* - connect( &m_sampleLength, SIGNAL( dataChanged( ) ), - this, SLOT( lengthChanged( ) ) ); - - connect( &m_graph, SIGNAL( samplesChanged( int, int ) ), - this, SLOT( samplesChanged( int, int ) ) ); - */ } @@ -267,16 +437,6 @@ void sfxrInstrument::loadSettings( const QDomElement & _this ) -void sfxrInstrument::samplesChanged( int _begin, int _end ) -{ - //TODO - //normalize(); - //engine::getSongEditor()->setModified(); -} - - - - QString sfxrInstrument::nodeName() const { return( sfxr_plugin_descriptor.name ); @@ -285,54 +445,23 @@ QString sfxrInstrument::nodeName() const -f_cnt_t sfxrInstrument::desiredReleaseFrames() const +void sfxrInstrument::playNote(notePlayHandle * _n, sampleFrame * _working_buffer ) { - //TODO: check whether this disables - return 0; -} - - - -void sfxrInstrument::playNote( notePlayHandle * _n, - sampleFrame * _working_buffer ) -{ - //TODO - /*if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) + m_synthMutex.lock(); + fpp_t frameNum = _n->framesLeftForCurrentPeriod(); + if ( _n->totalFramesPlayed() == 0 ) { - - float factor; - if( !m_normalize.value() ) - { - factor = 1.0f; - } - else - { - factor = m_normalizeFactor; - } - - _n->m_pluginData = new SfxrSynth( - const_cast( m_graph.samples() ), - m_graph.length(), - _n, - m_interpolation.value(), factor, - engine::mixer()->processingSampleRate() ); + _n->m_pluginData = new SfxrSynth( this ); + } + else if( static_cast(_n->m_pluginData)->isPlaying() == false ) + { + _n->noteOff(); } - const fpp_t frames = _n->framesLeftForCurrentPeriod(); + static_cast(_n->m_pluginData)->update( _working_buffer, frameNum ); + m_synthMutex.unlock(); - SfxrSynth * ps = static_cast( _n->m_pluginData ); - for( fpp_t frame = 0; frame < frames; ++frame ) - { - const sample_t cur = ps->nextStringSample(); - for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) - { - _working_buffer[frame][chnl] = cur; - } - } - - applyRelease( _working_buffer, _n ); - - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );*/ + instrumentTrack()->processAudioBuffer( _working_buffer, frameNum, NULL ); } @@ -356,6 +485,7 @@ PluginView * sfxrInstrument::instantiateView( QWidget * _parent ) void sfxrInstrument::resetModels() { + m_attModel.reset(); m_holdModel.reset(); m_susModel.reset(); @@ -463,7 +593,7 @@ sfxrInstrumentView::sfxrInstrumentView( Instrument * _instrument, createKnob(m_changeSpeedKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*1, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Change Speed"); createKnob(m_sqrDutyKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Squre Duty(Square wave only)"); - createKnob(m_sqrSpeedKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Squre Sweep(Square wave only)"); + createKnob(m_sqrSweepKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Squre Sweep(Square wave only)"); createKnob(m_repeatSpeedKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*3, "Repeat Speed"); @@ -485,9 +615,7 @@ sfxrInstrumentView::sfxrInstrumentView( Instrument * _instrument, m_waveBtnGroup->addButton(m_sqrWaveBtn); m_waveBtnGroup->addButton(m_sawWaveBtn); m_waveBtnGroup->addButton(m_sinWaveBtn); - m_waveBtnGroup->addButton(m_noiseWaveBtn); - connect( m_waveBtnGroup, SIGNAL ( dataChanged() ), - this, SLOT ( waveFormChanged() ) ); + m_waveBtnGroup->addButton(m_noiseWaveBtn); createButtonLocalGraphic(m_pickupBtn, GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*0, GENERATOR_BASE_Y, "Generate pick up/coin sfx", "pickup"); @@ -511,10 +639,6 @@ sfxrInstrumentView::sfxrInstrumentView( Instrument * _instrument, connect( m_randomizeBtn, SIGNAL ( clicked() ), this, SLOT ( randomize() ) ); connect( m_mutateBtn, SIGNAL ( clicked() ), this, SLOT ( mutate() ) ); - //TODO: for each generator button: - /*connect( m_pickupBtn, SIGNAL ( dataChanged() ), - this, SLOT ( pickupClicked() ) );*/ - } @@ -541,7 +665,7 @@ void sfxrInstrumentView::modelChanged() m_changeSpeedKnob->setModel( &s->m_changeSpeedModel ); m_sqrDutyKnob->setModel( &s->m_sqrDutyModel ); - m_sqrSpeedKnob->setModel( &s->m_sqrSweepModel ); + m_sqrSweepKnob->setModel( &s->m_sqrSweepModel ); m_repeatSpeedKnob->setModel( &s->m_repeatSpeedModel ); @@ -560,32 +684,20 @@ void sfxrInstrumentView::modelChanged() -void sfxrInstrumentView::waveFormChanged() -{ - //TODO: do we even need this slot? - /* - m_graph->model()->setWaveToNoise(); - engine::getSong()->setModified(); - */ -} - - - - void sfxrInstrumentView::genPickup() { sfxrInstrument * s = castModel(); s->resetModels(); - s->m_startFreqModel.setValue( 0.4f+frnd(0.5f) ); - s->m_attModel.setValue( 0.0f ); - s->m_holdModel.setValue( frnd(0.1f) ); - s->m_decModel.setValue( 0.1f+frnd(0.4f) ); - s->m_susModel.setValue( 0.3f+frnd(0.3f) ); + s->m_startFreqModel.setValue( 0.4f+frnd(0.5f) ); + s->m_attModel.setValue( 0.0f ); + s->m_holdModel.setValue( frnd(0.1f) ); + s->m_decModel.setValue( 0.1f+frnd(0.4f) ); + s->m_susModel.setValue( 0.3f+frnd(0.3f) ); if(rnd(1)) { - s->m_changeAmtModel.setValue( 0.5f+frnd(0.2f) ); - s->m_changeSpeedModel.setValue( 0.2f+frnd(0.4f) ); + s->m_changeSpeedModel.setValue( 0.5f+frnd(0.2f) ); + s->m_changeAmtModel.setValue( 0.2f+frnd(0.4f) ); } } @@ -599,9 +711,9 @@ void sfxrInstrumentView::genLaser() s->m_waveFormModel.setValue( rnd(2) ); if(s->m_waveFormModel.value()==2 && rnd(1)) - s->m_waveFormModel.setValue(rnd(1)); + s->m_waveFormModel.setValue( rnd(1) ); - s->m_startFreqModel.setValue( 0.5f+frnd(0.5f) ); + s->m_startFreqModel.setValue( 0.5f+frnd(0.5f) ); s->m_minFreqModel.setValue( s->m_startFreqModel.value()-0.2f-frnd(0.6f) ); if(s->m_minFreqModel.value()<0.2f) @@ -615,7 +727,7 @@ void sfxrInstrumentView::genLaser() { s->m_startFreqModel.setValue( 0.3f+frnd(0.6f) ); s->m_minFreqModel.setValue( frnd(0.1f) ); - s->m_slideModel.setValue( -0.35f-frnd(0.3f) ); + s->m_slideModel.setValue( -0.35f-frnd(0.3f) ); } if(rnd(1)) @@ -747,9 +859,13 @@ void sfxrInstrumentView::genHit() s->m_waveFormModel.setValue( rnd(2) ); if(s->m_waveFormModel.value()==2) + { s->m_waveFormModel.setValue( 3 ); + } if(s->m_waveFormModel.value()==0) + { s->m_sqrDutyModel.setValue( frnd(0.6f) ); + } s->m_startFreqModel.setValue( 0.2f+frnd(0.6f) ); s->m_slideModel.setValue( -0.3f-frnd(0.4f) ); @@ -785,12 +901,12 @@ void sfxrInstrumentView::genJump() { s->m_hpFilCutModel.setValue( frnd(0.3f) ); } - if(rnd(1)) { s->m_lpFilCutModel.setValue( 1.0f-frnd(0.6f) ); } + } diff --git a/plugins/sfxr/sfxr.h b/plugins/sfxr/sfxr.h index e363dd343..2d9934219 100644 --- a/plugins/sfxr/sfxr.h +++ b/plugins/sfxr/sfxr.h @@ -1,6 +1,7 @@ /* * sfxr.h - declaration of classes of the LMMS sfxr plugin - * The original readme file of sfxr can be found in readme.txt in this directory. + * Originally written by Tomas Pettersson. For the original license, + * please read readme.txt in this directory * * Copyright (c) 2014 Wong Cho Ching * @@ -60,28 +61,64 @@ const int KNOB_BLOCK_SIZE_X = 40; const int KNOB_BLOCK_SIZE_Y = 40; + + +class sfxrInstrument; + + + class SfxrSynth { public: - SfxrSynth( float * sample, int length, notePlayHandle * _nph, - bool _interpolation, float factor, - const sample_rate_t _sample_rate ); + SfxrSynth( const sfxrInstrument * s ); virtual ~SfxrSynth(); - sample_t nextStringSample(); + void resetSample( bool restart ); + void update( sampleFrame * buffer, const fpp_t frameNum ); + bool isPlaying() const; private: - int sample_index; - float sample_realindex; - float* sample_shape; - notePlayHandle* nph; - const int sample_length; - const sample_rate_t sample_rate; + const sfxrInstrument * s; + bool playing_sample; + int phase; + double fperiod; + double fmaxperiod; + double fslide; + double fdslide; + int period; + float square_duty; + float square_slide; + int env_stage; + int env_time; + int env_length[3]; + float env_vol; + float fphase; + float fdphase; + int iphase; + float phaser_buffer[1024]; + int ipp; + float noise_buffer[32]; + float fltp; + float fltdp; + float fltw; + float fltw_d; + float fltdmp; + float fltphp; + float flthp; + float flthp_d; + float vib_phase; + float vib_speed; + float vib_amp; + int rep_time; + int rep_limit; + int arp_time; + int arp_limit; + double arp_mod; + +} ; - bool interpolation; -}; /** * @brief A class that simplify the constructor of FloatModel, with value [0,1] @@ -93,6 +130,15 @@ public: FloatModel( val, 0.0, 1.0, 0.001, parent) { } + /* purpose: prevent the initial value of the model from being changed */ + virtual void loadSettings( const QDomElement& element, const QString& name = QString( "value" ) ) + { + float oldInitValue = initValue(); + FloatModel::loadSettings(element, name); + float oldValue = value(); + setInitValue(oldInitValue); + setValue(oldValue); + } }; /** @@ -105,6 +151,15 @@ public: FloatModel( val, -1.0, 1.0, 0.001, parent) { } + /* purpose: prevent the initial value of the model from being changed */ + virtual void loadSettings( const QDomElement& element, const QString& name = QString( "value" ) ) + { + float oldInitValue = initValue(); + FloatModel::loadSettings(element, name); + float oldValue = value(); + setInitValue(oldInitValue); + setValue(oldValue); + } }; class sfxrInstrument : public Instrument @@ -114,28 +169,22 @@ public: sfxrInstrument(InstrumentTrack * _instrument_track ); virtual ~sfxrInstrument(); - virtual void playNote( notePlayHandle * _n, - sampleFrame * _working_buffer ); + virtual void playNote( notePlayHandle * _n, sampleFrame * _working_buffer ); virtual void deleteNotePluginData( notePlayHandle * _n ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); virtual void loadSettings( const QDomElement & _this ); virtual QString nodeName() const; - virtual f_cnt_t desiredReleaseFrames() const; - virtual PluginView * instantiateView( QWidget * _parent ); void resetModels(); -protected slots: - void samplesChanged( int, int ); - private: + QMutex m_synthMutex; SfxrZeroToOneFloatModel m_attModel; SfxrZeroToOneFloatModel m_holdModel; SfxrZeroToOneFloatModel m_susModel; @@ -168,6 +217,7 @@ private: IntModel m_waveFormModel; friend class sfxrInstrumentView; + friend class SfxrSynth; }; @@ -182,7 +232,6 @@ public: virtual ~sfxrInstrumentView() {}; protected slots: - void waveFormChanged(); void genPickup(); void genLaser(); void genExplosion(); @@ -212,7 +261,7 @@ private: knob * m_changeSpeedKnob; //Change Speed knob * m_sqrDutyKnob; //Squre Duty - knob * m_sqrSpeedKnob; //Squre Sweep + knob * m_sqrSweepKnob; //Squre Sweep knob * m_repeatSpeedKnob; //Repeat Speed