diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 943c8088c..0241c3bc3 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -66,6 +66,7 @@ IF("${PLUGIN_LIST}" STREQUAL "") patman peak_controller_effect GigPlayer + ReverbSC sfxr sid SpectrumAnalyzer diff --git a/plugins/ReverbSC/CMakeLists.txt b/plugins/ReverbSC/CMakeLists.txt new file mode 100644 index 000000000..b2481e523 --- /dev/null +++ b/plugins/ReverbSC/CMakeLists.txt @@ -0,0 +1,24 @@ +INCLUDE(BuildPlugin) + +# Enable C++11 for CXXFLAGS only +# This is needed since this plugin uses C. Otherwise Travis fails +REMOVE_DEFINITIONS(-std=c++0x) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + +BUILD_PLUGIN( + reverbsc + ReverbSC.cpp + ReverbSCControls.cpp + ReverbSCControlDialog.cpp + base.c + revsc.c + dcblock.c + MOCFILES + ReverbSC.h + ReverbSCControls.h + ReverbSCControlDialog.h + base.h + revsc.h + dcblock.h + EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" +) diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp new file mode 100644 index 000000000..93e526e87 --- /dev/null +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -0,0 +1,141 @@ +/* + * ReverbSC.cpp - A native reverb based on an algorithm by Sean Costello + * + * This file is part of LMMS - http://lmms.io + * + * 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 "ReverbSC.h" + +#include "embed.cpp" + +#define DB2LIN(X) pow(10, X / 20.0f); + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT reverbsc_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "ReverbSC", + QT_TRANSLATE_NOOP( "pluginBrowser", "Reverb algorithm by Sean Costello" ), + "Paul Batchelor", + 0x0123, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + +} + +ReverbSCEffect::ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : + Effect( &reverbsc_plugin_descriptor, parent, key ), + m_reverbSCControls( this ) +{ + sp_create(&sp); + sp->sr = Engine::mixer()->processingSampleRate(); + + sp_revsc_create(&revsc); + sp_revsc_init(sp, revsc); + + sp_dcblock_create(&dcblk[0]); + sp_dcblock_create(&dcblk[1]); + + sp_dcblock_init(sp, dcblk[0]); + sp_dcblock_init(sp, dcblk[1]); +} + +ReverbSCEffect::~ReverbSCEffect() +{ + sp_revsc_destroy(&revsc); + sp_dcblock_destroy(&dcblk[0]); + sp_dcblock_destroy(&dcblk[1]); + sp_destroy(&sp); +} + +bool ReverbSCEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + + double outSum = 0.0; + const float d = dryLevel(); + const float w = wetLevel(); + + SPFLOAT tmpL, tmpR; + SPFLOAT dcblkL, dcblkR; + + ValueBuffer * inGainBuf = m_reverbSCControls.m_inputGainModel.valueBuffer(); + ValueBuffer * sizeBuf = m_reverbSCControls.m_sizeModel.valueBuffer(); + ValueBuffer * colorBuf = m_reverbSCControls.m_colorModel.valueBuffer(); + ValueBuffer * outGainBuf = m_reverbSCControls.m_outputGainModel.valueBuffer(); + + for( fpp_t f = 0; f < frames; ++f ) + { + sample_t s[2] = { buf[f][0], buf[f][1] }; + + const SPFLOAT inGain = (SPFLOAT)DB2LIN((inGainBuf ? + inGainBuf->values()[f] + : m_reverbSCControls.m_inputGainModel.value())); + const SPFLOAT outGain = (SPFLOAT)DB2LIN((outGainBuf ? + outGainBuf->values()[f] + : m_reverbSCControls.m_outputGainModel.value())); + + s[0] *= inGain; + s[1] *= inGain; + revsc->feedback = (SPFLOAT)(sizeBuf ? + sizeBuf->values()[f] + : m_reverbSCControls.m_sizeModel.value()); + + revsc->lpfreq = (SPFLOAT)(colorBuf ? + colorBuf->values()[f] + : m_reverbSCControls.m_colorModel.value()); + + + sp_revsc_compute(sp, revsc, &s[0], &s[1], &tmpL, &tmpR); + sp_dcblock_compute(sp, dcblk[0], &tmpL, &dcblkL); + sp_dcblock_compute(sp, dcblk[1], &tmpR, &dcblkR); + buf[f][0] = d * buf[f][0] + w * dcblkL * outGain; + buf[f][1] = d * buf[f][1] + w * dcblkR * outGain; + + 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 ReverbSCEffect( + parent, + static_cast(data) + ); +} + +} diff --git a/plugins/ReverbSC/ReverbSC.h b/plugins/ReverbSC/ReverbSC.h new file mode 100644 index 000000000..926afb6c6 --- /dev/null +++ b/plugins/ReverbSC/ReverbSC.h @@ -0,0 +1,60 @@ +/* + * RerverbSC.h - Reverb algorithm by Sean Costello + * + * Copyright (c) 2017 Paul Batchelor + * + * This file is part of LMMS - http://lmms.io + * + * 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 REVERBSC_H +#define REVERBSC_H + +#include "Effect.h" +#include "ReverbSCControls.h" +#include "ValueBuffer.h" + +extern "C" { + #include "base.h" + #include "revsc.h" + #include "dcblock.h" +} + +class ReverbSCEffect : public Effect +{ +public: + ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); + virtual ~ReverbSCEffect(); + virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ); + + virtual EffectControls* controls() + { + return &m_reverbSCControls; + } + + +private: + ReverbSCControls m_reverbSCControls; + sp_data *sp; + sp_revsc *revsc; + sp_dcblock *dcblk[2]; + friend class ReverbSCControls; +} ; + +#endif diff --git a/plugins/ReverbSC/ReverbSCControlDialog.cpp b/plugins/ReverbSC/ReverbSCControlDialog.cpp new file mode 100644 index 000000000..e8c3c571a --- /dev/null +++ b/plugins/ReverbSC/ReverbSCControlDialog.cpp @@ -0,0 +1,63 @@ +/* + * ReverbSCControlDialog.cpp - control dialog for ReverbSC + * + * Copyright (c) 2017 Paul Batchelor + * + * This file is part of LMMS - http://lmms.io + * + * 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 "ReverbSCControlDialog.h" +#include "ReverbSCControls.h" +#include "embed.h" + +ReverbSCControlDialog::ReverbSCControlDialog( ReverbSCControls* controls ) : + EffectControlDialog( controls ) +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 185, 55 ); + + Knob * inputGainKnob = new Knob( knobBright_26, this); + inputGainKnob -> move( 16, 10 ); + inputGainKnob->setModel( &controls->m_inputGainModel ); + inputGainKnob->setLabel( tr( "Input" ) ); + inputGainKnob->setHintText( tr( "Input Gain:" ) , "dB" ); + + Knob * sizeKnob = new Knob( knobBright_26, this); + sizeKnob -> move( 57, 10 ); + sizeKnob->setModel( &controls->m_sizeModel ); + sizeKnob->setLabel( tr( "Size" ) ); + sizeKnob->setHintText( tr( "Size:" ) , "" ); + + Knob * colorKnob = new Knob( knobBright_26, this); + colorKnob -> move( 98, 10 ); + colorKnob->setModel( &controls->m_colorModel ); + colorKnob->setLabel( tr( "Color" ) ); + colorKnob->setHintText( tr( "Color:" ) , "" ); + + Knob * outputGainKnob = new Knob( knobBright_26, this); + outputGainKnob -> move( 139, 10 ); + outputGainKnob->setModel( &controls->m_outputGainModel ); + outputGainKnob->setLabel( tr( "Output" ) ); + outputGainKnob->setHintText( tr( "Output Gain:" ) , "dB" ); +} diff --git a/plugins/ReverbSC/ReverbSCControlDialog.h b/plugins/ReverbSC/ReverbSCControlDialog.h new file mode 100644 index 000000000..9f262a88f --- /dev/null +++ b/plugins/ReverbSC/ReverbSCControlDialog.h @@ -0,0 +1,45 @@ +/* + * ReverbSCControlDialog.h - control dialog for ReverbSC + * + * Copyright (c) 2017 Paul Batchelor + * + * This file is part of LMMS - http://lmms.io + * + * 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 REVERBSC_CONTROL_DIALOG_H +#define REVERBSC_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" + + +class ReverbSCControls; + + +class ReverbSCControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + ReverbSCControlDialog( ReverbSCControls* controls ); + virtual ~ReverbSCControlDialog() + { + } + +} ; + +#endif diff --git a/plugins/ReverbSC/ReverbSCControls.cpp b/plugins/ReverbSC/ReverbSCControls.cpp new file mode 100644 index 000000000..59fe6ccdd --- /dev/null +++ b/plugins/ReverbSC/ReverbSCControls.cpp @@ -0,0 +1,62 @@ +/* + * ReverbSCControls.cpp - controls for ReverbSC + * + * Copyright (c) 2017 Paul Batchelor + * + * This file is part of LMMS - http://lmms.io + * + * 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 "ReverbSCControls.h" +#include "ReverbSC.h" +#include "Engine.h" +#include "Song.h" + +ReverbSCControls::ReverbSCControls( ReverbSCEffect* effect ) : + EffectControls( effect ), + m_effect( effect ), + m_inputGainModel( 0.0f, -60.0f, 15, 0.1f, this, tr( "Input Gain" ) ), + m_sizeModel( 0.89f, 0.0f, 1.0f, 0.01f, this, tr( "Size" ) ), + m_colorModel( 10000.0f, 100.0f, 15000.0f, 0.1f, this, tr( "Color" ) ), + m_outputGainModel( 0.0f, -60.0f, 15, 0.1f, this, tr( "Output Gain" ) ) +{ +} + +void ReverbSCControls::changeControl() +{ +} + +void ReverbSCControls::loadSettings( const QDomElement& _this ) +{ + m_inputGainModel.loadSettings( _this, "input_gain" ); + m_sizeModel.loadSettings( _this, "size" ); + m_colorModel.loadSettings( _this, "color" ); + m_outputGainModel.loadSettings( _this, "output_gain" ); +} + +void ReverbSCControls::saveSettings( QDomDocument& doc, QDomElement& _this ) +{ + m_inputGainModel.saveSettings( doc, _this, "input_gain" ); + m_sizeModel.saveSettings( doc, _this, "size" ); + m_colorModel.saveSettings( doc, _this, "color" ); + m_outputGainModel.saveSettings( doc, _this, "output_gain" ); +} + diff --git a/plugins/ReverbSC/ReverbSCControls.h b/plugins/ReverbSC/ReverbSCControls.h new file mode 100644 index 000000000..d75a79db4 --- /dev/null +++ b/plugins/ReverbSC/ReverbSCControls.h @@ -0,0 +1,77 @@ +/* + * ReverbSCControls.h + * + * Copyright (c) 2017 Paul Batchelor + * + * This file is part of LMMS - http://lmms.io + * + * 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 REVERBSC_CONTROLS_H +#define REVERBSC_CONTROLS_H + +#include "EffectControls.h" +#include "ReverbSCControlDialog.h" +#include "Knob.h" + + +class ReverbSCEffect; + +class ReverbSCControls : public EffectControls +{ + Q_OBJECT +public: + ReverbSCControls( ReverbSCEffect* effect ); + virtual ~ReverbSCControls() + { + } + + virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); + virtual void loadSettings( const QDomElement & _this ); + inline virtual QString nodeName() const + { + return "ReverbSCControls"; + } + + virtual int controlCount() + { + return 4; + } + + virtual EffectControlDialog* createView() + { + return new ReverbSCControlDialog( this ); + } + + +private slots: + void changeControl(); + +private: + ReverbSCEffect* m_effect; + FloatModel m_inputGainModel; + FloatModel m_sizeModel; + FloatModel m_colorModel; + FloatModel m_outputGainModel; + + friend class ReverbSCControlDialog; + friend class ReverbSCEffect; + +} ; + +#endif diff --git a/plugins/ReverbSC/artwork.png b/plugins/ReverbSC/artwork.png new file mode 100644 index 000000000..6fe5168fa Binary files /dev/null and b/plugins/ReverbSC/artwork.png differ diff --git a/plugins/ReverbSC/base.c b/plugins/ReverbSC/base.c new file mode 100644 index 000000000..c8d1dff67 --- /dev/null +++ b/plugins/ReverbSC/base.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include "base.h" + +int sp_create(sp_data **spp) +{ + *spp = (sp_data *) malloc(sizeof(sp_data)); + sp_data *sp = *spp; + sprintf(sp->filename, "test.wav"); + sp->nchan = 1; + SPFLOAT *out = malloc(sizeof(SPFLOAT) * sp->nchan); + *out = 0; + sp->out = out; + sp->sr = 44100; + sp->len = 5 * sp->sr; + sp->pos = 0; + sp->rand = 0; + return 0; +} + +int sp_createn(sp_data **spp, int nchan) +{ + *spp = (sp_data *) malloc(sizeof(sp_data)); + sp_data *sp = *spp; + sprintf(sp->filename, "test.wav"); + sp->nchan = nchan; + SPFLOAT *out = malloc(sizeof(SPFLOAT) * sp->nchan); + *out = 0; + sp->out = out; + sp->sr = 44100; + sp->len = 5 * sp->sr; + sp->pos = 0; + sp->rand = 0; + return 0; +} + +int sp_destroy(sp_data **spp) +{ + sp_data *sp = *spp; + free(sp->out); + free(*spp); + return 0; +} + +#ifndef NO_LIBSNDFILE + +int sp_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)) +{ + SNDFILE *sf[sp->nchan]; + char tmp[140]; + SF_INFO info; + memset(&info, 0, sizeof(SF_INFO)); + SPFLOAT buf[sp->nchan][SP_BUFSIZE]; + info.samplerate = sp->sr; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24; + int numsamps, i, chan; + if(sp->nchan == 1) { + sf[0] = sf_open(sp->filename, SFM_WRITE, &info); + } else { + for(chan = 0; chan < sp->nchan; chan++) { + sprintf(tmp, "%02d_%s", chan, sp->filename); + sf[chan] = sf_open(tmp, SFM_WRITE, &info); + } + } + + while(sp->len > 0){ + if(sp->len < SP_BUFSIZE) { + numsamps = sp->len; + }else{ + numsamps = SP_BUFSIZE; + } + for(i = 0; i < numsamps; i++){ + callback(sp, ud); + for(chan = 0; chan < sp->nchan; chan++) { + buf[chan][i] = sp->out[chan]; + } + sp->pos++; + } + for(chan = 0; chan < sp->nchan; chan++) { +#ifdef USE_DOUBLE + sf_write_double(sf[chan], buf[chan], numsamps); +#else + sf_write_float(sf[chan], buf[chan], numsamps); +#endif + } + sp->len -= numsamps; + } + for(i = 0; i < sp->nchan; i++) { + sf_close(sf[i]); + } + return 0; +} + +#endif + +int sp_process_raw(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)) +{ + int chan; + if(sp->len == 0) { + while(1) { + callback(sp, ud); + for (chan = 0; chan < sp->nchan; chan++) { + fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout); + } + sp->len--; + } + } else { + while(sp->len > 0) { + callback(sp, ud); + for (chan = 0; chan < sp->nchan; chan++) { + fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout); + } + sp->len--; + sp->pos++; + } + } + return SP_OK; +} + +#ifdef USE_SPA +int sp_process_spa(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)) +{ + sp_audio spa; + if(spa_open(sp, &spa, sp->filename, SPA_WRITE) == SP_NOT_OK) { + fprintf(stderr, "Error: could not open file %s.\n", sp->filename); + } + while(sp->len > 0) { + callback(sp, ud); + spa_write_buf(sp, &spa, sp->out, sp->nchan); + sp->len--; + sp->pos++; + } + spa_close(&spa); + return SP_OK; +} +#endif + +int sp_process_plot(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)) +{ + int chan; + fprintf(stdout, "sp_out = [ ... \n"); + while(sp->len > 0) { + callback(sp, ud); + for (chan = 0; chan < sp->nchan; chan++) { + /* fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout); */ + fprintf(stdout, "%g ", sp->out[chan]); + } + fprintf(stdout, "; ...\n"); + sp->len--; + sp->pos++; + } + fprintf(stdout, "];\n"); + + fprintf(stdout, "plot(sp_out);\n"); + fprintf(stdout, "title('Plot generated by Soundpipe');\n"); + fprintf(stdout, "xlabel('Time (samples)');\n"); + fprintf(stdout, "ylabel('Amplitude');\n"); + return SP_OK; +} + +int sp_auxdata_alloc(sp_auxdata *aux, size_t size) +{ + aux->ptr = malloc(size); + aux->size = size; + memset(aux->ptr, 0, size); + return SP_OK; +} + +int sp_auxdata_free(sp_auxdata *aux) +{ + free(aux->ptr); + return SP_OK; +} + + +SPFLOAT sp_midi2cps(SPFLOAT nn) +{ + return pow(2, (nn - 69.0) / 12.0) * 440.0; +} + +int sp_set(sp_param *p, SPFLOAT val) { + p->state = 1; + p->val = val; + return SP_OK; +} + +int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val) +{ + if(chan > sp->nchan - 1) { + fprintf(stderr, "sp_out: Invalid channel\n"); + return SP_NOT_OK; + } + sp->out[chan] = val; + return SP_OK; +} + +/* +uint32_t sp_rand(sp_data *sp) +{ + uint32_t val = (1103515245 * sp->rand + 12345) % SP_RANDMAX; + sp->rand = val; + return val; +} +*/ + +void sp_srand(sp_data *sp, uint32_t val) +{ + sp->rand = val; +} + + diff --git a/plugins/ReverbSC/base.h b/plugins/ReverbSC/base.h new file mode 100644 index 000000000..6edf5a394 --- /dev/null +++ b/plugins/ReverbSC/base.h @@ -0,0 +1,84 @@ +#include +#include + +#define NO_LIBSNDFILE + +#ifndef NO_LIBSNDFILE +#include "sndfile.h" +#endif + +#define SP_BUFSIZE 4096 +#ifndef SPFLOAT +#define SPFLOAT float +#endif +#define SP_OK 1 +#define SP_NOT_OK 0 + +#define SP_RANDMAX 2147483648 + +typedef unsigned long sp_frame; + +typedef struct sp_auxdata { + size_t size; + void *ptr; +} sp_auxdata; + +typedef struct sp_data { + SPFLOAT *out; + int sr; + int nchan; + unsigned long len; + unsigned long pos; + char filename[200]; + uint32_t rand; +} sp_data; + +typedef struct { + char state; + SPFLOAT val; +} sp_param; + +int sp_auxdata_alloc(sp_auxdata *aux, size_t size); +int sp_auxdata_free(sp_auxdata *aux); + +int sp_create(sp_data **spp); +int sp_createn(sp_data **spp, int nchan); + +int sp_destroy(sp_data **spp); +int sp_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)); +int sp_process_raw(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)); +int sp_process_plot(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)); +int sp_process_spa(sp_data *sp, void *ud, void (*callback)(sp_data *, void *)); + +SPFLOAT sp_midi2cps(SPFLOAT nn); + +int sp_set(sp_param *p, SPFLOAT val); + +int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val); + +uint32_t sp_rand(sp_data *sp); +void sp_srand(sp_data *sp, uint32_t val); + + +typedef struct { + SPFLOAT *utbl; + int16_t *BRLow; + int16_t *BRLowCpx; +} sp_fft; + +void sp_fft_create(sp_fft **fft); +void sp_fft_init(sp_fft *fft, int M); +void sp_fftr(sp_fft *fft, SPFLOAT *buf, int FFTsize); +void sp_fft_cpx(sp_fft *fft, SPFLOAT *buf, int FFTsize); +void sp_ifftr(sp_fft *fft, SPFLOAT *buf, int FFTsize); +void sp_fft_destroy(sp_fft *fft); +#ifndef kiss_fft_scalar +#define kiss_fft_scalar SPFLOAT +#endif +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; +typedef struct kiss_fftr_state* kiss_fftr_cfg; diff --git a/plugins/ReverbSC/dcblock.c b/plugins/ReverbSC/dcblock.c new file mode 100644 index 000000000..493a9ae92 --- /dev/null +++ b/plugins/ReverbSC/dcblock.c @@ -0,0 +1,52 @@ +/* + * DCblock + * + * This code has been extracted from the Csound opcode "dcblock". + * It has been modified to work as a Soundpipe module. + * + * Original Author(s): Perry R. Cook + * Year: 1995 + * Location: Opcodes/biquad.c + * + */ + +#include +#include "base.h" +#include "dcblock.h" + +int sp_dcblock_create(sp_dcblock **p) +{ + *p = malloc(sizeof(sp_dcblock)); + return SP_OK; +} + +int sp_dcblock_destroy(sp_dcblock **p) +{ + free(*p); + return SP_OK; +} + +int sp_dcblock_init(sp_data *sp, sp_dcblock *p) +{ + p->outputs = 0.0; + p->inputs = 0.0; + p->gain = 0.99; + if (p->gain == 0.0 || p->gain>=1.0 || p->gain<=-1.0) + p->gain = 0.99; + return SP_OK; +} + +int sp_dcblock_compute(sp_data *sp, sp_dcblock *p, SPFLOAT *in, SPFLOAT *out) +{ + SPFLOAT gain = p->gain; + SPFLOAT outputs = p->outputs; + SPFLOAT inputs = p->inputs; + + SPFLOAT sample = *in; + outputs = sample - inputs + (gain * outputs); + inputs = sample; + *out = outputs; + p->outputs = outputs; + p->inputs = inputs; + return SP_OK; +} diff --git a/plugins/ReverbSC/dcblock.h b/plugins/ReverbSC/dcblock.h new file mode 100644 index 000000000..6584c400a --- /dev/null +++ b/plugins/ReverbSC/dcblock.h @@ -0,0 +1,11 @@ +typedef struct { + SPFLOAT gg; + SPFLOAT outputs; + SPFLOAT inputs; + SPFLOAT gain; +} sp_dcblock; + +int sp_dcblock_create(sp_dcblock **p); +int sp_dcblock_destroy(sp_dcblock **p); +int sp_dcblock_init(sp_data *sp, sp_dcblock *p); +int sp_dcblock_compute(sp_data *sp, sp_dcblock *p, SPFLOAT *in, SPFLOAT *out); diff --git a/plugins/ReverbSC/logo.png b/plugins/ReverbSC/logo.png new file mode 100644 index 000000000..9340da708 Binary files /dev/null and b/plugins/ReverbSC/logo.png differ diff --git a/plugins/ReverbSC/revsc.c b/plugins/ReverbSC/revsc.c new file mode 100644 index 000000000..35084c175 --- /dev/null +++ b/plugins/ReverbSC/revsc.c @@ -0,0 +1,276 @@ +/* + * RevSC + * + * This code has been extracted from the Csound opcode "reverbsc". + * It has been modified to work as a Soundpipe module. + * + * Original Author(s): Sean Costello, Istvan Varga + * Year: 1999, 2005 + * Location: Opcodes/reverbsc.c + * + */ +#include +#include +#include +#include +#include "base.h" +#include "revsc.h" + +#define DEFAULT_SRATE 44100.0 +#define MIN_SRATE 5000.0 +#define MAX_SRATE 1000000.0 +#define MAX_PITCHMOD 20.0 +#define DELAYPOS_SHIFT 28 +#define DELAYPOS_SCALE 0x10000000 +#define DELAYPOS_MASK 0x0FFFFFFF + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + +/* reverbParams[n][0] = delay time (in seconds) */ +/* reverbParams[n][1] = random variation in delay time (in seconds) */ +/* reverbParams[n][2] = random variation frequency (in 1/sec) */ +/* reverbParams[n][3] = random seed (0 - 32767) */ + +static const SPFLOAT reverbParams[8][4] = { + { (2473.0 / DEFAULT_SRATE), 0.0010, 3.100, 1966.0 }, + { (2767.0 / DEFAULT_SRATE), 0.0011, 3.500, 29491.0 }, + { (3217.0 / DEFAULT_SRATE), 0.0017, 1.110, 22937.0 }, + { (3557.0 / DEFAULT_SRATE), 0.0006, 3.973, 9830.0 }, + { (3907.0 / DEFAULT_SRATE), 0.0010, 2.341, 20643.0 }, + { (4127.0 / DEFAULT_SRATE), 0.0011, 1.897, 22937.0 }, + { (2143.0 / DEFAULT_SRATE), 0.0017, 0.891, 29491.0 }, + { (1933.0 / DEFAULT_SRATE), 0.0006, 3.221, 14417.0 } +}; + +static int delay_line_max_samples(SPFLOAT sr, SPFLOAT iPitchMod, int n); +static int init_delay_line(sp_revsc *p, sp_revsc_dl *lp, int n); +static int delay_line_bytes_alloc(SPFLOAT sr, SPFLOAT iPitchMod, int n); +static const SPFLOAT outputGain = 0.35; +static const SPFLOAT jpScale = 0.25; +int sp_revsc_create(sp_revsc **p){ + *p = malloc(sizeof(sp_revsc)); + return SP_OK; +} + +int sp_revsc_init(sp_data *sp, sp_revsc *p) +{ + p->iSampleRate = sp->sr; + p->sampleRate = sp->sr; + p->feedback = 0.97; + p->lpfreq = 10000; + p->iPitchMod = 1; + p->iSkipInit = 0; + p->dampFact = 1.0; + p->prv_LPFreq = 0.0; + p->initDone = 1; + int i, nBytes = 0; + for(i = 0; i < 8; i++){ + nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + } + sp_auxdata_alloc(&p->aux, nBytes); + nBytes = 0; + for (i = 0; i < 8; i++) { + p->delayLines[i].buf = (p->aux.ptr) + nBytes; + init_delay_line(p, &p->delayLines[i], i); + nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + } + + return SP_OK; +} + + +int sp_revsc_destroy(sp_revsc **p) +{ + sp_revsc *pp = *p; + sp_auxdata_free(&pp->aux); + free(*p); + return SP_OK; +} + +static int delay_line_max_samples(SPFLOAT sr, SPFLOAT iPitchMod, int n) +{ + SPFLOAT maxDel; + + maxDel = reverbParams[n][0]; + maxDel += (reverbParams[n][1] * (SPFLOAT) iPitchMod * 1.125); + return (int) (maxDel * sr + 16.5); +} + +static int delay_line_bytes_alloc(SPFLOAT sr, SPFLOAT iPitchMod, int n) +{ + int nBytes = 0; + + nBytes += (delay_line_max_samples(sr, iPitchMod, n) * (int) sizeof(SPFLOAT)); + return nBytes; +} + +static void next_random_lineseg(sp_revsc *p, sp_revsc_dl *lp, int n) +{ + SPFLOAT prvDel, nxtDel, phs_incVal; + + /* update random seed */ + if (lp->seedVal < 0) + lp->seedVal += 0x10000; + lp->seedVal = (lp->seedVal * 15625 + 1) & 0xFFFF; + if (lp->seedVal >= 0x8000) + lp->seedVal -= 0x10000; + /* length of next segment in samples */ + lp->randLine_cnt = (int) ((p->sampleRate / reverbParams[n][2]) + 0.5); + prvDel = (SPFLOAT) lp->writePos; + prvDel -= ((SPFLOAT) lp->readPos + + ((SPFLOAT) lp->readPosFrac / (SPFLOAT) DELAYPOS_SCALE)); + while (prvDel < 0.0) + prvDel += lp->bufferSize; + prvDel = prvDel / p->sampleRate; /* previous delay time in seconds */ + nxtDel = (SPFLOAT) lp->seedVal * reverbParams[n][1] / 32768.0; + /* next delay time in seconds */ + nxtDel = reverbParams[n][0] + (nxtDel * (SPFLOAT) p->iPitchMod); + /* calculate phase increment per sample */ + phs_incVal = (prvDel - nxtDel) / (SPFLOAT) lp->randLine_cnt; + phs_incVal = phs_incVal * p->sampleRate + 1.0; + lp->readPosFrac_inc = (int) (phs_incVal * DELAYPOS_SCALE + 0.5); +} + +static int init_delay_line(sp_revsc *p, sp_revsc_dl *lp, int n) +{ + SPFLOAT readPos; + /* int i; */ + + /* calculate length of delay line */ + lp->bufferSize = delay_line_max_samples(p->sampleRate, 1, n); + lp->dummy = 0; + lp->writePos = 0; + /* set random seed */ + lp->seedVal = (int) (reverbParams[n][3] + 0.5); + /* set initial delay time */ + readPos = (SPFLOAT) lp->seedVal * reverbParams[n][1] / 32768; + readPos = reverbParams[n][0] + (readPos * (SPFLOAT) p->iPitchMod); + readPos = (SPFLOAT) lp->bufferSize - (readPos * p->sampleRate); + lp->readPos = (int) readPos; + readPos = (readPos - (SPFLOAT) lp->readPos) * (SPFLOAT) DELAYPOS_SCALE; + lp->readPosFrac = (int) (readPos + 0.5); + /* initialise first random line segment */ + next_random_lineseg(p, lp, n); + /* clear delay line to zero */ + lp->filterState = 0.0; + memset(lp->buf, 0, sizeof(SPFLOAT) * lp->bufferSize); + return SP_OK; +} + + +int sp_revsc_compute(sp_data *sp, sp_revsc *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2) +{ + SPFLOAT ainL, ainR, aoutL, aoutR; + SPFLOAT vm1, v0, v1, v2, am1, a0, a1, a2, frac; + sp_revsc_dl *lp; + int readPos; + uint32_t n; + int bufferSize; /* Local copy */ + SPFLOAT dampFact = p->dampFact; + + if (p->initDone <= 0) return SP_NOT_OK; + + /* calculate tone filter coefficient if frequency changed */ + + if (p->lpfreq != p->prv_LPFreq) { + p->prv_LPFreq = p->lpfreq; + dampFact = 2.0 - cos(p->prv_LPFreq * (2 * M_PI) / p->sampleRate); + dampFact = p->dampFact = dampFact - sqrt(dampFact * dampFact - 1.0); + } + + /* calculate "resultant junction pressure" and mix to input signals */ + + ainL = aoutL = aoutR = 0.0; + for (n = 0; n < 8; n++) { + ainL += p->delayLines[n].filterState; + } + ainL *= jpScale; + ainR = ainL + *in2; + ainL = ainL + *in1; + + /* loop through all delay lines */ + + for (n = 0; n < 8; n++) { + lp = &p->delayLines[n]; + bufferSize = lp->bufferSize; + + /* send input signal and feedback to delay line */ + + lp->buf[lp->writePos] = (SPFLOAT) ((n & 1 ? ainR : ainL) + - lp->filterState); + if (++lp->writePos >= bufferSize) { + lp->writePos -= bufferSize; + } + + /* read from delay line with cubic interpolation */ + + if (lp->readPosFrac >= DELAYPOS_SCALE) { + lp->readPos += (lp->readPosFrac >> DELAYPOS_SHIFT); + lp->readPosFrac &= DELAYPOS_MASK; + } + if (lp->readPos >= bufferSize) + lp->readPos -= bufferSize; + readPos = lp->readPos; + frac = (SPFLOAT) lp->readPosFrac * (1.0 / (SPFLOAT) DELAYPOS_SCALE); + + /* calculate interpolation coefficients */ + + a2 = frac * frac; a2 -= 1.0; a2 *= (1.0 / 6.0); + a1 = frac; a1 += 1.0; a1 *= 0.5; am1 = a1 - 1.0; + a0 = 3.0 * a2; a1 -= a0; am1 -= a2; a0 -= frac; + + /* read four samples for interpolation */ + + if (readPos > 0 && readPos < (bufferSize - 2)) { + vm1 = (SPFLOAT) (lp->buf[readPos - 1]); + v0 = (SPFLOAT) (lp->buf[readPos]); + v1 = (SPFLOAT) (lp->buf[readPos + 1]); + v2 = (SPFLOAT) (lp->buf[readPos + 2]); + } + else { + + /* at buffer wrap-around, need to check index */ + + if (--readPos < 0) readPos += bufferSize; + vm1 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v0 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v1 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v2 = (SPFLOAT) lp->buf[readPos]; + } + v0 = (am1 * vm1 + a0 * v0 + a1 * v1 + a2 * v2) * frac + v0; + + /* update buffer read position */ + + lp->readPosFrac += lp->readPosFrac_inc; + + /* apply feedback gain and lowpass filter */ + + v0 *= (SPFLOAT) p->feedback; + v0 = (lp->filterState - v0) * dampFact + v0; + lp->filterState = v0; + + /* mix to output */ + + if (n & 1) { + aoutR += v0; + }else{ + aoutL += v0; + } + + /* start next random line segment if current one has reached endpoint */ + + if (--(lp->randLine_cnt) <= 0) { + next_random_lineseg(p, lp, n); + } + } + /* someday, use aoutR for multimono out */ + + *out1 = aoutL * outputGain; + *out2 = aoutR * outputGain; + return SP_OK; +} diff --git a/plugins/ReverbSC/revsc.h b/plugins/ReverbSC/revsc.h new file mode 100644 index 000000000..7bf07e7bf --- /dev/null +++ b/plugins/ReverbSC/revsc.h @@ -0,0 +1,28 @@ +typedef struct { + int writePos; + int bufferSize; + int readPos; + int readPosFrac; + int readPosFrac_inc; + int dummy; + int seedVal; + int randLine_cnt; + SPFLOAT filterState; + SPFLOAT *buf; +} sp_revsc_dl; + +typedef struct { + SPFLOAT feedback, lpfreq; + SPFLOAT iSampleRate, iPitchMod, iSkipInit; + SPFLOAT sampleRate; + SPFLOAT dampFact; + SPFLOAT prv_LPFreq; + int initDone; + sp_revsc_dl delayLines[8]; + sp_auxdata aux; +} sp_revsc; + +int sp_revsc_create(sp_revsc **p); +int sp_revsc_destroy(sp_revsc **p); +int sp_revsc_init(sp_data *sp, sp_revsc *p); +int sp_revsc_compute(sp_data *sp, sp_revsc *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2);