diff --git a/ChangeLog b/ChangeLog index dd6824562..5e94259b3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2007-03-25 Javier Serrano Polo + + * plugins/polyb302/artwork.png: + * plugins/polyb302/logo.png: + * plugins/polyb302/Makefile.am: + * plugins/polyb302/polyb302.cpp: + * plugins/polyb302/polyb302.h: + initial release, polyphonic version of lb302 plugin + + * include/instrument_track.h: + * include/note_play_handle.h: + * src/core/note_play_handle.cpp: + * src/tracks/instrument_track.cpp: + trigger detuning without signals + + * configure.in: + fixed singerbot error + + * include/knob.h: + arranged private/protected members + + * data/locale/ca.ts: + updated translation + 2007-03-07 Javier Serrano Polo * plugins/singerbot/singerbot.cpp: diff --git a/configure.in b/configure.in index 11142e60d..bb2375eab 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.2.1-svn20070307, lmms-devel/at/lists/dot/sf/dot/net) -AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070307) +AC_INIT(lmms, 0.2.1-svn20070325, lmms-devel/at/lists/dot/sf/dot/net) +AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070325) AM_CONFIG_HEADER(config.h) @@ -359,9 +359,9 @@ if test ! -z "$HAVE_SAMPLERATE_H" ; then if test ! -z "$FESTIVAL_SUPPORT" ; then AC_DEFINE(SINGERBOT_SUPPORT) fi - AM_CONDITIONAL(SINGERBOT_SUPPORT, test ! -z "$FESTIVAL_SUPPORT" ) AC_LANG_POP(C++) fi +AM_CONDITIONAL(SINGERBOT_SUPPORT, test ! -z "$FESTIVAL_SUPPORT" ) # libsndfile-stuff @@ -590,6 +590,7 @@ AC_CONFIG_FILES([Makefile plugins/live_tool/Makefile plugins/midi_import/Makefile plugins/organic/Makefile + plugins/polyb302/Makefile plugins/plucked_string_synth/Makefile plugins/singerbot/Makefile plugins/stk/Makefile diff --git a/data/locale/ca.qm b/data/locale/ca.qm index aa307327c..a14147c3e 100644 Binary files a/data/locale/ca.qm and b/data/locale/ca.qm differ diff --git a/data/locale/ca.ts b/data/locale/ca.ts index 61e1d284d..4ca0a6238 100644 --- a/data/locale/ca.ts +++ b/data/locale/ca.ts @@ -407,7 +407,7 @@ http://lmms.sourceforge.net Use this knob for setting the arpeggio time in milliseconds. The arpeggio time specifies how long each arpeggio-tone should be played. - Usa aquesta roda per a ajustar el temps d'arpegi en milisegons. El temps d'arpegi especifica la durada de cada nota de l'arpegi. + Usa aquesta roda per a ajustar el temps d'arpegi en mil·lisegons. El temps d'arpegi especifica la durada de cada nota de l'arpegi. Arpeggio gate: @@ -738,7 +738,7 @@ http://lmms.sourceforge.net If you click here, selected values will be cut into the clipboard. You can paste them anywhere in any pattern by clicking on the paste-button. - Si piques aqui, els valors seleccionats seran tallats cap al portapapers. Pots enganxar-los a qualsevol banda de qualsevol patró picant el botó enganxar. + Si piques aquí, els valors seleccionats seran tallats cap al portapapers. Pots enganxar-los a qualsevol banda de qualsevol patró picant el botó enganxar. If you click here, selected values will be copied into the clipboard. You can paste them anywhere in any pattern by clicking on the paste-button. @@ -746,7 +746,7 @@ http://lmms.sourceforge.net If you click here, the values from the clipboard will be pasted at the first visible tact. - Si piques aqui, els valors del portapapers seran enganxats al primer compàs visible. + Si piques aquí, els valors del portapapers seran enganxats al primer compàs visible. Automation Editor - no pattern @@ -790,7 +790,7 @@ http://lmms.sourceforge.net If you click here, erase-mode will be activated. In this mode you can erase single values. You can also press 'Shift+E' on your keyboard to activate this mode. - Si piques aqui, s'activarà el mode esborrar. En aquest mode pots esborrar valors solts. També pots pitjar 'Maj+E' al teclat per a activar aquest mode. + Si piques aquí, s'activarà el mode esborrar. En aquest mode pots esborrar valors solts. També pots pitjar 'Maj+E' al teclat per a activar aquest mode. If you click here, select-mode will be activated. In this mode you can select values. This is neccessary if you want to cut, copy, paste, delete or move values. You can also press 'Shift+S' on your keyboard to activate this mode. @@ -798,7 +798,7 @@ http://lmms.sourceforge.net If you click here, move-mode will be activated. In this mode you can move the values you selected in select-mode. You can also press 'Shift+M' on your keyboard to activate this mode. - Si piques aqui, el mode moure serà activat. En aquest mode pots moure els valors que has seleccionat en el mode seleccionar. També pots pitjar 'Maj+M' al teclat per a activar aquest mode. + Si piques aquí, el mode moure serà activat. En aquest mode pots moure els valors que has seleccionat en el mode seleccionar. També pots pitjar 'Maj+M' al teclat per a activar aquest mode. @@ -1038,7 +1038,7 @@ Es mostrarà l'assistent per a reconfigurar LMMS. < &Back - < &Enrera + < &Enrere &Finish @@ -1383,15 +1383,15 @@ directori de treball de LMMS. Executar ara l'assistent? Q/Resonance: - Q/Resonància: + Q/Ressonància: Use this knob for setting Q/Resonance for the selected filter. Q/Resonance tells the filter, how much it should amplify frequencies near Cutoff-frequency. - Usa aquesta roda per a ajustar la Q/Resonància per al filtre seleccionat. Q/Resonància diu al filtre quant ha d'amplificar les freqüències a prop de la freqüència de tall. + Usa aquesta roda per a ajustar la Q/Ressonància per al filtre seleccionat. Q/Ressonància diu al filtre quant ha d'amplificar les freqüències a prop de la freqüència de tall. Q/Resonance - Q/Resonància + Q/Ressonància 2x LowPass @@ -1419,7 +1419,7 @@ directori de treball de LMMS. Executar ara l'assistent? Q/RESO - Q/RESO + Q/RESSO FILTER @@ -1972,11 +1972,11 @@ Fent doble clic a qualsevol connector mostrarà informació sobre els ports. VCF Resonance - Resonància VCF + Ressonància VCF Resonance: - Resonància: + Ressonància: RES @@ -2311,7 +2311,7 @@ Les tecles F1-F10 apaguen els 10 primers instruments a l'Editor de Ritme Ba Currently there's no help available in LMMS. Please visit http://wiki.mindrules.net for documentation on LMMS. - Actualment no hi ha ajuda diponible a LMMS. + Actualment no hi ha ajuda disponible a LMMS. Per favor, visita http://wiki.mindrules.net per a documentació sobre LMMS. @@ -2786,7 +2786,7 @@ usa la roda del ratolí per a ajustar el volum d'un pas If you click here, selected notes will be cut into the clipboard. You can paste them anywhere in any pattern by clicking on the paste-button. - Si piques aqui, les notes seleccionades seran tallades cap al portapapers. Pots enganxar-les a qualsevol banda de qualsevol patró picant el botó enganxar. + Si piques aquí, les notes seleccionades seran tallades cap al portapapers. Pots enganxar-les a qualsevol banda de qualsevol patró picant el botó enganxar. If you click here, selected notes will be copied into the clipboard. You can paste them anywhere in any pattern by clicking on the paste-button. @@ -2794,7 +2794,7 @@ usa la roda del ratolí per a ajustar el volum d'un pas If you click here, the notes from the clipboard will be pasted at the first visible tact. - Si piques aqui, les notes del portapapers seran enganxades al primer compàs visible. + Si piques aquí, les notes del portapapers seran enganxades al primer compàs visible. Paste notes from clipboard (Ctrl+V) @@ -2870,7 +2870,7 @@ usa la roda del ratolí per a ajustar el volum d'un pas If you click here, erase-mode will be activated. In this mode you can erase single notes. You can also press 'Shift+E' on your keyboard to activate this mode. - Si piques aqui, s'activarà el mode esborrar. En aquest mode pots esborrar notas soltes. També pots pitjar 'Maj+E' al teclat per a activar aquest mode. + Si piques aquí, s'activarà el mode esborrar. En aquest mode pots esborrar notes soltes. També pots pitjar 'Maj+E' al teclat per a activar aquest mode. If you click here, select-mode will be activated. In this mode you can select notes. This is neccessary if you want to cut, copy, paste, delete or move notes. You can also press 'Shift+S' on your keyboard to activate this mode. @@ -2878,7 +2878,7 @@ usa la roda del ratolí per a ajustar el volum d'un pas If you click here, move-mode will be activated. In this mode you can move the notes you selected in select-mode. You can also press 'Shift+M' on your keyboard to activate this mode. - Si piques aqui, el mode moure serà activat. En aquest mode pots moure les notes que has seleccionat en el mode seleccionar. També pots pitjar 'Maj+M' al teclat per a activar aquest mode. + Si piques aquí, el mode moure serà activat. En aquest mode pots moure les notes que has seleccionat en el mode seleccionar. També pots pitjar 'Maj+M' al teclat per a activar aquest mode. @@ -2993,6 +2993,141 @@ usa la roda del ratolí per a ajustar el volum d'un pas Tool for live performance Eina per a actuació en directe + + Incomplete polyphonic immitation tb303 + Imitació polifònica incompleta tb303 + + + + polyb302Synth + + VCF Cutoff Frequency + Freqüència de Tall VCF + + + Cutoff Freq: + Freq Tall: + + + CUT + TALL + + + VCF Resonance + Ressonància VCF + + + Resonance: + Ressonància: + + + RES + RES + + + VCF Envelope Mod + Mod Envoltant VCF + + + Env Mod: + Mod Env: + + + ENV MOD + MOD ENV + + + VCF Envelope Decay + Decaïment Envoltant VCF + + + Decay: + Decaïment: + + + DEC + DEC + + + Slide + Lliscament + + + 303-es-que, 24dB/octave, 3 pole filter + 303-es-que, 24dB/octava, filtre 3 pols + + + Slide Decay + Decaïment de Lliscament + + + Slide Decay: + Decaïment de Lliscament: + + + SLIDE + LLISCA + + + VCO fine detuning + Desafinament fi VCO + + + VCO Fine Detuning: + Desafinament Fi VCO: + + + DETUNE + DESAF + + + Distortion + Distorsió + + + DIST: + DIST: + + + DIST + DIST + + + Waveform + Forma d'ona + + + WAVE: + ONA: + + + WAVE + ONA + + + Sawtooth + Dent de serra + + + Inverted Sawtooth + Dent de serra invertida + + + Triangle + Triangular + + + Square + Quadrada + + + Rounded Square + Quadrada Arrodonida + + + Moog + Moog + projectNotes @@ -3125,7 +3260,7 @@ usa la roda del ratolí per a ajustar el volum d'un pas Toggles the effect on or off. - Encèn o apaga l'efecte. + Encén o apaga l'efecte. Wet/Dry mix @@ -3433,7 +3568,7 @@ Latència: %2 ms Here you can setup the internal buffer-size used by LMMS. Smaller values result in a lower latency but also may cause unusable sound or bad performance, especially on older computers or systems with a non-realtime kernel. - Aquí pots configurar el tamany de la memòria intermèdia interna usada per LMMS. Els valors més petits donen menor latència però també poden causar so inservible o baix rendiment, especialment a ordinadors antics o sistemes amb un nucli sense temps real. + Aquí pots configurar la grandària de la memòria intermèdia interna usada per LMMS. Els valors més petits donen menor latència però també poden causar so inservible o baix rendiment, especialment a ordinadors antics o sistemes amb un nucli sense temps real. Choose LMMS working directory @@ -3483,7 +3618,7 @@ Latència: %2 ms If you have a machine with more then one processor (e.g. dual-core systems) you should use a parallelizing-level above 1 which means that LMMS will try to split up sound-processing into several threads which should should be run on several cores by the underlaying operating-system. Please note that in some cases parallelizing won't work with small buffer-sizes. If you experience problems (i.e. lot of xruns), try to increase buffer-size. Si tens una màquina amb més d'un processador (p.e. sistemes amb nucli dual) hauries d'usar un nivell de paral·lelització més gran que 1, que vol dir que LMMS tractarà de dividir el processament de so en varis fils que haurien d'executar-se en varis nuclis pel sistema operatiu subjacent. -Fixa't que en alguns casos la paral·lelització no funcionarà amb memòries intermèdies de petit tamany. Si experimentes problemes (p.e. molts xruns), prova d'augmentar el tamany de la memòria intermèdia. +Fixa't que en alguns casos la paral·lelització no funcionarà amb memòries intermèdies de grandària petita. Si experimentes problemes (p.e. molts xruns), prova d'augmentar la grandària de la memòria intermèdia. Choose LADSPA plugin directory @@ -4277,7 +4412,7 @@ El botó 'N' normalitzarà la forma d'ona. The 'S' knob sets the stiffness of the selected string. The stiffness of the string affects how long the string will ring out. The lower the setting, the longer the string will ring. - La roda 'S' ajusta la rigidesa de la corda seleccionada. La rigidesa de la corda afecta el temps que la corda resonarà. Quan més baix el valor, més temps sonarà la corda. + La roda 'S' ajusta la rigidesa de la corda seleccionada. La rigidesa de la corda afecta el temps que la corda ressonarà. Quan més baix el valor, més temps sonarà la corda. Pick position diff --git a/include/instrument_track.h b/include/instrument_track.h index 714a6c24a..1d91a6ec0 100755 --- a/include/instrument_track.h +++ b/include/instrument_track.h @@ -189,7 +189,7 @@ public slots: signals: void baseNoteChanged( void ); - void sentMidiTime( const midiTime & _time ); + void noteDone( const note & _n ); protected: @@ -232,6 +232,8 @@ private: tones m_baseTone; octaves m_baseOctave; + vlist m_processHandles; + // widgets on the top of a instrument-track-window tabWidget * m_generalSettingsWidget; @@ -276,10 +278,6 @@ private: void FASTCALL setBaseTone( tones _new_tone ); void FASTCALL setBaseOctave( octaves _new_octave ); - -signals: - void noteDone( const note & _n ); - } ; diff --git a/include/knob.h b/include/knob.h index 135b0d09f..784abbbc4 100644 --- a/include/knob.h +++ b/include/knob.h @@ -104,6 +104,19 @@ signals: protected: + static float s_copiedValue; + static textFloat * s_textFloat; + + float m_mouseOffset; + QPoint m_origMousePos; + bool m_buttonPressed; + + QPixmap * m_knobPixmap; + QString m_hintTextBeforeValue; + QString m_hintTextAfterValue; + + float m_initValue; + virtual void contextMenuEvent( QContextMenuEvent * _me ); virtual void dragEnterEvent( QDragEnterEvent * _dee ); virtual void dropEvent( QDropEvent * _de ); @@ -118,46 +131,25 @@ protected: void drawKnob( QPainter * _p ); void setPosition( const QPoint & _p ); - -// TODO: Need to figure out what is really used by tempoSyncKnob -// to get the private/protected attributes sorted out. Right -// now, just make everything protected. -//private: - void layoutKnob( bool _update = TRUE ); float getValue( const QPoint & _p ); + + +private: + void layoutKnob( bool _update = TRUE ); void recalcAngle( void ); void valueChange( void ); void rangeChange( void ); - void buttonReleased( void ); - - - static float s_copiedValue; - static textFloat * s_textFloat; - - - float m_mouseOffset; - QPoint m_origMousePos; - bool m_buttonPressed; - - float m_pageSize; float m_angle; float m_totalAngle; - - QPixmap * m_knobPixmap; int m_knobNum; - QString m_hintTextBeforeValue; - QString m_hintTextAfterValue; QString m_label; - - float m_initValue; - } ; #endif diff --git a/include/note_play_handle.h b/include/note_play_handle.h index 842bc8f26..90e45194e 100644 --- a/include/note_play_handle.h +++ b/include/note_play_handle.h @@ -207,6 +207,8 @@ public: m_instrumentTrack->m_instrument->waitForWorkerThread(); } + void processMidiTime( const midiTime & _time ); + #if SINGERBOT_SUPPORT int patternIndex( void ) { @@ -250,7 +252,6 @@ private: private slots: - void processMidiTime( const midiTime & _time ); void updateFrequency( void ); } ; diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 4d5b8ba1e..69cdc45ca 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -26,6 +26,7 @@ SUBDIRS = \ midi_import \ organic \ plucked_string_synth \ + polyb302 \ $(SINGERBOT_DIR) \ $(STK_DIR) \ triple_oscillator \ diff --git a/plugins/polyb302/Makefile.am b/plugins/polyb302/Makefile.am new file mode 100644 index 000000000..bd94dece1 --- /dev/null +++ b/plugins/polyb302/Makefile.am @@ -0,0 +1,33 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="polyb302" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./polyb302.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(BIN2RES) $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES = libpolyb302.la + +libpolyb302_la_SOURCES = polyb302.cpp polyb302.h + +$(libpolyb302_la_SOURCES): ./embedded_resources.h diff --git a/plugins/polyb302/artwork.png b/plugins/polyb302/artwork.png new file mode 100644 index 000000000..719a9e354 Binary files /dev/null and b/plugins/polyb302/artwork.png differ diff --git a/plugins/polyb302/logo.png b/plugins/polyb302/logo.png new file mode 100644 index 000000000..456a42675 Binary files /dev/null and b/plugins/polyb302/logo.png differ diff --git a/plugins/polyb302/polyb302.cpp b/plugins/polyb302/polyb302.cpp new file mode 100644 index 000000000..5da52fc1b --- /dev/null +++ b/plugins/polyb302/polyb302.cpp @@ -0,0 +1,961 @@ +/* + * polyb302.cpp - implementation of class polyb302 which is a bass synth + * attempting to emulate the Roland TB303 bass synth + * + * Copyright (c) 2006-2007 Paul Giblock + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * lb302FilterIIR2 is based on the gsyn filter code by Andy Sloane. + * + * lb302Filter3Pole is based on the TB303 instrument written by + * Josep M Comajuncosas for the CSounds library + * + * 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 "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "polyb302.h" +#include "audio_device.h" +#include "instrument_track.h" +#include "instrument_play_handle.h" +#include "led_checkbox.h" +#include "note_play_handle.h" +#include "templates.h" +#include "buffer_allocator.h" +#include "knob.h" + +#undef SINGLE_SOURCE_COMPILE +#include "embed.cpp" +#include "polyb302.moc" + + +// Envelope Recalculation period +#define ENVINC 64 + +// +// New config +// +#define LB_24_IGNORE_ENVELOPE +#define LB_FILTERED +//#define LB_24_RES_TRICK + +#define LB_DIST_RATIO 4.0 +#define LB_24_VOL_ADJUST 3.0 + + + + +using namespace std; +extern "C" +{ + +plugin::descriptor polyb302_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "PoLyB302", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Incomplete polyphonic immitation tb303" ), + "Javier Serrano Polo ", + 0x0100, + plugin::Instrument, + new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ), + NULL +} ; + + +// necessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new polyb302Synth( static_cast( _data ) ) ); +} + +} + + + +// +// lb302Filter +// + +lb302Filter::lb302Filter( lb302FilterState * _p_fs, engine * _engine ) : + engineObject( _engine ), + m_fs( _p_fs ), + m_vcf_c0( 0 ), + m_vcf_e0( 0 ), + m_vcf_e1( 0 ) +{ +} + + + + +void lb302Filter::recalc( void ) +{ + m_vcf_e1 = exp( 4.909 + 1.5876 * m_fs->envmod + 2.1553 * m_fs->cutoff + + 1.2 * m_fs->reso ); + m_vcf_e0 = exp( 4.8434 - 0.8 * m_fs->envmod + 2.1553 * m_fs->cutoff + + 0.7696 * m_fs->reso ); + m_vcf_e0 *= M_PI / eng()->getMixer()->sampleRate(); + m_vcf_e1 *= M_PI / eng()->getMixer()->sampleRate(); + m_vcf_e1 -= m_vcf_e0; +} + + + + +void lb302Filter::envRecalc( void ) +{ + // Filter Decay. vcf_decay is adjusted for Hz and ENVINC + m_vcf_c0 *= m_fs->envdecay; + m_vcf_rescoeff = exp( -1.20 + 3.455 * m_fs->reso ); +} + + + + +void lb302Filter::playNote( void ) +{ + m_vcf_c0 = m_vcf_e1; +} + + + +// +// lb302FilterIIR2 +// + +lb302FilterIIR2::lb302FilterIIR2( lb302FilterState * _p_fs, engine * _engine ) : + lb302Filter( _p_fs, _engine ), + m_vcf_d1( 0 ), + m_vcf_d2( 0 ), + m_vcf_a( 0 ), + m_vcf_b( 0 ), + m_vcf_c( 1 ) +{ + m_dist = new effectLib::distortion<>( 1.0, 1.0f ); +} + + + + +lb302FilterIIR2::~lb302FilterIIR2() +{ + delete m_dist; +} + + + + +void lb302FilterIIR2::recalc( void ) +{ + lb302Filter::recalc(); + //m_dist->setThreshold(0.5+(m_fs->dist*2.0)); + m_dist->setThreshold( m_fs->dist * 75.0 ); +} + + + + +void lb302FilterIIR2::envRecalc( void ) +{ + lb302Filter::envRecalc(); + + // e0 is adjusted for Hz and doesn't need ENVINC + float w = m_vcf_e0 + m_vcf_c0; + float k = exp( -w / m_vcf_rescoeff ); + // Does this mean c0 is inheritantly? + m_vcf_a = 2.0 * cos( 2.0 * w ) * k; + m_vcf_b = -k * k; + m_vcf_c = 1.0 - m_vcf_a - m_vcf_b; +} + + + + +float lb302FilterIIR2::process( const float & _samp ) +{ + float ret = m_vcf_a * m_vcf_d1 + m_vcf_b * m_vcf_d2 + m_vcf_c * _samp; + // Delayed samples for filter + m_vcf_d2 = m_vcf_d1; + m_vcf_d1 = ret; + + if( m_fs->dist > 0 ) + { + ret = m_dist->nextSample( ret ); + } + // output = IIR2 + dry + return( ret ); +} + + + +// +// lb302Filter3Pole +// + +lb302Filter3Pole::lb302Filter3Pole( lb302FilterState * _p_fs, + engine * _engine ) : + lb302Filter( _p_fs, _engine ), + m_ay1( 0 ), + m_ay2( 0 ), + m_aout( 0 ), + m_lastin( 0 ) +{ +} + + + + +void lb302Filter3Pole::recalc( void ) +{ + // DO NOT CALL BASE CLASS + m_vcf_e0 = 0.000001; + m_vcf_e1 = 1.0; +} + + + + +// TODO: Try using k instead of vcf_reso +void lb302Filter3Pole::envRecalc( void ) +{ + lb302Filter::envRecalc(); + + + // e0 is adjusted for Hz and doesn't need ENVINC + float w = m_vcf_e0 + m_vcf_c0; + float k = ( m_fs->cutoff > 0.975 ) ? 0.975 : m_fs->cutoff; + //TODO: Fix high quality + float kfco = 50.0f + k * ( 2300.0f - 1600.0f * m_fs->envmod + + w * ( 700.0f + 1500.0f * k + ( 1500.0f + k * ( + eng()->getMixer()->sampleRate() / 2.0f - 6000.0f ) ) + * m_fs->envmod ) ); + //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 + + +#ifdef LB_24_IGNORE_ENVELOPE + // m_kfcn = m_fs->cutoff; + m_kfcn = 2.0 * kfco / eng()->getMixer()->sampleRate(); +#else + m_kfcn = w; +#endif + m_kp = ( ( -2.7528 * m_kfcn + 3.0429 ) * m_kfcn + 1.718 ) * m_kfcn + - 0.9984; + m_kp1 = m_kp + 1.0; + m_kp1h = 0.5 * m_kp1; +#ifdef LB_24_RES_TRICK + k = exp( -w / m_vcf_rescoeff ); + m_kres = k * ( ( ( -2.7079 * m_kp1 + 10.963 ) * m_kp1 - 14.934 ) * m_kp1 + + 8.4974 ); +#else + m_kres = m_fs->reso * ( ( ( -2.7079 * m_kp1 + 10.963 ) * m_kp1 + - 14.934 ) * m_kp1 + 8.4974 ); +#endif + // ENVMOD was DIST*/ + m_value = 1.0 + ( m_fs->dist * ( 1.5 + 2.0 * m_kres + * ( 1.0 - m_kfcn ) ) ); +} + + + + +float lb302Filter3Pole::process( const float & _samp ) +{ + float ax1 = m_lastin; + float ay11 = m_ay1; + float ay31 = m_ay2; + m_lastin = _samp - tanh( m_kres * m_aout ); + m_ay1 = m_kp1h * ( m_lastin + ax1 ) - m_kp * m_ay1; + m_ay2 = m_kp1h * ( m_ay1 + ay11 ) - m_kp * m_ay2; + m_aout = m_kp1h * ( m_ay2 + ay31 ) - m_kp * m_aout; + + return( tanh( m_aout * m_value ) * LB_24_VOL_ADJUST + / ( 1.0 + m_fs->dist ) ); +} + + + +// +// PoLyBSynth +// + +polyb302Synth::polyb302Synth( instrumentTrack * _track ) : + instrument( _track, &polyb302_plugin_descriptor ) +{ + m_vcf_cut_knob = new knob( knobBright_26, this, + tr( "VCF Cutoff Frequency" ), + eng(), _track ); + m_vcf_cut_knob->setRange( 0.0f, 1.5f, 0.005f ); // Originally [0,1.0] + m_vcf_cut_knob->setInitValue( 0.75f ); + m_vcf_cut_knob->move( 75, 130 ); + m_vcf_cut_knob->setHintText( tr( "Cutoff Freq:" ) + " ", "" ); + m_vcf_cut_knob->setLabel( tr( "CUT" ) ); + + m_vcf_res_knob = new knob( knobBright_26, this, tr( "VCF Resonance" ), + eng(), _track ); + m_vcf_res_knob->setRange( 0.0f, 1.25f, 0.005f ); // Originally [0,1.0] + m_vcf_res_knob->setInitValue( 0.75f ); + m_vcf_res_knob->move( 120, 130 ); + m_vcf_res_knob->setHintText( tr( "Resonance:" ) + " ", "" ); + m_vcf_res_knob->setLabel( tr( "RES" ) ); + + m_vcf_mod_knob = new knob( knobBright_26, this, + tr( "VCF Envelope Mod" ), + eng(), _track ); + m_vcf_mod_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + m_vcf_mod_knob->setInitValue( 1.0f ); + m_vcf_mod_knob->move( 165, 130 ); + m_vcf_mod_knob->setHintText( tr( "Env Mod:" ) + " ", "" ); + m_vcf_mod_knob->setLabel( tr( "ENV MOD" ) ); + + m_vcf_dec_knob = new knob( knobBright_26, this, + tr( "VCF Envelope Decay" ), + eng(), _track ); + m_vcf_dec_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + m_vcf_dec_knob->setInitValue( 0.1f ); + m_vcf_dec_knob->move( 210, 130 ); + m_vcf_dec_knob->setHintText( tr( "Decay:" ) + " ", "" ); + m_vcf_dec_knob->setLabel( tr( "DEC" ) ); + + m_slideToggle = new ledCheckBox( "Slide", this, tr( "Slide" ), + eng(), _track ); + m_slideToggle->move( 10, 200 ); + + +// m_accentToggle = new ledCheckBox( "Accent", this, +// tr( "Accent" ), +// eng(), _track ); +// m_accentToggle->move( 10, 200 ); +// m_accentToggle->setDisabled(true); + + +// m_deadToggle = new ledCheckBox( "Dead", this, +// tr( "Dead" ), +// eng(), _track ); +// m_deadToggle->move( 10, 220 ); + + m_db24Toggle = new ledCheckBox( "24dB/oct", this, + tr( "303-es-que, 24dB/octave, 3 pole filter" ), + eng(), _track ); + m_db24Toggle->move( 10, 150 ); + + + m_slide_dec_knob = new knob( knobBright_26, this, tr( "Slide Decay" ), + eng(), _track ); + m_slide_dec_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + m_slide_dec_knob->setInitValue( 0.6f ); + m_slide_dec_knob->move( 210, 75 ); + m_slide_dec_knob->setHintText( tr( "Slide Decay:" ) + " ", "" ); + m_slide_dec_knob->setLabel( tr( "SLIDE" ) ); + + m_vco_fine_detune_knob = new knob( knobBright_26, this, + tr( "VCO fine detuning" ), + eng(), _track ); + m_vco_fine_detune_knob->setRange( -100.0f, 100.0f, 1.0f ); + m_vco_fine_detune_knob->setInitValue( 0.0f ); + m_vco_fine_detune_knob->move( 165, 75 ); + m_vco_fine_detune_knob->setHintText( tr( "VCO Fine Detuning:" ) + " ", + "cents" ); + m_vco_fine_detune_knob->setLabel( tr( "DETUNE" ) ); + + + m_dist_knob = new knob( knobBright_26, this, tr( "Distortion" ), + eng(), _track ); + m_dist_knob->setRange( 0.0f, 1.0f, 0.01f ); // Originally [0,1.0] + m_dist_knob->setInitValue( 0.0f ); + m_dist_knob->move( 210, 190 ); + m_dist_knob->setHintText( tr( "DIST:" ) + " ", "" ); + m_dist_knob->setLabel( tr( "DIST" ) ); + + + m_wave_knob = new knob( knobBright_26, this, tr( "Waveform" ), + eng(), _track ); + m_wave_knob->setRange( 0.0f, 5.0f, 1.0f ); // Originally [0,1.0] + m_wave_knob->setInitValue( 0.0f ); + m_wave_knob->move( 120, 75 ); + m_wave_knob->setHintText( tr( "WAVE:" ) + " ", "" ); + m_wave_knob->setLabel( tr( "WAVE" ) ); + + +#ifdef QT4 + setAutoFillBackground( TRUE ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + setPalette( pal ); +#else + setErasePixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); +#endif + + connect( m_vcf_cut_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + connect( m_vcf_res_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + connect( m_vcf_mod_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + connect( m_vcf_dec_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + connect( m_vco_fine_detune_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( detuneChanged( float) ) ); + connect( m_db24Toggle, SIGNAL( toggled( bool ) ), + this, SLOT ( db24Toggled( bool) ) ); + connect( m_dist_knob, SIGNAL( valueChanged(float) ), + this, SLOT ( filterChanged( float ) ) ); + connect( m_wave_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( waveChanged( float ) ) ); +} + + + + +polyb302Synth::~polyb302Synth() +{ +} + + + + +void polyb302Synth::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + m_vcf_cut_knob->saveSettings( _doc, _this, "vcf_cut" ); + m_vcf_res_knob->saveSettings( _doc, _this, "vcf_res" ); + m_vcf_mod_knob->saveSettings( _doc, _this, "vcf_mod" ); + m_vcf_dec_knob->saveSettings( _doc, _this, "vcf_dec" ); + + m_vco_fine_detune_knob->saveSettings( _doc, _this, "vco_detune" ); + m_wave_knob->saveSettings( _doc, _this, "shape" ); + m_dist_knob->saveSettings( _doc, _this, "dist" ); + m_slide_dec_knob->saveSettings( _doc, _this, "slide_dec" ); + + m_slideToggle->saveSettings( _doc, _this, "slide" ); +// m_deadToggle->saveSettings( _doc, _this, "dead" ); + m_db24Toggle->saveSettings( _doc, _this, "db24"); +} + + + + +void polyb302Synth::loadSettings( const QDomElement & _this ) +{ + m_vcf_cut_knob->loadSettings( _this, "vcf_cut" ); + m_vcf_res_knob->loadSettings( _this, "vcf_res" ); + m_vcf_mod_knob->loadSettings( _this, "vcf_mod" ); + m_vcf_dec_knob->loadSettings( _this, "vcf_dec" ); + + m_vco_fine_detune_knob->loadSettings( _this, "vco_detune" ); + m_dist_knob->loadSettings( _this, "dist" ); + m_wave_knob->loadSettings( _this, "shape" ); + m_slide_dec_knob->loadSettings( _this, "slide_dec" ); + + m_slideToggle->loadSettings( _this, "slide" ); +// m_deadToggle->loadSettings( _this, "dead" ); + m_db24Toggle->loadSettings( _this, "db24" ); +} + + + + +QString polyb302Synth::nodeName( void ) const +{ + return( polyb302_plugin_descriptor.name ); +} + + + + +void polyb302Synth::playNote( notePlayHandle * _n, bool ) +{ + //int nidx = _n->index(); + + //if( _n->nphsOfInstrumentTrack(_n->getInstrumentTrack()).first() != _n ) + //if( _n->released() && _n->nphsOfInstrumentTrack( _n->getInstrumentTrack() ).count() > 1 ) + // return; + +/* + if (_n->released() ) { + if( notePlayHandle::nphsOfInstrumentTrack( getInstrumentTrack() ).size() > 0 + && notePlayHandle::nphsOfInstrumentTrack( getInstrumentTrack(), + TRUE ).last() == _n ) + { + return; + } + } +*/ + + handleState * hstate; + if( !_n->m_pluginData ) + { + hstate = new handleState( this ); + _n->m_pluginData = hstate; + m_handleStates.push_back( hstate ); + } + else + { + hstate = (handleState *)_n->m_pluginData; + } + + float freq = getInstrumentTrack()->frequency( _n ); + + if( _n->totalFramesPlayed() <= hstate->m_lastFramesPlayed ) + { + // TODO: Try moving to the if() below +// if( m_deadToggle->value() == 0 ) + { + hstate->m_sample_cnt = 0; + hstate->m_vca_mode = 0; + hstate->m_vca_a = 0; + } + + // Adjust inc on SampRate change or detuning change + hstate->m_vco_inc = hstate->m_vco_detune + / eng()->getMixer()->sampleRate(); + + // Initiate Slide + // TODO: Break out into function, + // should be called again on detuneChanged + if( hstate->m_vco_slideinc ) + { + hstate->m_vco_slide = hstate->m_vco_inc + - hstate->m_vco_slideinc; + hstate->m_vco_slidebase = hstate->m_vco_inc; + hstate->m_vco_slideinc = 0; + } + else + { + hstate->m_vco_slide = 0; + } + // End break-out + + // Slide note, save inc for next note + if (m_slideToggle->value()) + { + hstate->m_vco_slideinc = hstate->m_vco_inc; + // May need to equal m_vco_slidebase+m_vco_slide if last + // note slid + } + + + hstate->recalcFilter(); + +// if( m_deadToggle->value() == 0 ) + { + // Swap next two blocks?? + hstate->m_vcf->playNote(); + // Ensure envelope is recalculated + hstate->m_vcf_envpos = ENVINC; + + // Double Check + hstate->m_vca_mode = 0; + hstate->m_vca_a = 0.0; + } + } + + const Uint32 frames = eng()->getMixer()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + if( buf ) + { + hstate->process( buf, frames, freq ); + getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); + + bufferAllocator::free( buf ); + } + + hstate->m_lastFramesPlayed = _n->totalFramesPlayed(); +} + + + + +void polyb302Synth::deleteNotePluginData( notePlayHandle * _n ) +{ + handleState * hstate = (handleState *)_n->m_pluginData; + m_handleStates.remove( hstate ); + delete hstate; +} + + + + +void polyb302Synth::db24Toggled( bool ) +{ + for( vlist::iterator it = m_handleStates.begin(); + it != m_handleStates.end(); ++it ) + { + ( *it )->db24Toggled(); + } +} + + + + +void polyb302Synth::detuneChanged( float ) +{ + for( vlist::iterator it = m_handleStates.begin(); + it != m_handleStates.end(); ++it ) + { + ( *it )->detuneChanged(); + } +} + + + + +// TODO: Split into one function per knob. envdecay doesn't require +// recalcFilter. +void polyb302Synth::filterChanged( float ) +{ + for( vlist::iterator it = m_handleStates.begin(); + it != m_handleStates.end(); ++it ) + { + ( *it )->filterChanged(); + } +} + + + + +// TODO: Set m_vco_shape in here. +void polyb302Synth::waveChanged( float ) +{ + switch( (int)rint( m_wave_knob->value() ) ) + { + case 0: + m_wave_knob->setHintText( tr( "Sawtooth " ), "" ); + break; + case 1: + m_wave_knob->setHintText( tr( "Inverted Sawtooth " ), + "" ); + break; + case 2: + m_wave_knob->setHintText( tr( "Triangle " ), "" ); + break; + case 3: + m_wave_knob->setHintText( tr( "Square " ), "" ); + break; + case 4: + m_wave_knob->setHintText( tr( "Rounded Square " ), "" ); + break; + case 5: + m_wave_knob->setHintText( tr( "Moog " ), "" ); + break; + } +} + + + + + + + + +polyb302Synth::handleState::handleState( polyb302Synth * _synth ) : + engineObject( _synth->eng() ) +{ + m_vco_inc = 0.0; + m_vco_c = 0; + m_vco_k = 0; + + m_vco_slide = 0; + m_vco_slideinc = 0; + + m_fs.cutoff = 0; + m_fs.envmod = 0; + m_fs.reso = 0; + m_fs.envdecay = 0; + m_fs.dist = 0; + + m_vcf_envpos = ENVINC; + m_vco_detune = 0; + + m_vca_mode = 2; + m_vca_a = 0; + //m_vca_attack = 1.0 - 0.94406088; + m_vca_attack = 1.0 - 0.96406088; + m_vca_decay = 0.99897516; + + m_vco_shape = SAWTOOTH; + + // Experimenting between original (0.5) and 1.0 + m_vca_a0 = 0.5; + + if( _synth->m_db24Toggle->isChecked() ) + { + m_vcf = new lb302Filter3Pole( &m_fs, eng() ); + } + else + { + m_vcf = new lb302FilterIIR2( &m_fs, eng() ); + } + recalcFilter(); + + m_lastFramesPlayed = 0; + + m_synth = _synth; + + filterChanged(); + detuneChanged(); +} + + + + +polyb302Synth::handleState::~handleState() +{ + delete m_vcf; +} + + + + +void polyb302Synth::handleState::db24Toggled( void ) +{ + delete m_vcf; + if( m_synth->m_db24Toggle->isChecked() ) + { + m_vcf = new lb302Filter3Pole( &m_fs, eng() ); + } + else + { + m_vcf = new lb302FilterIIR2( &m_fs, eng() ); + } + recalcFilter(); +} + + + + +void polyb302Synth::handleState::detuneChanged( void ) +{ + m_vco_detune = powf( 2.0f, + (float)m_synth->m_vco_fine_detune_knob->value() + / 1200.0f ); + m_vco_inc = m_vco_detune / eng()->getMixer()->sampleRate(); + + // If a slide note is pending, + if( m_vco_slideinc ) + { + m_vco_slideinc = m_vco_inc; + } + + // If currently sliding, + // May need to rescale m_vco_slide as well + if( m_vco_slide ) + { + m_vco_slidebase = m_vco_detune + / eng()->getMixer()->sampleRate(); + } +} + + + + +// TODO: Split into one function per knob. envdecay doesn't require +// recalcFilter. +void polyb302Synth::handleState::filterChanged( void ) +{ + m_fs.cutoff = m_synth->m_vcf_cut_knob->value(); + m_fs.reso = m_synth->m_vcf_res_knob->value(); + m_fs.envmod = m_synth->m_vcf_mod_knob->value(); + m_fs.dist = LB_DIST_RATIO * m_synth->m_dist_knob->value(); + + float d = 0.2 + 2.3 * m_synth->m_vcf_dec_knob->value(); + d *= eng()->getMixer()->sampleRate(); + // decay is 0.1 to the 1/d * ENVINC + m_fs.envdecay = pow( 0.1, ENVINC / d ); + // vcf_envdecay is now adjusted for both + // sampling rate and ENVINC + recalcFilter(); +} + + + + +// OBSOLETE. Break apart once we get Q_OBJECT to work. >:[ +void polyb302Synth::handleState::recalcFilter( void ) +{ + m_vcf->recalc(); + + // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it + // right now. Should be toggled by LB_24_RES_TRICK at the moment. + + /*kfcn = 2.0 * (((vcf_cutoff*3000))) / m_LB_HZ; + kp = ((-2.7528*kfcn + 3.0429)*kfcn + 1.718)*kfcn - 0.9984; + kp1 = kp+1.0; + kp1h = 0.5*kp1; + kres = (((vcf_reso))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974); + value = 1.0+( (((0))) *(1.5 + 2.0*kres*(1.0-kfcn))); // ENVMOD was DIST*/ + + m_vcf_envpos = ENVINC; // Trigger filter update in process() +} + + + + +void polyb302Synth::handleState::process( sampleFrame * _outbuf, + const Uint32 _size, + float _freq ) +{ + for( Uint32 i = 0; i < _size; i++ ) + { + // update m_vcf + if( m_vcf_envpos >= ENVINC ) + { + m_vcf->envRecalc(); + + m_vcf_envpos = 0; + + if( m_vco_slide ) + { + m_vco_inc = m_vco_slidebase - m_vco_slide; + // Calculate coeff from dec_knob on knob change. + // TODO: Adjust for ENVINC + m_vco_slide *= 0.9 + + ( m_synth->m_slide_dec_knob->value() + * 0.0999 ); + } + } + + m_sample_cnt++; + m_vcf_envpos++; + + // 01/21/07 Changed to VCF -> VCA instead of VCA -> VCF +#ifdef LB_FILTERED + float samp = m_vcf->process( m_vco_k ) * 2.0 * m_vca_a; +#else + float samp = m_vco_k * m_vca_a; +#endif + + for( int c = 0; c < DEFAULT_CHANNELS; c++ ) + { + _outbuf[i][c] = samp; + } + + + // update vco + m_vco_c += m_vco_inc * _freq; + if( m_vco_c > 0.5 ) + { + m_vco_c -= 1.0; + } + + switch( (int)rint( m_synth->m_wave_knob->value() ) ) + { + case 0: m_vco_shape = SAWTOOTH; break; + case 1: m_vco_shape = INVERTED_SAWTOOTH; break; + case 2: m_vco_shape = TRIANGLE; break; + case 3: m_vco_shape = SQUARE; break; + case 4: m_vco_shape = ROUND_SQUARE; break; + case 5: m_vco_shape = MOOG; break; + default: m_vco_shape = SAWTOOTH; break; + } + + // add m_vco_shape_param the changes the shape of each curve. + // merge sawtooths with triangle and square with round square? + switch( m_vco_shape ) + { + case SAWTOOTH: // p0: curviness of line + // Is this sawtooth backwards? + m_vco_k = m_vco_c; + break; + + case INVERTED_SAWTOOTH: // p0: curviness of line + // Is this sawtooth backwards? + m_vco_k = -m_vco_c; + break; + + // TODO: I think TRIANGLE is broken. + // p0: duty rev.saw<->triangle<->saw p1: curviness + case TRIANGLE: + m_vco_k = m_vco_c * 2.0 + 0.5; + if( m_vco_k > 0.5 ) + { + m_vco_k = 1.0 - m_vco_k; + } + break; + + case SQUARE: // p0: slope of top + m_vco_k = ( m_vco_c < 0 ) ? 0.5 : -0.5; + break; + + case ROUND_SQUARE: // p0: width of round + m_vco_k = ( m_vco_c < 0 ) ? + sqrtf( 1 - m_vco_c * m_vco_c * 4 ) + - 0.5 : + -0.5; + break; + + // Maybe the fall should be exponential/sinsoidal + // instead of quadric. + case MOOG: + // [-0.5, 0]: Rise, [0,0.25]: Slope down, + // [0.25,0.5]: Low + m_vco_k = m_vco_c * 2.0 + 0.5; + if( m_vco_k > 1.0 ) + { + m_vco_k = -0.5; + } + else if( m_vco_k > 0.5 ) + { + float w = 2 * ( m_vco_k - 0.5 ) - 1; + m_vco_k = 0.5 - sqrtf( 1 - w * w ); + // MOOG wave gets filtered away + m_vco_k *= 2.0; + } + break; + } + + // Make it louder. For the better? + //m_vco_k*=2.0; + + // Handle Envelope + // TODO: Add decay once I figure out how to extend past the end + // of a note. + if( m_sample_cnt >= 0.5 * eng()->getMixer()->sampleRate() ) + { + m_vca_mode = 2; + } + if( m_vca_mode == 0 ) + { + m_vca_a += ( m_vca_a0 - m_vca_a ) * m_vca_attack; + } + else if( m_vca_mode == 1 ) + { + m_vca_a *= m_vca_decay; + // the following line actually speeds up processing + if( m_vca_a < 1 / 65536.0 ) + { + m_vca_a = 0; + m_vca_mode = 2; + } + } + + } +} diff --git a/plugins/polyb302/polyb302.h b/plugins/polyb302/polyb302.h new file mode 100644 index 000000000..0e63d0eab --- /dev/null +++ b/plugins/polyb302/polyb302.h @@ -0,0 +1,251 @@ +/* + * polyb302.h - declaration of class polyb302 which is a bass synth attempting + * to emulate the Roland TB303 bass synth + * + * Copyright (c) 2006-2007 Paul Giblock + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * lb302FilterIIR2 is based on the gsyn filter code by Andy Sloane. + * + * lb302Filter3Pole is based on the TB303 instrument written by + * Josep M Comajuncosas for the CSounds library + * + * 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 _POLYB302_H_ +#define _POLYB302_H_ + +#include "instrument.h" +#include "effect_lib.h" + + +class knob; +class ledCheckBox; +class notePlayHandle; + + +typedef struct +{ + float cutoff; + float reso; + float envmod; + float envdecay; + float dist; +} lb302FilterState; + + + + +class lb302Filter : public engineObject +{ +public: + lb302Filter( lb302FilterState * _p_fs, engine * _engine ); + virtual ~lb302Filter() {}; + + virtual void recalc( void ); + virtual void envRecalc( void ); + virtual float process( const float & _samp ) = 0; + virtual void playNote( void ); + + +protected: + lb302FilterState * m_fs; + + // Filter Decay + float m_vcf_c0; // c0=e1 on retrigger; c0*=ed every sample; cutoff=e0+c0 + float m_vcf_e0; // e0 and e1 for interpolation + float m_vcf_e1; + float m_vcf_rescoeff; // Resonance coefficient [0.30,9.54] + +}; + + + + +class lb302FilterIIR2 : public lb302Filter +{ +public: + lb302FilterIIR2( lb302FilterState * _p_fs, engine * _engine ); + virtual ~lb302FilterIIR2(); + + virtual void recalc( void ); + virtual void envRecalc( void ); + virtual float process( const float & _samp ); + + +protected: + float m_vcf_d1; // d1 and d2 are added back into the sample with + float m_vcf_d2; // vcf_a and b as coefficients. IIR2 resonance + // loop. + + // IIR2 Coefficients for mixing dry and delay. + float m_vcf_a; // Mixing coefficients for the final sound. + float m_vcf_b; + float m_vcf_c; + + effectLib::distortion<> * m_dist; + +}; + + + + +class lb302Filter3Pole : public lb302Filter +{ +public: + lb302Filter3Pole( lb302FilterState * _p_fs, engine * _engine ); + + virtual void envRecalc( void ); + virtual void recalc( void ); + virtual float process( const float & _samp ); + + +protected: + float m_kfcn, + m_kp, + m_kp1, + m_kp1h, + m_kres; + float m_ay1, + m_ay2, + m_aout, + m_lastin, + m_value; +}; + + + + +class polyb302Synth : public instrument +{ + Q_OBJECT +public: + polyb302Synth( instrumentTrack * _track ); + virtual ~polyb302Synth(); + + virtual void FASTCALL playNote( notePlayHandle * _n, + bool _try_parallelizing ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + + +public slots: + void db24Toggled( bool ); + void detuneChanged( float ); + void filterChanged( float ); + void waveChanged( float ); + + +private: + class handleState : public engineObject + { + public: + handleState( polyb302Synth * _synth ); + virtual ~handleState(); + + enum vco_shape_t { + SAWTOOTH, INVERTED_SAWTOOTH, SQUARE, TRIANGLE, MOOG, + ROUND_SQUARE + }; + + // Oscillator + // Sample increment for the frequency. Creates Sawtooth. + float m_vco_inc; + // Raw oscillator sample [-0.5,0.5] + float m_vco_k; + // Raw oscillator sample [-0.5,0.5] + float m_vco_c; + + // Current value of slide exponential curve. Nonzero=sliding + float m_vco_slide; + // Slide base to use in next node. Nonzero=slide next note + float m_vco_slideinc; + // The base vco_inc while sliding. + float m_vco_slidebase; + + float m_vco_detune; + + vco_shape_t m_vco_shape; + + // User settings + lb302FilterState m_fs; + lb302Filter * m_vcf; + + float m_lastFramesPlayed; + + // More States + // Update counter. Updates when >= ENVINC + int m_vcf_envpos; + + float m_vca_attack; // Amp attack + float m_vca_decay; // Amp decay + float m_vca_a0; // Initial amplifier coefficient + float m_vca_a; // Amplifier coefficient. + + // Envelope State + int m_vca_mode; // 0: attack, 1: decay, 2: idle + + // My hacks + int m_sample_cnt; + + // TODO: split synth slots + polyb302Synth * m_synth; + + void recalcFilter( void ); + + void process( sampleFrame * _outbuf, const Uint32 _size, + float _freq ); + + void db24Toggled( void ); + void detuneChanged( void ); + void filterChanged( void ); + + } ; + + + knob * m_vcf_cut_knob; + knob * m_vcf_res_knob; + knob * m_vcf_dec_knob; + knob * m_vcf_mod_knob; + + knob * m_vco_fine_detune_knob; + + knob * m_dist_knob; + knob * m_wave_knob; + + ledCheckBox * m_slideToggle; +// ledCheckBox * m_accentToggle; +// ledCheckBox * m_deadToggle; + ledCheckBox * m_db24Toggle; + + knob * m_slide_dec_knob; + + vlist m_handleStates; + +} ; + + +#endif diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index 1f2c3c19f..c9bb0aee5 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -66,11 +66,8 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, setDetuning( _n.detuning() ); if( detuning() ) { - connect( m_instrumentTrack, - SIGNAL( sentMidiTime( const midiTime & ) ), - this, - SLOT( processMidiTime( const midiTime & ) ) ); processMidiTime( pos() ); + m_instrumentTrack->m_processHandles.push_back( this ); connect( detuning(), SIGNAL( valueChanged( float ) ), this, SLOT( updateFrequency() ) ); } @@ -106,9 +103,16 @@ notePlayHandle::~notePlayHandle() noteOff( 0 ); } - if( m_instrumentTrack != NULL && m_pluginData != NULL ) + if( m_instrumentTrack != NULL ) { - m_instrumentTrack->deleteNotePluginData( this ); + if( detuning() ) + { + m_instrumentTrack->m_processHandles.remove( this ); + } + if( m_pluginData != NULL ) + { + m_instrumentTrack->deleteNotePluginData( this ); + } } for( notePlayHandleVector::iterator it = m_subNotes.begin(); diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index 2b1b917a1..72649c7e1 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -972,7 +972,13 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start, bb_track = NULL; sendMidiTime( _start ); } - emit sentMidiTime( _start ); + + // Handle automation: detuning + for( vlist::iterator it = m_processHandles.begin(); + it != m_processHandles.end(); ++it ) + { + ( *it )->processMidiTime( _start ); + } if ( tcos.size() == 0 ) { @@ -1349,6 +1355,7 @@ void instrumentTrack::invalidateAllMyNPH( void ) m_notesMutex.unlock(); // invalidate all note-play-handles linked to this channel + m_processHandles.clear(); eng()->getMixer()->checkValidityOfPlayHandles(); m_trackType = INSTRUMENT_TRACK;