diff --git a/plugins/kicker/KickerOsc.h b/plugins/kicker/KickerOsc.h new file mode 100644 index 000000000..edcdcf5e5 --- /dev/null +++ b/plugins/kicker/KickerOsc.h @@ -0,0 +1,91 @@ +/* + * KickerOsc.h - alternative sweeping oscillator + * + * Copyright (c) 2006-2014 Tobias Doerffel + * Copyright (c) 2014 Hannu Haahti + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef KICKER_OSC_H +#define KICKER_OSC_H + +#include "DspEffectLibrary.h" +#include "Oscillator.h" + +#include "fastpow.h" + + +template +class KickerOsc +{ +public: + KickerOsc( const FX & fx, const float start, const float end, const float noise, const float offset, const float slope, const float env, const float length ) : + m_phase( offset ), + m_startFreq( start ), + m_endFreq( end ), + m_noise( noise ), + m_slope( slope ), + m_env( env ), + m_length( length ), + m_FX( fx ), + m_counter( 0 ), + m_freq( start ) + { + } + + virtual ~KickerOsc() + { + } + + void update( sampleFrame* buf, const fpp_t frames, const float sampleRate ) + { + for( fpp_t frame = 0; frame < frames; ++frame ) + { + const double gain = ( 1 - fastPow( ( m_counter < m_length ) ? m_counter / m_length : 1, m_env ) ); + const sample_t s = ( Oscillator::sinSample( m_phase ) * ( 1 - m_noise ) ) + ( Oscillator::noiseSample( 0 ) * gain * gain * m_noise ); + buf[frame][0] = s * gain; + buf[frame][1] = s * gain; + m_FX.nextSample( buf[frame][0], buf[frame][1] ); + m_phase += m_freq / sampleRate; + + const double change = ( m_counter < m_length ) ? ( ( m_startFreq - m_endFreq ) * ( 1 - fastPow( m_counter / m_length, m_slope ) ) ) : 0; + m_freq = m_endFreq + change; + ++m_counter; + } + } + + +private: + float m_phase; + const float m_startFreq; + const float m_endFreq; + const float m_noise; + const float m_slope; + const float m_env; + const float m_length; + FX m_FX; + + unsigned long m_counter; + double m_freq; + +}; + + +#endif diff --git a/plugins/kicker/artwork.png b/plugins/kicker/artwork.png index 12a565044..59c0e1941 100644 Binary files a/plugins/kicker/artwork.png and b/plugins/kicker/artwork.png differ diff --git a/plugins/kicker/fastpow.h b/plugins/kicker/fastpow.h new file mode 100644 index 000000000..48fd83c03 --- /dev/null +++ b/plugins/kicker/fastpow.h @@ -0,0 +1,22 @@ + +#ifndef FASTPOW_H +#define FASTPOW_H + +#include + +/* + * source: + * http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/ + */ + +double fastPow(double a, double b) { + union { + double d; + int32_t x[2]; + } u = { a }; + u.x[1] = (int32_t)(b * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + return u.d; +} + +#endif diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index c04f69edb..5bbd35a10 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -1,7 +1,8 @@ /* - * kicker.cpp - bassdrum-synthesizer + * kicker.cpp - drum synthesizer * * Copyright (c) 2006-2009 Tobias Doerffel + * Copyright (c) 2014 Hannu Haahti * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -31,7 +32,7 @@ #include "InstrumentTrack.h" #include "knob.h" #include "NotePlayHandle.h" -#include "SweepOscillator.h" +#include "KickerOsc.h" #include "embed.cpp" @@ -44,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = STRINGIFY( PLUGIN_NAME ), "Kicker", QT_TRANSLATE_NOOP( "pluginBrowser", - "Versatile kick- & bassdrum-synthesizer" ), + "Versatile drum synthesizer" ), "Tobias Doerffel ", 0x0100, Plugin::Instrument, @@ -60,9 +61,15 @@ kickerInstrument::kickerInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &kicker_plugin_descriptor ), m_startFreqModel( 150.0f, 5.0f, 1000.0f, 1.0f, this, tr( "Start frequency" ) ), m_endFreqModel( 40.0f, 5.0f, 1000.0f, 1.0f, this, tr( "End frequency" ) ), - m_decayModel( 120.0f, 5.0f, 1000.0f, 1.0f, this, tr( "Decay" ) ), + m_decayModel( 440.0f, 5.0f, 2000.0f, 1.0f, this, tr( "Length" ) ), m_distModel( 0.8f, 0.0f, 100.0f, 0.1f, this, tr( "Distortion" ) ), - m_gainModel( 1.0f, 0.1f, 5.0f, 0.05f, this, tr( "Gain" ) ) + m_gainModel( 1.0f, 0.1f, 5.0f, 0.05f, this, tr( "Gain" ) ), + m_envModel( 0.163f, 0.01f, 1.0f, 0.001f, this, tr( "Env" ) ), + m_noiseModel( 0.0f, 0.0f, 1.0f, 0.01f, this, tr( "Noise" ) ), + m_clickModel( 0.4f, 0.0f, 1.0f, 0.05f, this, tr( "Click" ) ), + m_slopeModel( 0.06f, 0.001f, 1.0f, 0.001f, this, tr( "Slope" ) ), + m_startNoteModel( false, this, tr( "Start from note" ) ), + m_endNoteModel( false, this, tr( "End to note" ) ) { } @@ -84,6 +91,12 @@ void kickerInstrument::saveSettings( QDomDocument & _doc, m_decayModel.saveSettings( _doc, _this, "decay" ); m_distModel.saveSettings( _doc, _this, "dist" ); m_gainModel.saveSettings( _doc, _this, "gain" ); + m_envModel.saveSettings( _doc, _this, "env" ); + m_noiseModel.saveSettings( _doc, _this, "noise" ); + m_clickModel.saveSettings( _doc, _this, "click" ); + m_slopeModel.saveSettings( _doc, _this, "slope" ); + m_startNoteModel.saveSettings( _doc, _this, "startnote" ); + m_endNoteModel.saveSettings( _doc, _this, "endnote" ); } @@ -96,6 +109,12 @@ void kickerInstrument::loadSettings( const QDomElement & _this ) m_decayModel.loadSettings( _this, "decay" ); m_distModel.loadSettings( _this, "dist" ); m_gainModel.loadSettings( _this, "gain" ); + m_envModel.loadSettings( _this, "env" ); + m_noiseModel.loadSettings( _this, "noise" ); + m_clickModel.loadSettings( _this, "click" ); + m_slopeModel.loadSettings( _this, "slope" ); + m_startNoteModel.loadSettings( _this, "startnote" ); + m_endNoteModel.loadSettings( _this, "endnote" ); } @@ -108,9 +127,8 @@ QString kickerInstrument::nodeName() const -//typedef DspEffectLibrary::foldbackDistortion<> DistFX; typedef DspEffectLibrary::Distortion DistFX; -typedef SweepOscillator > SweepOsc; +typedef KickerOsc > SweepOsc; void kickerInstrument::playNote( NotePlayHandle * _n, @@ -124,37 +142,32 @@ void kickerInstrument::playNote( NotePlayHandle * _n, { _n->m_pluginData = new SweepOsc( DistFX( m_distModel.value(), - m_gainModel.value() ) ); + m_gainModel.value() ), + m_startNoteModel.value() ? _n->frequency() : m_startFreqModel.value(), + m_endNoteModel.value() ? _n->frequency() : m_endFreqModel.value(), + m_noiseModel.value() * m_noiseModel.value(), + m_clickModel.value() * 0.25f, + m_slopeModel.value(), + m_envModel.value(), + decfr ); } else if( tfp > decfr && !_n->isReleased() ) { _n->noteOff(); } - //const float freq = instrumentTrack()->frequency( _n ) / 2; - const float fdiff = m_endFreqModel.value() - m_startFreqModel.value(); -/* const fpp_t frames = _n->isReleased() ? - tMax( tMin( desiredReleaseFrames() - - _n->releaseFramesDone(), - engine::mixer()->framesPerAudioBuffer() ), 0 ) - : - engine::mixer()->framesPerAudioBuffer();*/ const fpp_t frames = _n->framesLeftForCurrentPeriod(); - const float f1 = m_startFreqModel.value() + tfp * fdiff / decfr; - const float f2 = m_startFreqModel.value() + (frames+tfp-1)*fdiff/decfr; - SweepOsc * so = static_cast( _n->m_pluginData ); - so->update( _working_buffer, frames, f1, f2, - engine::mixer()->processingSampleRate() ); + so->update( _working_buffer, frames, engine::mixer()->processingSampleRate() ); if( _n->isReleased() ) { - const float rfd = _n->releaseFramesDone(); - const float drf = desiredReleaseFrames(); + const float done = _n->releaseFramesDone(); + const float desired = desiredReleaseFrames(); for( fpp_t f = 0; f < frames; ++f ) { - const float fac = 1.0f - ( rfd+f ) / drf; + const float fac = ( done+f < desired ) ? ( 1.0f - ( ( done+f ) / desired ) ) : 0; _working_buffer[f][0] *= fac; _working_buffer[f][1] *= fac; } @@ -188,7 +201,7 @@ public: kickerKnob( QWidget * _parent ) : knob( knobStyled, _parent ) { - setFixedSize( 37, 47 ); + setFixedSize( 35, 45 ); } }; @@ -201,23 +214,45 @@ kickerInstrumentView::kickerInstrumentView( Instrument * _instrument, { m_startFreqKnob = new kickerKnob( this ); m_startFreqKnob->setHintText( tr( "Start frequency:" ) + " ", "Hz" ); - m_startFreqKnob->move( 12, 124 ); + m_startFreqKnob->move( 15, 100 ); m_endFreqKnob = new kickerKnob( this ); m_endFreqKnob->setHintText( tr( "End frequency:" ) + " ", "Hz" ); - m_endFreqKnob->move( 59, 124 ); + m_endFreqKnob->move( 60, 100 ); - m_decayKnob = new kickerKnob( this ); - m_decayKnob->setHintText( tr( "Decay:" ) + " ", "ms" ); - m_decayKnob->move( 107, 124 ); - - m_distKnob = new kickerKnob( this ); - m_distKnob->setHintText( tr( "Distortion:" ) + " ", "" ); - m_distKnob->move( 155, 124 ); + m_slopeKnob = new kickerKnob( this ); + m_slopeKnob->setHintText( tr( "Slope:" ) + " ", "" ); + m_slopeKnob->move( 105, 100 ); m_gainKnob = new kickerKnob( this ); m_gainKnob->setHintText( tr( "Gain:" ) + " ", "" ); - m_gainKnob->move( 203, 124 ); + m_gainKnob->move( 15, 155 ); + + m_decayKnob = new kickerKnob( this ); + m_decayKnob->setHintText( tr( "Length:" ) + " ", "ms" ); + m_decayKnob->move( 60, 155 ); + + m_envKnob = new kickerKnob( this ); + m_envKnob->setHintText( tr( "Env:" ) + " ", "" ); + m_envKnob->move( 105, 155 ); + + m_clickKnob = new kickerKnob( this ); + m_clickKnob->setHintText( tr( "Click:" ) + " ", "" ); + m_clickKnob->move( 200, 45 ); + + m_noiseKnob = new kickerKnob( this ); + m_noiseKnob->setHintText( tr( "Noise:" ) + " ", "" ); + m_noiseKnob->move( 200, 100 ); + + m_distKnob = new kickerKnob( this ); + m_distKnob->setHintText( tr( "Dist:" ) + " ", "" ); + m_distKnob->move( 200, 155 ); + + m_startNoteToggle = new ledCheckBox( "", this ); + m_startNoteToggle->move( 24, 79 ); + + m_endNoteToggle = new ledCheckBox( "", this ); + m_endNoteToggle->move( 69, 79 ); setAutoFillBackground( true ); QPalette pal; @@ -244,6 +279,12 @@ void kickerInstrumentView::modelChanged() m_decayKnob->setModel( &k->m_decayModel ); m_distKnob->setModel( &k->m_distModel ); m_gainKnob->setModel( &k->m_gainModel ); + m_envKnob->setModel( &k->m_envModel ); + m_noiseKnob->setModel( &k->m_noiseModel ); + m_clickKnob->setModel( &k->m_clickModel ); + m_slopeKnob->setModel( &k->m_slopeModel ); + m_startNoteToggle->setModel( &k->m_startNoteModel ); + m_endNoteToggle->setModel( &k->m_endNoteModel ); } diff --git a/plugins/kicker/kicker.h b/plugins/kicker/kicker.h index f6633b07e..8253becf8 100644 --- a/plugins/kicker/kicker.h +++ b/plugins/kicker/kicker.h @@ -1,5 +1,6 @@ /* - * kicker.h - bassdrum-synthesizer + * kicker.h - drum synthesizer + * Copyright (c) 2014 Hannu Haahti * * Copyright (c) 2006-2008 Tobias Doerffel * @@ -30,6 +31,7 @@ #include "Instrument.h" #include "InstrumentView.h" #include "knob.h" +#include "led_checkbox.h" class kickerInstrumentView; @@ -46,12 +48,16 @@ public: 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 Flags flags() const + { + return IsNotBendable; + } + virtual f_cnt_t desiredReleaseFrames() const { return( 512 ); @@ -66,6 +72,13 @@ private: FloatModel m_decayModel; FloatModel m_distModel; FloatModel m_gainModel; + FloatModel m_envModel; + FloatModel m_noiseModel; + FloatModel m_clickModel; + FloatModel m_slopeModel; + + BoolModel m_startNoteModel; + BoolModel m_endNoteModel; friend class kickerInstrumentView; @@ -88,6 +101,13 @@ private: knob * m_decayKnob; knob * m_distKnob; knob * m_gainKnob; + knob * m_envKnob; + knob * m_noiseKnob; + knob * m_clickKnob; + knob * m_slopeKnob; + + ledCheckBox * m_startNoteToggle; + ledCheckBox * m_endNoteToggle; } ;