diff --git a/include/Graph.h b/include/Graph.h index 018b0ff0d..9474c354d 100644 --- a/include/Graph.h +++ b/include/Graph.h @@ -137,7 +137,7 @@ public: inline int length() const { - return( m_samples.count() ); + return m_length; } inline const float * samples() const @@ -175,6 +175,7 @@ private: void drawSampleAt( int x, float val ); QVector m_samples; + int m_length; float m_minValue; float m_maxValue; float m_step; diff --git a/include/MixHelpers.h b/include/MixHelpers.h index 4a2f510ee..ec5395a90 100644 --- a/include/MixHelpers.h +++ b/include/MixHelpers.h @@ -42,6 +42,9 @@ void add( sampleFrame* dst, const sampleFrame* src, int frames ); /*! \brief Add samples from src multiplied by coeffSrc to dst */ void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); +/*! \brief Add samples from src multiplied by coeffSrc to dst, swap inputs */ +void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); + /*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst */ void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ); diff --git a/include/RingBuffer.h b/include/RingBuffer.h index d32ef01ed..db903109d 100644 --- a/include/RingBuffer.h +++ b/include/RingBuffer.h @@ -179,6 +179,24 @@ public: */ void writeAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ); +/** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with + * a specified multiplier applied to the frames, with swapped channels + * \param src Pointer to the source buffer + * \param offset Offset in frames against current position, may *NOT* be negative + * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! + * \param level Multiplier applied to the frames before they're written to the ringbuffer + */ + void writeSwappedAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ); + +/** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with + * a specified multiplier applied to the frames, with swapped channels + * \param src Pointer to the source buffer + * \param offset Offset in milliseconds against current position, may *NOT* be negative + * \param length Length of the source buffer, if zero, period size is used + * \param level Multiplier applied to the frames before they're written to the ringbuffer + */ + void writeSwappedAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ); + protected slots: void updateSamplerate(); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4f4a668e7..0f98b5d8f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -16,6 +16,7 @@ ADD_SUBDIRECTORY(LadspaEffect) ADD_SUBDIRECTORY(lb302) #ADD_SUBDIRECTORY(lb303) ADD_SUBDIRECTORY(MidiImport) +ADD_SUBDIRECTORY(MultitapEcho) ADD_SUBDIRECTORY(monstro) ADD_SUBDIRECTORY(nes) ADD_SUBDIRECTORY(opl2) diff --git a/plugins/MultitapEcho/CMakeLists.txt b/plugins/MultitapEcho/CMakeLists.txt new file mode 100644 index 000000000..bfd9e6249 --- /dev/null +++ b/plugins/MultitapEcho/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(multitapecho MultitapEcho.cpp MultitapEchoControls.cpp MultitapEchoControlDialog.cpp MOCFILES MultitapEchoControls.h MultitapEchoControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp new file mode 100644 index 000000000..abbd34f25 --- /dev/null +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -0,0 +1,155 @@ +/* + * MultitapEcho.cpp - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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. + * + */ + +#include "MultitapEcho.h" +#include "embed.cpp" + + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT multitapecho_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Multitap Echo", + QT_TRANSLATE_NOOP( "pluginBrowser", "A multitap echo delay plugin" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + +} + + +MultitapEchoEffect::MultitapEchoEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : + Effect( &multitapecho_plugin_descriptor, parent, key ), + m_controls( this ), + m_buffer( 20000.0f ), + m_sampleRate( Engine::mixer()->processingSampleRate() ), + m_sampleRatio( 1.0f / m_sampleRate ) +{ + updateFilters( 0, 19 ); +} + + +MultitapEchoEffect::~MultitapEchoEffect() +{ +} + + +void MultitapEchoEffect::updateFilters( int begin, int end ) +{ + for( int i = begin; i <= end; ++i ) + { + m_filter[i][0].setFc( m_lpFreq[i] * m_sampleRatio ); + m_filter[i][1].setFc( m_lpFreq[i] * m_sampleRatio ); + } +} + + +void MultitapEchoEffect::runFilter( sampleFrame * dst, sampleFrame * src, StereoOnePole & filter, const fpp_t frames ) +{ + for( int f = 0; f < frames; ++f ) + { + dst[f][0] = filter[0].update( src[f][0] ); + dst[f][1] = filter[1].update( src[f][1] ); + } +} + + +bool MultitapEchoEffect::processAudioBuffer( sampleFrame * buf, const fpp_t frames ) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + + double outSum = 0.0; + const float d = dryLevel(); + const float w = wetLevel(); + + // get processing vars + const int steps = m_controls.m_steps.value(); + const float stepLength = m_controls.m_stepLength.value(); + const float dryGain = dbvToAmp( m_controls.m_dryGain.value() ); + const bool swapInputs = m_controls.m_swapInputs.value(); + + // temp processing stackbuffer for lp-filtering + sampleFrame work [frames]; + + // add dry buffer - never swap inputs for dry + m_buffer.writeAddingMultiplied( buf, 0, frames, dryGain ); + + // swapped inputs? + if( swapInputs ) + { + float offset = stepLength; + for( int i = 0; i < steps; ++i ) // add all steps swapped + { + runFilter( &work[0], buf, m_filter[i], frames ); + m_buffer.writeSwappedAddingMultiplied( &work[0], offset, frames, m_amp[i] ); + offset += stepLength; + } + } + else + { + float offset = stepLength; + for( int i = 0; i < steps; ++i ) // add all steps swapped + { + runFilter( &work[0], buf, m_filter[i], frames ); + m_buffer.writeAddingMultiplied( &work[0], offset, frames, m_amp[i] ); + offset += stepLength; + } + } + + // pop the buffer and mix it into output + m_buffer.pop( &work[0] ); + + for( int f = 0; f < frames; ++f ) + { + buf[f][0] = d * buf[f][0] + w * work[f][0]; + buf[f][1] = d * buf[f][1] + w * work[f][1]; + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + + checkGate( outSum / frames ); + + return isRunning(); +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new MultitapEchoEffect( parent, static_cast( data ) ); +} + +} diff --git a/plugins/MultitapEcho/MultitapEcho.h b/plugins/MultitapEcho/MultitapEcho.h new file mode 100644 index 000000000..c79f0b5e0 --- /dev/null +++ b/plugins/MultitapEcho/MultitapEcho.h @@ -0,0 +1,95 @@ +/* + * MultitapEcho.h - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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 MULTITAP_ECHO_H +#define MULTITAP_ECHO_H + +#include "Effect.h" +#include "MultitapEchoControls.h" +#include "ValueBuffer.h" +#include "RingBuffer.h" +#include "lmms_math.h" + +class OnePole +{ +public: + OnePole() + { + m_a0 = 1.0; + m_b1 = 0.0; + m_z1 = 0.0; + } + virtual ~OnePole() {} + + inline void setFc( float fc ) + { + m_b1 = expf( -2.0f * F_PI * fc ); + m_a0 = 1.0f - m_b1; + } + + inline float update( float s ) + { + return m_z1 = s * m_a0 + m_z1 * m_b1; + } + +private: + float m_a0, m_b1, m_z1; +}; + +typedef OnePole StereoOnePole [2]; + +class MultitapEchoEffect : public Effect +{ +public: + MultitapEchoEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); + virtual ~MultitapEchoEffect(); + virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ); + + virtual EffectControls* controls() + { + return &m_controls; + } + +private: + void updateFilters( int begin, int end ); + void runFilter( sampleFrame * dst, sampleFrame * src, StereoOnePole & filter, const fpp_t frames ); + + MultitapEchoControls m_controls; + + float m_amp [20]; + float m_lpFreq [20]; + + RingBuffer m_buffer; + StereoOnePole m_filter [20]; + + float m_sampleRate; + float m_sampleRatio; + + friend class MultitapEchoControls; + +}; + + +#endif diff --git a/plugins/MultitapEcho/MultitapEchoControlDialog.cpp b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp new file mode 100644 index 000000000..dbb6e448f --- /dev/null +++ b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp @@ -0,0 +1,99 @@ +/* + * MultitapEchoControlDialog.cpp - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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. + * + */ + +#include + +#include "MultitapEchoControlDialog.h" +#include "MultitapEchoControls.h" +#include "embed.h" +#include "Graph.h" +#include "PixmapButton.h" +#include "ToolTip.h" +#include "LedCheckbox.h" +#include "Knob.h" +#include "TempoSyncKnob.h" +#include "LcdSpinBox.h" + + +MultitapEchoControlDialog::MultitapEchoControlDialog( MultitapEchoControls * controls ) : + EffectControlDialog( controls ) +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 245, 300 ); + + // graph widgets + + Graph * ampGraph = new Graph( this, Graph::BarStyle, 204, 105 ); + Graph * lpGraph = new Graph( this, Graph::BarStyle, 204, 105 ); + + ampGraph->move( 30, 10 ); + lpGraph->move( 30, 125 ); + + ampGraph->setModel( & controls->m_ampGraph ); + lpGraph->setModel( & controls->m_lpGraph ); + + pal = QPalette(); + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap("graph_bg") ); + + ampGraph->setAutoFillBackground( true ); + ampGraph->setPalette( pal ); + ampGraph->setGraphColor( QColor( 48, 255, 117 ) ); + ampGraph -> setMaximumSize( 204, 105 ); + + lpGraph->setAutoFillBackground( true ); + lpGraph->setPalette( pal ); + lpGraph->setGraphColor( QColor( 48, 255, 117 ) ); + lpGraph -> setMaximumSize( 204, 105 ); + + // steps spinbox + + LcdSpinBox * steps = new LcdSpinBox( 2, this, "Steps" ); + steps->move( 20, 240 ); + steps->setModel( & controls->m_steps ); + + // knobs + + TempoSyncKnob * stepLength = new TempoSyncKnob( knobBright_26, this ); + stepLength->move( 80, 240 ); + stepLength->setModel( & controls->m_stepLength ); + stepLength->setLabel( tr( "Length" ) ); + stepLength->setHintText( tr( "Step length:" ) + " ", "ms" ); + + Knob * dryGain = new Knob( knobBright_26, this ); + dryGain->move( 130, 240 ); + dryGain->setModel( & controls->m_dryGain ); + dryGain->setLabel( tr( "Dry" ) ); + dryGain->setHintText( tr( "Dry Gain:" ) + " ", "dBV" ); + + // switch led + + LedCheckBox * swapInputs = new LedCheckBox( "Swap inputs", this, tr( "Swap in" ), LedCheckBox::Green ); + swapInputs->move( 180, 240 ); + swapInputs->setModel( & controls-> m_swapInputs ); + ToolTip::add( swapInputs, tr( "Swap left and right channel for reflections" ) ); +} diff --git a/plugins/MultitapEcho/MultitapEchoControlDialog.h b/plugins/MultitapEcho/MultitapEchoControlDialog.h new file mode 100644 index 000000000..c0fdf7c3c --- /dev/null +++ b/plugins/MultitapEcho/MultitapEchoControlDialog.h @@ -0,0 +1,44 @@ +/* + * MultitapEchoControlDialog.h - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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 MULTITAP_ECHO_CONTROL_DIALOG_H +#define MULTITAP_ECHO_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" + +class MultitapEchoControls; + +class MultitapEchoControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + MultitapEchoControlDialog( MultitapEchoControls * controls ); + virtual ~MultitapEchoControlDialog() + { + } +}; + +#endif diff --git a/plugins/MultitapEcho/MultitapEchoControls.cpp b/plugins/MultitapEcho/MultitapEchoControls.cpp new file mode 100644 index 000000000..1a0b446a5 --- /dev/null +++ b/plugins/MultitapEcho/MultitapEchoControls.cpp @@ -0,0 +1,168 @@ +/* + * MultitapEchoControls.cpp - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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. + * + */ + +#include + +#include "MultitapEchoControls.h" +#include "MultitapEcho.h" +#include "lmms_math.h" +#include "base64.h" + + +MultitapEchoControls::MultitapEchoControls( MultitapEchoEffect * eff ) : + EffectControls( eff ), + m_effect( eff ), + m_steps( 16, 4, 20, this, "Steps" ), + m_stepLength( 100.0f, 1.0f, 1000.0f, 0.1f, 1000.0f, this, "Step length" ), + m_dryGain( 0.0f, -80.0f, 20.0f, 0.1f, this, "Dry gain" ), + m_swapInputs( false, this, "Swap inputs" ), + m_ampGraph( -100.0f, 0.0f, 16, this ), + m_lpGraph( 0.0f, 3.0f, 16, this ) +{ + connect( &m_ampGraph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( ampSamplesChanged( int, int ) ) ); + connect( &m_lpGraph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( lpSamplesChanged( int, int ) ) ); + + connect( &m_steps, SIGNAL( dataChanged() ), this, SLOT( lengthChanged() ) ); + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); + + setDefaultAmpShape(); + setDefaultLpShape(); +} + + +MultitapEchoControls::~MultitapEchoControls() +{ +} + + +void MultitapEchoControls::saveSettings( QDomDocument & doc, QDomElement & parent ) +{ + m_steps.saveSettings( doc, parent, "steps" ); + m_stepLength.saveSettings( doc, parent, "steplength" ); + m_dryGain.saveSettings( doc, parent, "drygain" ); + m_swapInputs.saveSettings( doc, parent, "swapinputs" ); + + QString ampString; + base64::encode( (const char *) m_ampGraph.samples(), m_ampGraph.length() * sizeof(float), ampString ); + parent.setAttribute( "ampsteps", ampString ); + + QString lpString; + base64::encode( (const char *) m_lpGraph.samples(), m_lpGraph.length() * sizeof(float), lpString ); + parent.setAttribute( "lpsteps", lpString ); +} + + +void MultitapEchoControls::loadSettings( const QDomElement & elem ) +{ + m_steps.loadSettings( elem, "steps" ); + m_stepLength.loadSettings( elem, "steplength" ); + m_dryGain.loadSettings( elem, "drygain" ); + m_swapInputs.loadSettings( elem, "swapinputs" ); + + int size = 0; + char * dst = 0; + + base64::decode( elem.attribute( "ampsteps"), &dst, &size ); + m_ampGraph.setSamples( (float*) dst ); + + base64::decode( elem.attribute( "lpsteps"), &dst, &size ); + m_lpGraph.setSamples( (float*) dst ); + + delete[] dst; +} + + +void MultitapEchoControls::setDefaultAmpShape() +{ + const int length = m_steps.value(); + + float samples [length]; + for( int i = 0; i < length; ++i ) + { + samples[i] = 0.0f; + } + + m_ampGraph.setSamples( &samples[0] ); +} + + +void MultitapEchoControls::setDefaultLpShape() +{ + const int length = m_steps.value(); + + float samples [length]; + for( int i = 0; i < length; ++i ) + { + samples[i] = 3.0f; + } + + m_lpGraph.setSamples( &samples[0] ); +} + + +void MultitapEchoControls::ampSamplesChanged( int begin, int end ) +{ + const float * samples = m_ampGraph.samples(); + for( int i = begin; i <= end; ++i ) + { + m_effect->m_amp[i] = dbvToAmp( samples[i] ); + } +} + + +void MultitapEchoControls::ampResetClicked() +{ + setDefaultAmpShape(); +} + + +void MultitapEchoControls::lpSamplesChanged( int begin, int end ) +{ + const float * samples = m_lpGraph.samples(); + for( int i = begin; i <= end; ++i ) + { + m_effect->m_lpFreq[i] = 20.0f * exp10f( samples[i] ); + } + m_effect->updateFilters( begin, end ); +} + + +void MultitapEchoControls::lpResetClicked() +{ + setDefaultLpShape(); +} + + +void MultitapEchoControls::lengthChanged() +{ + m_ampGraph.setLength( m_steps.value() ); + m_lpGraph.setLength( m_steps.value() ); +} + + +void MultitapEchoControls::sampleRateChanged() +{ + m_effect->updateFilters( 0, 19 ); +} diff --git a/plugins/MultitapEcho/MultitapEchoControls.h b/plugins/MultitapEcho/MultitapEchoControls.h new file mode 100644 index 000000000..645e66ac4 --- /dev/null +++ b/plugins/MultitapEcho/MultitapEchoControls.h @@ -0,0 +1,90 @@ +/* + * MultitapEchoControls.h - a multitap echo delay plugin + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-2014 Tobias Doerffel + * + * 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 MULTITAP_ECHO_CONTROLS_H +#define MULTITAP_ECHO_CONTROLS_H + +#include "EffectControls.h" +#include "MultitapEchoControlDialog.h" +#include "Knob.h" +#include "Graph.h" + + +class MultitapEchoEffect; + +class MultitapEchoControls : public EffectControls +{ + Q_OBJECT +public: + MultitapEchoControls( MultitapEchoEffect * eff ); + virtual ~MultitapEchoControls(); + + virtual void saveSettings( QDomDocument & doc, QDomElement & parent ); + virtual void loadSettings( const QDomElement & elem ); + inline virtual QString nodeName() const + { + return( "multitapechocontrols" ); + } + + void setDefaultAmpShape(); + void setDefaultLpShape(); + + virtual int controlCount() + { + return( 4 ); + } + + virtual EffectControlDialog * createView() + { + return( new MultitapEchoControlDialog( this ) ); + } + +private slots: + void ampSamplesChanged( int, int ); + void ampResetClicked(); + + void lpSamplesChanged( int, int ); + void lpResetClicked(); + + void lengthChanged(); + void sampleRateChanged(); + +private: + MultitapEchoEffect * m_effect; + IntModel m_steps; + TempoSyncKnobModel m_stepLength; + + FloatModel m_dryGain; + BoolModel m_swapInputs; + + graphModel m_ampGraph; + graphModel m_lpGraph; + + friend class MultitapEchoEffect; + friend class MultitapEchoControlDialog; +}; + + +#endif diff --git a/plugins/MultitapEcho/artwork.png b/plugins/MultitapEcho/artwork.png new file mode 100644 index 000000000..e8a5d1e46 Binary files /dev/null and b/plugins/MultitapEcho/artwork.png differ diff --git a/plugins/MultitapEcho/graph_bg.png b/plugins/MultitapEcho/graph_bg.png new file mode 100644 index 000000000..078637d20 Binary files /dev/null and b/plugins/MultitapEcho/graph_bg.png differ diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index a6e43e05a..513dd40d9 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -124,6 +124,25 @@ void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, in } +struct AddSwappedMultipliedOp +{ + AddSwappedMultipliedOp( float coeff ) : m_coeff( coeff ) { } + + void operator()( sampleFrame& dst, const sampleFrame& src ) const + { + dst[0] += src[1] * m_coeff; + dst[1] += src[0] * m_coeff; + } + + const float m_coeff; +}; + +void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ) +{ + run<>( dst, src, frames, AddSwappedMultipliedOp(coeffSrc) ); +} + + void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ) { for( int f = 0; f < frames; ++f ) diff --git a/src/core/RingBuffer.cpp b/src/core/RingBuffer.cpp index 69456ab00..f81736bbe 100644 --- a/src/core/RingBuffer.cpp +++ b/src/core/RingBuffer.cpp @@ -272,6 +272,33 @@ void RingBuffer::writeAddingMultiplied( sampleFrame * src, float offset, f_cnt_t } +void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ) +{ + const f_cnt_t pos = ( m_position + offset ) % m_size; + if( length == 0 ) { length = m_fpp; } + + if( pos + length <= m_size ) // we won't go over the edge so we can just memcpy here + { + MixHelpers::addSwappedMultiplied( m_buffer + ( pos * sizeof( sampleFrame ) ), src, level, length ); + } + else + { + f_cnt_t first = m_size - pos; + f_cnt_t second = length - first; + + MixHelpers::addSwappedMultiplied( m_buffer + ( pos * sizeof( sampleFrame ) ), src, level, first ); + + MixHelpers::addSwappedMultiplied( m_buffer, src + ( first * sizeof( sampleFrame ) ), level, second ); + } +} + + +void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ) +{ + writeSwappedAddingMultiplied( src, msToFrames( offset ), length, level ); +} + + void RingBuffer::updateSamplerate() { float newsize = static_cast( ( m_size - m_fpp ) * Engine::mixer()->processingSampleRate() ) / m_samplerate; diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 009bf4b3b..2894e8283 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -444,6 +444,7 @@ graphModel::graphModel( float _min, float _max, int _length, ::Model * _parent, bool _default_constructed, float _step ) : Model( _parent, tr( "Graph" ), _default_constructed ), m_samples( _length ), + m_length( _length ), m_minValue( _min ), m_maxValue( _max ), m_step( _step ) @@ -482,9 +483,13 @@ void graphModel::setRange( float _min, float _max ) void graphModel::setLength( int _length ) { - if( _length != length() ) + if( _length != m_length ) { - m_samples.resize( _length ); + m_length = _length; + if( m_samples.size() < m_length ) + { + m_samples.resize( m_length ); + } emit lengthChanged(); } }