diff --git a/data/presets/Kicker/Clap.xpf b/data/presets/Kicker/Clap.xpf new file mode 100644 index 000000000..4b3394228 --- /dev/null +++ b/data/presets/Kicker/Clap.xpf @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Kicker/HihatClosed.xpf b/data/presets/Kicker/HihatClosed.xpf new file mode 100644 index 000000000..72ee5b9bd --- /dev/null +++ b/data/presets/Kicker/HihatClosed.xpf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Kicker/HihatOpen.xpf b/data/presets/Kicker/HihatOpen.xpf new file mode 100644 index 000000000..1e0c9b3a9 --- /dev/null +++ b/data/presets/Kicker/HihatOpen.xpf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Kicker/Shaker.xpf b/data/presets/Kicker/Shaker.xpf new file mode 100644 index 000000000..452047aa3 --- /dev/null +++ b/data/presets/Kicker/Shaker.xpf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Kicker/SnareLong.xpf b/data/presets/Kicker/SnareLong.xpf new file mode 100644 index 000000000..bcccb4172 --- /dev/null +++ b/data/presets/Kicker/SnareLong.xpf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/themes/default/loop_point.png b/data/themes/default/loop_point.png deleted file mode 100644 index be6499207..000000000 Binary files a/data/themes/default/loop_point.png and /dev/null differ diff --git a/data/themes/default/loop_point_b.png b/data/themes/default/loop_point_b.png new file mode 100644 index 000000000..bcd65ad08 Binary files /dev/null and b/data/themes/default/loop_point_b.png differ diff --git a/data/themes/default/loop_point_e.png b/data/themes/default/loop_point_e.png new file mode 100644 index 000000000..40fdad94a Binary files /dev/null and b/data/themes/default/loop_point_e.png differ diff --git a/data/themes/default/mixer_send_off.png b/data/themes/default/mixer_send_off.png index 6f426c36f..0adafe296 100644 Binary files a/data/themes/default/mixer_send_off.png and b/data/themes/default/mixer_send_off.png differ diff --git a/data/themes/default/mixer_send_on.png b/data/themes/default/mixer_send_on.png index 6861c7acd..e4b865587 100644 Binary files a/data/themes/default/mixer_send_on.png and b/data/themes/default/mixer_send_on.png differ diff --git a/data/themes/default/send_bg_arrow.png b/data/themes/default/send_bg_arrow.png index fde514da6..daeb9fe65 100644 Binary files a/data/themes/default/send_bg_arrow.png and b/data/themes/default/send_bg_arrow.png differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index b68ef0658..572139dfc 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -245,6 +245,18 @@ nStateButton { min-width: 26px; } +/* track background colors */ +trackContentWidget { + qproperty-darkerColor1: rgb( 50, 50, 50 ); + qproperty-darkerColor2: rgb( 20, 20, 20 ); + qproperty-darkerColor3: rgb( 15, 15, 15 ); + qproperty-lighterColor1: rgb( 50, 50, 50 ); + qproperty-lighterColor2: rgb( 40, 40, 40 ); + qproperty-lighterColor3: rgb( 30, 30, 30 ); + qproperty-gradMidPoint: 0.33; +} + + /* gear button in tracks */ trackOperationsWidget > QPushButton { @@ -472,16 +484,27 @@ TrackContainerView QLabel /* instrument pattern */ patternView { color: rgb( 119, 199, 216 ); + qproperty-fgColor: rgb( 187, 227, 236 ); + qproperty-textColor: rgb( 255, 255, 255 ); } /* sample track pattern */ SampleTCOView { color: rgb( 74, 253, 133 ); + qproperty-fgColor: rgb( 187, 227, 236 ); + qproperty-textColor: rgb( 255, 60, 60 ); } /* automation pattern */ AutomationPatternView { color: #99afff; + qproperty-fgColor: rgb( 204, 215, 255 ); + qproperty-textColor: rgb( 255, 255, 255 ); +} + +/* bb-pattern */ +bbTCOView { + qproperty-textColor: rgb( 255, 255, 255 ); } /* Plugins */ @@ -644,6 +667,16 @@ WatsynView knob#xtalkKnob { qproperty-outerColor: #fb50fb; } +MonstroView knob { + color: #ffffff; + qproperty-outerColor: #aaaaaa; + qproperty-outerRadius: 9; + qproperty-innerRadius: 6; + qproperty-centerPointX: 10; + qproperty-centerPointY: 10; + qproperty-lineWidth: 2.5; +} + /* palette information - each colour definition must be on a single line, and the line must begin with "palette:", with no leading whitespace * colour codes MUST be of the form #RRGGBB */ diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 5d8bed1d4..73b2a7d9e 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -33,11 +33,15 @@ class AutomationPattern; class AutomationPatternView : public trackContentObjectView { Q_OBJECT + +// theming qproperties + Q_PROPERTY( QColor fgColor READ fgColor WRITE setFgColor ) + Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) + public: AutomationPatternView( AutomationPattern * _pat, trackView * _parent ); virtual ~AutomationPatternView(); - public slots: virtual void update(); @@ -67,7 +71,6 @@ private: bool m_needsUpdate; void scaleTimemapToFit( float oldMin, float oldMax ); - } ; diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 181956ea9..d10e9a2d5 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -46,6 +46,11 @@ class EXPORT SampleBuffer : public QObject, public sharedObject { Q_OBJECT public: + enum LoopMode { + LoopOff = 0, + LoopOn, + LoopPingPong + }; class EXPORT handleState { public: @@ -62,11 +67,21 @@ public: m_frameIndex = _index; } + inline bool isBackwards() const + { + return m_isBackwards; + } + + inline void setBackwards( bool _backwards ) + { + m_isBackwards = _backwards; + } private: f_cnt_t m_frameIndex; const bool m_varyingPitch; + bool m_isBackwards; SRC_STATE * m_resamplingData; friend class SampleBuffer; @@ -86,7 +101,7 @@ public: bool play( sampleFrame * _ab, handleState * _state, const fpp_t _frames, const float _freq, - const bool _looped = false ); + const LoopMode _loopmode = LoopOff ); void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ); inline void visualize( QPainter & _p, const QRect & _dr, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ) @@ -108,7 +123,7 @@ public: { return m_endFrame; } - + inline f_cnt_t loopStartFrame() const { return m_loopStartFrame; @@ -133,6 +148,16 @@ public: m_varLock.unlock(); } + void setAllPointFrames( f_cnt_t _start, f_cnt_t _end, f_cnt_t _loopstart, f_cnt_t _loopend ) + { + m_varLock.lock(); + m_startFrame = _start; + m_endFrame = _end; + m_loopStartFrame = _loopstart; + m_loopEndFrame = _loopend; + m_varLock.unlock(); + } + inline f_cnt_t frames() const { return m_frames; @@ -207,23 +232,13 @@ public: inline sample_t userWaveSample( const float _sample ) const { - // Precise implementation -// const float frame = fraction( _sample ) * m_frames; -// const f_cnt_t f1 = static_cast( frame ); -// const f_cnt_t f2 = ( f1 + 1 ) % m_frames; -// sample_t waveSample = linearInterpolate( m_data[f1][0], -// m_data[f2][0], -// fraction( frame ) ); -// return waveSample; - - // Fast implementation const float frame = _sample * m_frames; f_cnt_t f1 = static_cast( frame ) % m_frames; if( f1 < 0 ) { f1 += m_frames; } - return m_data[f1][0]; + return linearInterpolate( m_data[f1][0], m_data[ (f1 + 1) % m_frames ][0], fraction( frame ) ); } static QString tryToMakeRelative( const QString & _file ); @@ -245,7 +260,7 @@ private: void convertIntToFloat ( int_sample_t * & _ibuf, f_cnt_t _frames, int _channels); void directFloatWrite ( sample_t * & _fbuf, f_cnt_t _frames, int _channels); - f_cnt_t decodeSampleSF( const char * _f, int_sample_t * & _buf, + f_cnt_t decodeSampleSF( const char * _f, sample_t * & _buf, ch_cnt_t & _channels, sample_rate_t & _sample_rate ); #ifdef LMMS_HAVE_OGGVORBIS @@ -272,10 +287,13 @@ private: float m_frequency; sample_rate_t m_sampleRate; - sampleFrame * getSampleFragment( f_cnt_t _start, f_cnt_t _frames, - bool _looped, - sampleFrame * * _tmp ) const; - f_cnt_t getLoopedIndex( f_cnt_t _index ) const; + sampleFrame * getSampleFragment( f_cnt_t _index, f_cnt_t _frames, + LoopMode _loopmode, + sampleFrame * * _tmp, + bool * _backwards, f_cnt_t _loopstart, f_cnt_t _loopend, + f_cnt_t _end ) const; + f_cnt_t getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; + f_cnt_t getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; signals: diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 2afd9285b..a2e8f5b75 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -88,6 +88,11 @@ signals: class SampleTCOView : public trackContentObjectView { Q_OBJECT + +// theming qproperties + Q_PROPERTY( QColor fgColor READ fgColor WRITE setFgColor ) + Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) + public: SampleTCOView( SampleTCO * _tco, trackView * _tv ); virtual ~SampleTCOView(); @@ -108,7 +113,6 @@ protected: private: SampleTCO * m_tco; - } ; diff --git a/include/aeffectx.h b/include/aeffectx.h index 4188bf41a..852ac72fe 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -113,16 +113,24 @@ const int effGetVstVersion = 58; // currently unused const int kEffectMagic = CCONST( 'V', 's', 't', 'P' ); const int kVstLangEnglish = 1; const int kVstMidiType = 1; -const int kVstParameterUsesFloatStep = 1 << 2; -const int kVstPpqPosValid = 1 << 9; -const int kVstTempoValid = 1 << 10; -const int kVstBarsValid = 1 << 11; -const int kVstCyclePosValid = 1 << 12; -const int kVstTimeSigValid = 1 << 13; + const int kVstTransportPlaying = 1 << 1; const int kVstTransportCycleActive = 1 << 2; const int kVstTransportChanged = 1; +/* validity flags for a VstTimeInfo structure, this info comes from the web */ + +const int kVstNanosValid (1 << 8); +const int kVstPpqPosValid (1 << 9); +const int kVstTempoValid (1 << 10); +const int kVstBarsValid (1 << 11); +const int kVstCyclePosValid (1 << 12); +const int kVstTimeSigValid (1 << 13); +const int kVstSmpteValid (1 << 14); +const int kVstClockValid (1 << 15); + + + class RemoteVstPlugin; @@ -180,43 +188,50 @@ public: } ; - - -// Not finished, neither really used -class VstParameterProperties +/* constants from http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=3740&sid=183f74631fee71a493316735e2b9f28b */ +enum Vestige2StringConstants { -public: -/* float stepFloat; - char label[64]; - int flags; - int minInteger; - int maxInteger; - int stepInteger; - char shortLabel[8]; - int category; - char categoryLabel[24]; - char empty[128];*/ - - float stepFloat; - float smallStepFloat; - float largeStepFloat; - char label[64]; - unsigned int flags; - unsigned int minInteger; - unsigned int maxInteger; - unsigned int stepInteger; - unsigned int largeStepInteger; - char shortLabel[8]; - unsigned short displayIndex; - unsigned short category; - unsigned short numParametersInCategory; - unsigned short reserved; - char categoryLabel[24]; - char future[16]; - -} ; + VestigeMaxNameLen = 64, + VestigeMaxLabelLen = 64, + VestigeMaxShortLabelLen = 8, + VestigeMaxCategLabelLen = 24, + VestigeMaxFileNameLen = 100 +}; +/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +struct VstParameterProperties +{ + float stepFloat; /* float step */ + float smallStepFloat; /* small float step */ + float largeStepFloat; /* large float step */ + char label[VestigeMaxLabelLen]; /* parameter label */ + int32_t flags; /* @see VstParameterFlags */ + int32_t minInteger; /* integer minimum */ + int32_t maxInteger; /* integer maximum */ + int32_t stepInteger; /* integer step */ + int32_t largeStepInteger; /* large integer step */ + char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */ + int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */ + int16_t category; /* 0: no category, else group index + 1 */ + int16_t numParametersInCategory; /* number of parameters in category */ + int16_t reserved; /* zero */ + char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */ + char future[16]; /* reserved for future use */ +}; + + +/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +enum VstParameterFlags +{ + kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ + kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */ + kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */ + kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */ + kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */ + kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */ + kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */ +}; class AEffect @@ -274,7 +289,7 @@ public: // 08 double sampleRate; // unconfirmed 10 - char empty1[8]; + double nanoSeconds; // 18 double ppqPos; // 20? @@ -286,13 +301,15 @@ public: // 38? double cycleEndPos; // 40? - int timeSigNumerator; + int32_t timeSigNumerator; // 44? - int timeSigDenominator; - // unconfirmed 48 4c 50 - char empty3[4 + 4 + 4]; + int32_t timeSigDenominator; + + int32_t smpteOffset; + int32_t smpteFrameRate; + int32_t samplesToNextClock; // 54 - int flags; + int32_t flags; } ; diff --git a/include/interpolation.h b/include/interpolation.h index 4ff582775..43c50e102 100644 --- a/include/interpolation.h +++ b/include/interpolation.h @@ -23,8 +23,8 @@ */ -#ifndef _INTERPOLATION_H -#define _INTERPOLATION_H +#ifndef INTERPOLATION_H +#define INTERPOLATION_H #ifndef __USE_XOPEN #define __USE_XOPEN @@ -80,14 +80,21 @@ inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x ) inline float cosinusInterpolate( float v0, float v1, float x ) { float f = cosf( x * ( F_PI_2 ) ); - return( v0*f + v1*( 1.0f-f ) ); + return( v1 - f * (v1-v0) ); +// return( v0*f + v1*( 1.0f-f ) ); } inline float linearInterpolate( float v0, float v1, float x ) { - return( v0*( 1.0f-x ) + v1*x ); +// take advantage of fma function if present in hardware + +#ifdef FP_FAST_FMAF + return fmaf( x, v1-v0, v0 ); +#else + return x * (v1-v0) + v0; +#endif } diff --git a/include/lmms_math.h b/include/lmms_math.h index 57454ac57..b82e589a7 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -26,6 +26,7 @@ #ifndef _LMMS_MATH_H #define _LMMS_MATH_H +#include #ifdef __INTEL_COMPILER @@ -106,5 +107,18 @@ static inline int fast_rand() +// source: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/ +static inline double fastPow( double a, double b ) +{ + union + { + double d; + int32_t x[2]; + } u = { a }; + u.x[1] = static_cast( b * ( u.x[1] - 1072632447 ) + 1072632447 ); + u.x[0] = 0; + return u.d; +} + #endif diff --git a/include/pattern.h b/include/pattern.h index c3985ff7c..2fff9697e 100644 --- a/include/pattern.h +++ b/include/pattern.h @@ -147,6 +147,10 @@ private: class patternView : public trackContentObjectView { Q_OBJECT + +// theming qproperties + Q_PROPERTY( QColor fgColor READ fgColor WRITE setFgColor ) + Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) public: patternView( pattern * _pattern, trackView * _parent ); virtual ~patternView(); @@ -185,7 +189,6 @@ private: pattern * m_pat; QPixmap m_paintPixmap; bool m_needsUpdate; - } ; diff --git a/include/timeline.h b/include/timeline.h index ff4661837..58322d747 100644 --- a/include/timeline.h +++ b/include/timeline.h @@ -149,7 +149,8 @@ protected: private: static QPixmap * s_timeLinePixmap; static QPixmap * s_posMarkerPixmap; - static QPixmap * s_loopPointPixmap; + static QPixmap * s_loopPointBeginPixmap; + static QPixmap * s_loopPointEndPixmap; static QPixmap * s_loopPointDisabledPixmap; AutoScrollStates m_autoScroll; diff --git a/include/track.h b/include/track.h index 47196e847..f1baa570d 100644 --- a/include/track.h +++ b/include/track.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "lmms_basics.h" #include "MidiTime.h" @@ -162,6 +163,11 @@ private: class trackContentObjectView : public selectableObject, public ModelView { Q_OBJECT + +// theming qproperties + Q_PROPERTY( QColor fgColor READ fgColor WRITE setFgColor ) + Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) + public: trackContentObjectView( trackContentObject * _tco, trackView * _tv ); virtual ~trackContentObjectView(); @@ -172,7 +178,11 @@ public: { return m_tco; } - +// qproperty access func + QColor fgColor() const; + QColor textColor() const; + void setFgColor( const QColor & _c ); + void setTextColor( const QColor & _c ); public slots: virtual bool close(); @@ -227,6 +237,9 @@ private: MidiTime m_oldTime;// used for undo/redo while mouse-button is pressed +// qproperty fields + QColor * m_fgColor; + QColor * m_textColor; } ; @@ -236,6 +249,18 @@ private: class trackContentWidget : public QWidget, public JournallingObject { Q_OBJECT + + // qproperties for track background gradients + Q_PROPERTY( QColor darkerColor1 READ darkerColor1 WRITE setDarkerColor1 ) + Q_PROPERTY( QColor darkerColor2 READ darkerColor2 WRITE setDarkerColor2 ) + Q_PROPERTY( QColor darkerColor3 READ darkerColor3 WRITE setDarkerColor3 ) + + Q_PROPERTY( QColor lighterColor1 READ lighterColor1 WRITE setLighterColor1 ) + Q_PROPERTY( QColor lighterColor2 READ lighterColor2 WRITE setLighterColor2 ) + Q_PROPERTY( QColor lighterColor3 READ lighterColor3 WRITE setLighterColor3 ) + + Q_PROPERTY( float gradMidPoint READ gradMidPoint WRITE setGradMidPoint ) + public: trackContentWidget( trackView * _parent ); virtual ~trackContentWidget(); @@ -255,6 +280,28 @@ public: MidiTime endPosition( const MidiTime & _pos_start ); + // qproperty access methods + + QColor darkerColor1() const; + QColor darkerColor2() const; + QColor darkerColor3() const; + + QColor lighterColor1() const; + QColor lighterColor2() const; + QColor lighterColor3() const; + + float gradMidPoint() const; + + void setDarkerColor1( const QColor & _c ); + void setDarkerColor2( const QColor & _c ); + void setDarkerColor3( const QColor & _c ); + + void setLighterColor1( const QColor & _c ); + void setLighterColor2( const QColor & _c ); + void setLighterColor3( const QColor & _c ); + + void setGradMidPoint( float _g ); + public slots: void update(); void changePosition( const MidiTime & _new_pos = MidiTime( -1 ) ); @@ -295,6 +342,14 @@ private: QPixmap m_background; + // qproperty fields + QColor * m_darkerColor1; + QColor * m_darkerColor2; + QColor * m_darkerColor3; + QColor * m_lighterColor1; + QColor * m_lighterColor2; + QColor * m_lighterColor3; + float m_gradMidPoint; } ; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b1247ce18..c5bf9175f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -12,6 +12,7 @@ ADD_SUBDIRECTORY(LadspaEffect) ADD_SUBDIRECTORY(lb302) #ADD_SUBDIRECTORY(lb303) ADD_SUBDIRECTORY(midi_import) +ADD_SUBDIRECTORY(monstro) ADD_SUBDIRECTORY(organic) ADD_SUBDIRECTORY(opl2) ADD_SUBDIRECTORY(papu) diff --git a/plugins/LadspaEffect/caps/interface.cc b/plugins/LadspaEffect/caps/interface.cc index d0289d934..96e3d9806 100644 --- a/plugins/LadspaEffect/caps/interface.cc +++ b/plugins/LadspaEffect/caps/interface.cc @@ -70,7 +70,7 @@ seed() extern "C" { __attribute__ ((constructor)) -void _init() +void caps_so_init() { DescriptorStub ** d = descriptors; @@ -126,7 +126,7 @@ void _init() } __attribute__ ((destructor)) -void _fini() +void caps_so_fini() { for (ulong i = 0; i < N; ++i) delete descriptors[i]; diff --git a/plugins/LadspaEffect/tap/tap_autopan.c b/plugins/LadspaEffect/tap/tap_autopan.c index 1eda204b0..387ae2b92 100644 --- a/plugins/LadspaEffect/tap/tap_autopan.c +++ b/plugins/LadspaEffect/tap/tap_autopan.c @@ -233,10 +233,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { +_init() { int i; char ** port_names; @@ -341,9 +341,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { +_fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_chorusflanger.c b/plugins/LadspaEffect/tap/tap_chorusflanger.c index 24301e242..c85b39439 100644 --- a/plugins/LadspaEffect/tap/tap_chorusflanger.c +++ b/plugins/LadspaEffect/tap/tap_chorusflanger.c @@ -436,10 +436,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -575,9 +575,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_deesser.c b/plugins/LadspaEffect/tap/tap_deesser.c index 583290e5c..b18f33b45 100644 --- a/plugins/LadspaEffect/tap/tap_deesser.c +++ b/plugins/LadspaEffect/tap/tap_deesser.c @@ -349,10 +349,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { int i; char ** port_names; @@ -471,9 +471,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_doubler.c b/plugins/LadspaEffect/tap/tap_doubler.c index ae64ac8c6..b84c1d583 100644 --- a/plugins/LadspaEffect/tap/tap_doubler.c +++ b/plugins/LadspaEffect/tap/tap_doubler.c @@ -574,10 +574,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -717,9 +717,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_dynamics_m.c b/plugins/LadspaEffect/tap/tap_dynamics_m.c index a3d83555f..6ea8d6263 100644 --- a/plugins/LadspaEffect/tap/tap_dynamics_m.c +++ b/plugins/LadspaEffect/tap/tap_dynamics_m.c @@ -516,10 +516,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -646,9 +646,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_dynamics_st.c b/plugins/LadspaEffect/tap/tap_dynamics_st.c index bab3b8b16..7a42fe1e8 100644 --- a/plugins/LadspaEffect/tap/tap_dynamics_st.c +++ b/plugins/LadspaEffect/tap/tap_dynamics_st.c @@ -696,10 +696,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -857,9 +857,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_echo.c b/plugins/LadspaEffect/tap/tap_echo.c index c1aed7ff8..e84cc2b51 100644 --- a/plugins/LadspaEffect/tap/tap_echo.c +++ b/plugins/LadspaEffect/tap/tap_echo.c @@ -436,10 +436,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -607,9 +607,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_eq.c b/plugins/LadspaEffect/tap/tap_eq.c index 90c5dd7ab..2c07cd004 100644 --- a/plugins/LadspaEffect/tap/tap_eq.c +++ b/plugins/LadspaEffect/tap/tap_eq.c @@ -494,7 +494,7 @@ run_adding_eq(LADSPA_Handle instance, unsigned long sample_count) { void -__attribute__((constructor)) _init() { + _init() { char **port_names; LADSPA_PortDescriptor *port_descriptors; @@ -765,7 +765,7 @@ __attribute__((constructor)) _init() { void -__attribute__((destructor)) _fini() { + _fini() { if (eqDescriptor) { free((LADSPA_PortDescriptor *)eqDescriptor->PortDescriptors); diff --git a/plugins/LadspaEffect/tap/tap_eqbw.c b/plugins/LadspaEffect/tap/tap_eqbw.c index 8135b1c93..3805d003a 100644 --- a/plugins/LadspaEffect/tap/tap_eqbw.c +++ b/plugins/LadspaEffect/tap/tap_eqbw.c @@ -596,7 +596,7 @@ run_adding_eq(LADSPA_Handle instance, unsigned long sample_count) { void -__attribute__((constructor)) _init() { + _init() { char **port_names; LADSPA_PortDescriptor *port_descriptors; @@ -955,7 +955,7 @@ __attribute__((constructor)) _init() { void -__attribute__((destructor)) _fini() { + _fini() { if (eqDescriptor) { free((LADSPA_PortDescriptor *)eqDescriptor->PortDescriptors); diff --git a/plugins/LadspaEffect/tap/tap_limiter.c b/plugins/LadspaEffect/tap/tap_limiter.c index 346d3c6ce..7cd829c7c 100644 --- a/plugins/LadspaEffect/tap/tap_limiter.c +++ b/plugins/LadspaEffect/tap/tap_limiter.c @@ -324,10 +324,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -423,9 +423,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_pinknoise.c b/plugins/LadspaEffect/tap/tap_pinknoise.c index 4a55628e4..5984ecee4 100644 --- a/plugins/LadspaEffect/tap/tap_pinknoise.c +++ b/plugins/LadspaEffect/tap/tap_pinknoise.c @@ -225,10 +225,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -325,9 +325,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_pitch.c b/plugins/LadspaEffect/tap/tap_pitch.c index d0f39d390..eec650682 100644 --- a/plugins/LadspaEffect/tap/tap_pitch.c +++ b/plugins/LadspaEffect/tap/tap_pitch.c @@ -418,10 +418,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { int i; char ** port_names; @@ -536,9 +536,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_reflector.c b/plugins/LadspaEffect/tap/tap_reflector.c index 9c1826f42..4e8e33aa6 100644 --- a/plugins/LadspaEffect/tap/tap_reflector.c +++ b/plugins/LadspaEffect/tap/tap_reflector.c @@ -378,10 +378,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { int i; char ** port_names; @@ -480,9 +480,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_reverb.c b/plugins/LadspaEffect/tap/tap_reverb.c index c42c7ae8c..87afa28da 100644 --- a/plugins/LadspaEffect/tap/tap_reverb.c +++ b/plugins/LadspaEffect/tap/tap_reverb.c @@ -665,10 +665,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -815,9 +815,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_rotspeak.c b/plugins/LadspaEffect/tap/tap_rotspeak.c index 7ec435bd0..c16393c3f 100644 --- a/plugins/LadspaEffect/tap/tap_rotspeak.c +++ b/plugins/LadspaEffect/tap/tap_rotspeak.c @@ -624,10 +624,10 @@ LADSPA_Descriptor * stereo_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { int i; char ** port_names; @@ -748,9 +748,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(stereo_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_sigmoid.c b/plugins/LadspaEffect/tap/tap_sigmoid.c index 83c28cdd6..b1d33e843 100644 --- a/plugins/LadspaEffect/tap/tap_sigmoid.c +++ b/plugins/LadspaEffect/tap/tap_sigmoid.c @@ -228,10 +228,10 @@ cleanup_Sigmoid(LADSPA_Handle Instance) { LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -318,9 +318,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_tremolo.c b/plugins/LadspaEffect/tap/tap_tremolo.c index 820d35b91..a4a929574 100644 --- a/plugins/LadspaEffect/tap/tap_tremolo.c +++ b/plugins/LadspaEffect/tap/tap_tremolo.c @@ -220,10 +220,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -323,9 +323,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_tubewarmth.c b/plugins/LadspaEffect/tap/tap_tubewarmth.c index 4c66fd17c..42a0db495 100644 --- a/plugins/LadspaEffect/tap/tap_tubewarmth.c +++ b/plugins/LadspaEffect/tap/tap_tubewarmth.c @@ -381,10 +381,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { + _init() { char ** port_names; LADSPA_PortDescriptor * port_descriptors; @@ -471,9 +471,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { + _fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/LadspaEffect/tap/tap_vibrato.c b/plugins/LadspaEffect/tap/tap_vibrato.c index b7780b46a..40d14541e 100644 --- a/plugins/LadspaEffect/tap/tap_vibrato.c +++ b/plugins/LadspaEffect/tap/tap_vibrato.c @@ -308,10 +308,10 @@ LADSPA_Descriptor * mono_descriptor = NULL; -/* __attribute__((constructor)) _init() is called automatically when the plugin library is first +/* _init() is called automatically when the plugin library is first loaded. */ void -__attribute__((constructor)) _init() { +_init() { int i; char ** port_names; @@ -425,9 +425,9 @@ delete_descriptor(LADSPA_Descriptor * descriptor) { } -/* __attribute__((destructor)) _fini() is called automatically when the library is unloaded. */ +/* _fini() is called automatically when the library is unloaded. */ void -__attribute__((destructor)) _fini() { +_fini() { delete_descriptor(mono_descriptor); } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index a70fe52a4..e133a64ef 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -2,7 +2,7 @@ * audio_file_processor.cpp - instrument for using audio-files * * Copyright (c) 2004-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 @@ -76,9 +76,10 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : m_endPointModel( 1, 0, 1, 0.0000001f, this, tr( "End of sample" ) ), m_loopPointModel( 0, 0, 1, 0.0000001f, this, tr( "Loopback point" ) ), m_reverseModel( false, this, tr( "Reverse sample" ) ), - m_loopModel( false, this, tr( "Loop enabled" ) ), + m_loopModel( 0, 0, 2, this, tr( "Loop mode" ) ), m_stutterModel( false, this, tr( "Stutter" ) ), - m_nextPlayStartPoint( 0 ) + m_nextPlayStartPoint( 0 ), + m_nextPlayBackwards( false ) { connect( &m_reverseModel, SIGNAL( dataChanged() ), this, SLOT( reverseModelChanged() ) ); @@ -116,6 +117,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_stutterModel.value() == true && _n->frequency() < 20.0 ) { m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; return; } @@ -126,9 +128,11 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, // Restart playing the note if in stutter mode, not in loop mode, // and we're at the end of the sample. m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; } _n->m_pluginData = new handleState( _n->hasDetuningInfo() ); ((handleState *)_n->m_pluginData)->setFrameIndex( m_nextPlayStartPoint ); + ((handleState *)_n->m_pluginData)->setBackwards( m_nextPlayBackwards ); // debug code /* qDebug( "frames %d", m_sampleBuffer.frames() ); @@ -141,7 +145,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_sampleBuffer.play( _working_buffer, (handleState *)_n->m_pluginData, frames, _n->frequency(), - m_loopModel.value() ) ) + static_cast( m_loopModel.value() ) ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, @@ -161,6 +165,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( m_stutterModel.value() == true ) { m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex(); + m_nextPlayBackwards = ((handleState *)_n->m_pluginData)->isBackwards(); } } @@ -212,17 +217,18 @@ void audioFileProcessor::loadSettings( const QDomElement & _this ) m_reverseModel.loadSettings( _this, "reversed" ); m_loopModel.loadSettings( _this, "looped" ); m_ampModel.loadSettings( _this, "amp" ); - m_startPointModel.loadSettings( _this, "sframe" ); m_endPointModel.loadSettings( _this, "eframe" ); - + // compat code for not having a separate loopback point if( _this.hasAttribute( "lframe" ) ) { m_loopPointModel.loadSettings( _this, "lframe" ); + m_startPointModel.loadSettings( _this, "sframe" ); } else { - m_loopPointModel.setValue( m_startPointModel.value() ); + m_loopPointModel.loadSettings( _this, "sframe" ); + m_startPointModel.setValue( m_loopPointModel.value() ); } m_stutterModel.loadSettings( _this, "stutter" ); @@ -307,6 +313,7 @@ void audioFileProcessor::ampModelChanged( void ) void audioFileProcessor::stutterModelChanged() { m_nextPlayStartPoint = m_sampleBuffer.startFrame(); + m_nextPlayBackwards = false; } @@ -321,29 +328,36 @@ void audioFileProcessor::loopPointChanged( void ) m_endPointModel.setValue( m_startPointModel.value() ); m_startPointModel.setValue( tmp ); } - + // check if start & end overlap and nudge end up if so if( m_startPointModel.value() == m_endPointModel.value() ) { m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) ); } - - const f_cnt_t f_start = static_cast( m_startPointModel.value() * - ( m_sampleBuffer.frames()-1 ) ); - const f_cnt_t f_end = static_cast( m_endPointModel.value() * - ( m_sampleBuffer.frames()-1 ) ); - m_nextPlayStartPoint = f_start; - // check that loop point is between start-end points + // check that loop point is between start-end points and not overlapping with endpoint + // ...and move start/end points ahead if loop point is moved over them + if( m_loopPointModel.value() >= m_endPointModel.value() ) + { + m_endPointModel.setValue( m_loopPointModel.value() + 0.001f ); + if( m_endPointModel.value() == 1.0f ) + { + m_loopPointModel.setValue( 1.0f - 0.001f ); + } + } if( m_loopPointModel.value() < m_startPointModel.value() ) - m_loopPointModel.setValue( m_startPointModel.value() ); - if( m_loopPointModel.value() > m_endPointModel.value() ) - m_loopPointModel.setValue( m_endPointModel.value() ); + { + m_startPointModel.setValue( m_loopPointModel.value() ); + } - m_sampleBuffer.setStartFrame( f_start ); - m_sampleBuffer.setEndFrame( f_end ); - m_sampleBuffer.setLoopStartFrame( static_cast( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) ) ); - m_sampleBuffer.setLoopEndFrame( f_end ); + const f_cnt_t f_start = static_cast( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + const f_cnt_t f_end = static_cast( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + const f_cnt_t f_loop = static_cast( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) ); + + m_nextPlayStartPoint = f_start; + m_nextPlayBackwards = false; + + m_sampleBuffer.setAllPointFrames( f_start, f_end, f_loop, f_end ); emit dataChanged(); } @@ -377,13 +391,13 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, m_openAudioFileButton->setWhatsThis( tr( "Click here, if you want to open another audio-file. " "A dialog will appear where you can select your file. " - "Settings like looping-mode, start and end-points, " + "Settings like looping-mode, start and end-points, " "amplify-value, and so on are not reset. So, it may not " "sound like the original sample.") ); m_reverseButton = new pixmapButton( this ); m_reverseButton->setCheckable( TRUE ); - m_reverseButton->move( 174, 124 ); + m_reverseButton->move( 200, 124 ); m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "reverse_on" ) ); m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( @@ -394,18 +408,51 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "This is useful for cool effects, e.g. a reversed " "crash." ) ); - m_loopButton = new pixmapButton( this ); - m_loopButton->setCheckable( TRUE ); - m_loopButton->move( 200, 124 ); - m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( - "loop_on" ) ); - m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( - "loop_off" ) ); - toolTip::add( m_loopButton, tr( "Enable loop" ) ); - m_loopButton->setWhatsThis( - tr( "This button enables looping. " +// loop button group + + pixmapButton * m_loopOffButton = new pixmapButton( this ); + m_loopOffButton->setCheckable( TRUE ); + m_loopOffButton->move( 174, 144 ); + m_loopOffButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_off_on" ) ); + m_loopOffButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_off_off" ) ); + toolTip::add( m_loopOffButton, tr( "Disable loop" ) ); + m_loopOffButton->setWhatsThis( + tr( "This button disables looping. " + "The sample plays only once from start to end. " ) ); + + + pixmapButton * m_loopOnButton = new pixmapButton( this ); + m_loopOnButton->setCheckable( TRUE ); + m_loopOnButton->move( 200, 144 ); + m_loopOnButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_on_on" ) ); + m_loopOnButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_on_off" ) ); + toolTip::add( m_loopOnButton, tr( "Enable loop" ) ); + m_loopOnButton->setWhatsThis( + tr( "This button enables forwards-looping. " "The sample loops between the end point and the loop point." ) ); - + + pixmapButton * m_loopPingPongButton = new pixmapButton( this ); + m_loopPingPongButton->setCheckable( TRUE ); + m_loopPingPongButton->move( 226, 144 ); + m_loopPingPongButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_pingpong_on" ) ); + m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_pingpong_off" ) ); + toolTip::add( m_loopPingPongButton, tr( "Enable loop" ) ); + m_loopPingPongButton->setWhatsThis( + tr( "This button enables ping-pong-looping. " + "The sample loops backwards and forwards between the end point " + "and the loop point." ) ); + + m_loopGroup = new automatableButtonGroup( this ); + m_loopGroup->addButton( m_loopOffButton ); + m_loopGroup->addButton( m_loopOnButton ); + m_loopGroup->addButton( m_loopPingPongButton ); + m_stutterButton = new pixmapButton( this ); m_stutterButton->setCheckable( true ); m_stutterButton->move( 226, 124 ); @@ -604,7 +651,7 @@ void AudioFileProcessorView::modelChanged( void ) m_endKnob->setModel( &a->m_endPointModel ); m_loopKnob->setModel( &a->m_loopPointModel ); m_reverseButton->setModel( &a->m_reverseModel ); - m_loopButton->setModel( &a->m_loopModel ); + m_loopGroup->setModel( &a->m_loopModel ); m_stutterButton->setModel( &a->m_stutterModel ); sampleUpdated(); } @@ -675,16 +722,18 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) { m_isDragging = true; m_draggingLastPoint = _me->pos(); - - const int start_dist = qAbs( m_startFrameX - _me->x() ); - const int end_dist = qAbs( m_endFrameX - _me->x() ); - const int loop_dist = qAbs( m_loopFrameX - _me->x() ); - + + const int x = _me->x(); + + const int start_dist = qAbs( m_startFrameX - x ); + const int end_dist = qAbs( m_endFrameX - x ); + const int loop_dist = qAbs( m_loopFrameX - x ); + draggingType dt = sample_loop; int md = loop_dist; if( start_dist < loop_dist ) { dt = sample_start; md = start_dist; } - if( end_dist < start_dist ) { dt = sample_end; md = end_dist; } - - if( md < 3 ) + else if( end_dist < loop_dist ) { dt = sample_end; md = end_dist; } + + if( md < 4 ) { m_draggingType = dt; } @@ -780,7 +829,6 @@ void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe ) p.drawPixmap( s_padding, s_padding, m_graph ); - p.setPen( QColor( 0xFF, 0xFF, 0xFF ) ); //TODO: put into a qproperty const QRect graph_rect( s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding ); const f_cnt_t frames = m_to - m_from; m_startFrameX = graph_rect.x() + ( m_sampleBuffer.startFrame() - m_from ) * @@ -792,17 +840,21 @@ void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe ) const int played_width_px = ( m_framesPlayed - m_from ) * double( graph_rect.width() ) / frames; + // loop point line + p.setPen( QColor( 0x7F, 0xFF, 0xFF ) ); //TODO: put into a qproperty + p.drawLine( m_loopFrameX, graph_rect.y(), + m_loopFrameX, + graph_rect.height() + graph_rect.y() ); + + // start/end lines + p.setPen( QColor( 0xFF, 0xFF, 0xFF ) ); //TODO: put into a qproperty p.drawLine( m_startFrameX, graph_rect.y(), m_startFrameX, graph_rect.height() + graph_rect.y() ); p.drawLine( m_endFrameX, graph_rect.y(), m_endFrameX, graph_rect.height() + graph_rect.y() ); - - p.setPen( QColor( 0x7F, 0xFF, 0xFF ) ); //TODO: put into a qproperty - p.drawLine( m_loopFrameX, graph_rect.y(), - m_loopFrameX, - graph_rect.height() + graph_rect.y() ); + if( m_endFrameX - m_startFrameX > 2 ) { @@ -1006,7 +1058,7 @@ void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end, knob * _l m_endKnob->setWaveView( this ); m_endKnob->setRelatedKnob( m_startKnob ); - + m_loopKnob->setWaveView( this ); } @@ -1130,7 +1182,7 @@ bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const { return true; } - + if( ( m_relatedKnob->model()->value() - _v > 0 ) != ( m_relatedKnob->model()->value() - model()->value() >= 0 ) ) return false; diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index 0fd0f97f3..1f0558ab5 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -34,6 +34,7 @@ #include "SampleBuffer.h" #include "knob.h" #include "pixmap_button.h" +#include "automatable_button.h" @@ -91,10 +92,11 @@ private: FloatModel m_endPointModel; FloatModel m_loopPointModel; BoolModel m_reverseModel; - BoolModel m_loopModel; + IntModel m_loopModel; BoolModel m_stutterModel; f_cnt_t m_nextPlayStartPoint; + bool m_nextPlayBackwards; friend class AudioFileProcessorView; @@ -137,7 +139,7 @@ private: pixmapButton * m_openAudioFileButton; pixmapButton * m_reverseButton; - pixmapButton * m_loopButton; + automatableButtonGroup * m_loopGroup; pixmapButton * m_stutterButton; } ; @@ -270,7 +272,7 @@ private: static bool isCloseTo( int _a, int _b ) { - return qAbs( _a - _b ) < 3; + return qAbs( _a - _b ) < 4; } } ; diff --git a/plugins/audio_file_processor/loop_off_off.png b/plugins/audio_file_processor/loop_off_off.png new file mode 100644 index 000000000..a66e94d99 Binary files /dev/null and b/plugins/audio_file_processor/loop_off_off.png differ diff --git a/plugins/audio_file_processor/loop_off_on.png b/plugins/audio_file_processor/loop_off_on.png new file mode 100644 index 000000000..4cf0edf2e Binary files /dev/null and b/plugins/audio_file_processor/loop_off_on.png differ diff --git a/plugins/audio_file_processor/loop_off.png b/plugins/audio_file_processor/loop_on_off.png similarity index 100% rename from plugins/audio_file_processor/loop_off.png rename to plugins/audio_file_processor/loop_on_off.png diff --git a/plugins/audio_file_processor/loop_on.png b/plugins/audio_file_processor/loop_on_on.png similarity index 100% rename from plugins/audio_file_processor/loop_on.png rename to plugins/audio_file_processor/loop_on_on.png diff --git a/plugins/audio_file_processor/loop_pingpong_off.png b/plugins/audio_file_processor/loop_pingpong_off.png new file mode 100644 index 000000000..6758d2c79 Binary files /dev/null and b/plugins/audio_file_processor/loop_pingpong_off.png differ diff --git a/plugins/audio_file_processor/loop_pingpong_on.png b/plugins/audio_file_processor/loop_pingpong_on.png new file mode 100644 index 000000000..f21112c7a Binary files /dev/null and b/plugins/audio_file_processor/loop_pingpong_on.png differ diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index 852115afb..682f470a6 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -37,6 +37,7 @@ #include "templates.h" #include "tooltip.h" #include "song.h" +#include "interpolation.h" #include "embed.cpp" @@ -108,9 +109,9 @@ sample_t bSynth::nextStringSample() } // Nachkommaanteil - float frac = sample_realindex - static_cast(sample_realindex); + const float frac = fraction( sample_realindex ); - sample = sample_shape[a]*(1-frac) + sample_shape[b]*(frac); + sample = linearInterpolate( sample_shape[a], sample_shape[b], frac ); } else { // No interpolation diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index e5bea78d8..c3eb4eb35 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -25,7 +25,9 @@ #include "dynamics_processor.h" -#include +#include "lmms_math.h" +#include "interpolation.h" + #include "embed.cpp" #ifdef LMMS_BUILD_WIN32 @@ -107,8 +109,7 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, // variables for effect int i = 0; - float lookup; - float frac; + float sm_peak[2] = { 0.0f, 0.0f }; float gain; @@ -182,22 +183,20 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, for ( i=0; i <= 1; i++ ) { - if( sm_peak[i] != 0 ) + const int lookup = static_cast( sm_peak[i] * 200.0f ); + const float frac = fraction( sm_peak[i] * 200.0f ); + + if( sm_peak[i] > 0 ) { - lookup = sm_peak[i] * 200.0f; - if ( lookup < 1 ) { - frac = lookup - truncf(lookup); gain = frac * m_dpControls.m_wavegraphModel.samples()[0]; } else if ( lookup < 200 ) { - frac = lookup - truncf(lookup); - gain = - (( (1.0f-frac) * m_dpControls.m_wavegraphModel.samples()[ (int)truncf(lookup) - 1 ] ) + - ( frac * m_dpControls.m_wavegraphModel.samples()[ (int)truncf(lookup) ] )); + gain = linearInterpolate( m_dpControls.m_wavegraphModel.samples()[ lookup - 1 ], + m_dpControls.m_wavegraphModel.samples()[ lookup ], frac ); } else { @@ -206,8 +205,6 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, s[i] *= ( gain / sm_peak[i] ); } - else { s[i] = 0.0f; } - } // apply output gain diff --git a/plugins/kicker/KickerOsc.h b/plugins/kicker/KickerOsc.h index edcdcf5e5..6fdabdb1d 100644 --- a/plugins/kicker/KickerOsc.h +++ b/plugins/kicker/KickerOsc.h @@ -29,7 +29,7 @@ #include "DspEffectLibrary.h" #include "Oscillator.h" -#include "fastpow.h" +#include "lmms_math.h" template diff --git a/plugins/kicker/fastpow.h b/plugins/kicker/fastpow.h deleted file mode 100644 index 48fd83c03..000000000 --- a/plugins/kicker/fastpow.h +++ /dev/null @@ -1,22 +0,0 @@ - -#ifndef FASTPOW_H -#define FASTPOW_H - -#include - -/* - * source: - * http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/ - */ - -double fastPow(double a, double b) { - union { - double d; - int32_t x[2]; - } u = { a }; - u.x[1] = (int32_t)(b * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - return u.d; -} - -#endif diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 5bbd35a10..e694d3cf9 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -69,7 +69,8 @@ kickerInstrument::kickerInstrument( InstrumentTrack * _instrument_track ) : m_clickModel( 0.4f, 0.0f, 1.0f, 0.05f, this, tr( "Click" ) ), m_slopeModel( 0.06f, 0.001f, 1.0f, 0.001f, this, tr( "Slope" ) ), m_startNoteModel( false, this, tr( "Start from note" ) ), - m_endNoteModel( false, this, tr( "End to note" ) ) + m_endNoteModel( false, this, tr( "End to note" ) ), + m_versionModel( 0, 0, KICKER_PRESET_VERSION, this, "" ) { } @@ -97,6 +98,7 @@ void kickerInstrument::saveSettings( QDomDocument & _doc, m_slopeModel.saveSettings( _doc, _this, "slope" ); m_startNoteModel.saveSettings( _doc, _this, "startnote" ); m_endNoteModel.saveSettings( _doc, _this, "endnote" ); + m_versionModel.saveSettings( _doc, _this, "version" ); } @@ -115,6 +117,17 @@ void kickerInstrument::loadSettings( const QDomElement & _this ) m_slopeModel.loadSettings( _this, "slope" ); m_startNoteModel.loadSettings( _this, "startnote" ); m_endNoteModel.loadSettings( _this, "endnote" ); + m_versionModel.loadSettings( _this, "version" ); + + // Try to maintain backwards compatibility + if( !_this.hasAttribute( "version" ) ) + { + m_decayModel.setValue( m_decayModel.value() * 1.33f ); + m_envModel.setValue( 1.0f ); + m_slopeModel.setValue( 1.0f ); + m_clickModel.setValue( 0.0f ); + m_versionModel.setValue( KICKER_PRESET_VERSION ); + } } diff --git a/plugins/kicker/kicker.h b/plugins/kicker/kicker.h index 8253becf8..8e58a42e0 100644 --- a/plugins/kicker/kicker.h +++ b/plugins/kicker/kicker.h @@ -34,6 +34,9 @@ #include "led_checkbox.h" +#define KICKER_PRESET_VERSION 1 + + class kickerInstrumentView; class NotePlayHandle; @@ -80,6 +83,8 @@ private: BoolModel m_startNoteModel; BoolModel m_endNoteModel; + IntModel m_versionModel; + friend class kickerInstrumentView; } ; diff --git a/plugins/monstro/CMakeLists.txt b/plugins/monstro/CMakeLists.txt new file mode 100644 index 000000000..6a9195b75 --- /dev/null +++ b/plugins/monstro/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(monstro Monstro.cpp Monstro.h MOCFILES Monstro.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp new file mode 100644 index 000000000..465f0b859 --- /dev/null +++ b/plugins/monstro/Monstro.cpp @@ -0,0 +1,1828 @@ +/* + * Monstro.cpp - a monstrous semi-modular 3-osc synth with modulation matrix + * + * Copyright (c) 2014 Vesa Kivimäki + * + * 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 "Monstro.h" +#include "engine.h" +#include "InstrumentTrack.h" +#include "templates.h" +#include "gui_templates.h" +#include "tooltip.h" +#include "song.h" +#include "lmms_math.h" +#include "interpolation.h" + +#include "embed.cpp" + + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Monstro", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Monstrous 3-oscillator synth with modulation matrix" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Instrument, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + +} + + + + +MonstroSynth::MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph, + const sample_rate_t _samplerate, fpp_t _frames ) : + m_parent( _i ), + m_nph( _nph ), + m_samplerate( _samplerate ), + m_fpp( _frames ) +{ + m_env1_buf = new sample_t[_frames]; + m_env2_buf = new sample_t[_frames]; + m_lfo1_buf = new sample_t[_frames]; + m_lfo2_buf = new sample_t[_frames]; + + m_osc1l_phase = 0.0f; + m_osc1r_phase = 0.0f; + m_osc2l_phase = 0.0f; + m_osc2r_phase = 0.0f; + m_osc3l_phase = 0.0f; + m_osc3r_phase = 0.0f; + + m_env1_phase = 0.0f; + m_env2_phase = 0.0f; + + m_lfo1_phase = 0.0f; + m_lfo2_phase = 0.0f; + + m_osc1l_last = 0.0f; + m_osc1r_last = 0.0f; + + m_osc2l_last = 0.0f; + m_osc2r_last = 0.0f; + + m_osc3l_last = 0.0f; + m_osc3r_last = 0.0f; + + m_l_last = 0.0f; + m_r_last = 0.0f; + +// constants for very simple antialias/bandlimiting by amp delta capping + m_adcap1 = ADCAP1 / m_samplerate; + m_adcap2 = ADCAP2 / m_samplerate; +} + + +MonstroSynth::~MonstroSynth() +{ + delete[] m_env1_buf; + delete[] m_env2_buf; + delete[] m_lfo1_buf; + delete[] m_lfo2_buf; + +} + + +void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) +{ +// macros for modulating with env/lfos +#define modulatefreq( car, mod ) \ + if( mod##_e1 != 0.0f ) car = qBound( MIN_FREQ, car * powf( 2.0f, m_env1_buf[f] * mod##_e1 * 2.0f ), MAX_FREQ ); \ + if( mod##_e2 != 0.0f ) car = qBound( MIN_FREQ, car * powf( 2.0f, m_env2_buf[f] * mod##_e2 * 2.0f ), MAX_FREQ ); \ + if( mod##_l1 != 0.0f ) car = qBound( MIN_FREQ, car * powf( 2.0f, m_lfo1_buf[f] * mod##_l1 ), MAX_FREQ ); \ + if( mod##_l2 != 0.0f ) car = qBound( MIN_FREQ, car * powf( 2.0f, m_lfo2_buf[f] * mod##_l2 ), MAX_FREQ ); + +#define modulateabs( car, mod ) \ + if( mod##_e1 != 0.0f ) car = qBound( 0.0f, car + mod##_e1 * m_env1_buf[f], 1.0f ); \ + if( mod##_e2 != 0.0f ) car = qBound( 0.0f, car + mod##_e2 * m_env2_buf[f], 1.0f ); \ + if( mod##_l1 != 0.0f ) car = qBound( 0.0f, car + mod##_l1 * 0.5f * m_lfo1_buf[f], 1.0f ); \ + if( mod##_l2 != 0.0f ) car = qBound( 0.0f, car + mod##_l2 * 0.5f * m_lfo2_buf[f], 1.0f ); + +#define modulatephs( car, mod ) \ + if( mod##_e1 != 0.0f ) car += ( mod##_e1 * m_env1_buf[f] ); \ + if( mod##_e2 != 0.0f ) car += ( mod##_e2 * m_env2_buf[f] ); \ + if( mod##_l1 != 0.0f ) car += ( mod##_l1 * 0.5f * m_lfo1_buf[f] ); \ + if( mod##_l2 != 0.0f ) car += ( mod##_l2 * 0.5f * m_lfo2_buf[f] ); + +#define modulatevol( car, mod ) \ + if( mod##_e1 > 0.0f ) car = qBound( 0.0f, car * ( 1.0f - mod##_e1 + mod##_e1 * m_env1_buf[f] ), MODCLIP ); \ + if( mod##_e1 < 0.0f ) car = qBound( 0.0f, car * ( 1.0f + mod##_e1 * m_env1_buf[f] ), MODCLIP ); \ + if( mod##_e2 > 0.0f ) car = qBound( 0.0f, car * ( 1.0f - mod##_e2 + mod##_e2 * m_env2_buf[f] ), MODCLIP ); \ + if( mod##_e2 < 0.0f ) car = qBound( 0.0f, car * ( 1.0f + mod##_e2 * m_env2_buf[f] ), MODCLIP ); \ + if( mod##_l1 != 0.0f ) car = qBound( 0.0f, car * ( 1.0f + mod##_l1 * m_lfo1_buf[f] ), MODCLIP ); \ + if( mod##_l2 != 0.0f ) car = qBound( 0.0f, car * ( 1.0f + mod##_l2 * m_lfo2_buf[f] ), MODCLIP ); + + // pre-render env's and lfo's + renderModulators( _frames ); + + // get updated osc1 values + // get pulse width + const float pw = ( m_parent->m_osc1Pw.value() / 100.0f ); + const float o1pw_e1 = ( m_parent->m_pw1env1.value() ); + const float o1pw_e2 = ( m_parent->m_pw1env2.value() ); + const float o1pw_l1 = ( m_parent->m_pw1lfo1.value() ); + const float o1pw_l2 = ( m_parent->m_pw1lfo2.value() ); + + // get phases + const float o1lpo = m_parent->m_osc1l_po; + const float o1rpo = m_parent->m_osc1r_po; + const float o1p_e1 = ( m_parent->m_phs1env1.value() ); + const float o1p_e2 = ( m_parent->m_phs1env2.value() ); + const float o1p_l1 = ( m_parent->m_phs1lfo1.value() ); + const float o1p_l2 = ( m_parent->m_phs1lfo2.value() ); + + // get pitch + const float o1lfb = ( m_parent->m_osc1l_freq * m_nph->frequency() ); + const float o1rfb = ( m_parent->m_osc1r_freq * m_nph->frequency() ); + const float o1f_e1 = ( m_parent->m_pit1env1.value() ); + const float o1f_e2 = ( m_parent->m_pit1env2.value() ); + const float o1f_l1 = ( m_parent->m_pit1lfo1.value() ); + const float o1f_l2 = ( m_parent->m_pit1lfo2.value() ); + + // get volumes + const float o1lv = m_parent->m_osc1l_vol; + const float o1rv = m_parent->m_osc1r_vol; + const float o1v_e1 = ( m_parent->m_vol1env1.value() ); + const float o1v_e2 = ( m_parent->m_vol1env2.value() ); + const float o1v_l1 = ( m_parent->m_vol1lfo1.value() ); + const float o1v_l2 = ( m_parent->m_vol1lfo2.value() ); + + // update osc2 + // get waveform + const int o2w = m_parent->m_osc2Wave.value(); + + // get phases + const float o2lpo = m_parent->m_osc2l_po; + const float o2rpo = m_parent->m_osc2r_po; + const float o2p_e1 = ( m_parent->m_phs2env1.value() ); + const float o2p_e2 = ( m_parent->m_phs2env2.value() ); + const float o2p_l1 = ( m_parent->m_phs2lfo1.value() ); + const float o2p_l2 = ( m_parent->m_phs2lfo2.value() ); + + // get pitch + const float o2lfb = ( m_parent->m_osc2l_freq * m_nph->frequency() ); + const float o2rfb = ( m_parent->m_osc2r_freq * m_nph->frequency() ); + const float o2f_e1 = ( m_parent->m_pit2env1.value() ); + const float o2f_e2 = ( m_parent->m_pit2env2.value() ); + const float o2f_l1 = ( m_parent->m_pit2lfo1.value() ); + const float o2f_l2 = ( m_parent->m_pit2lfo2.value() ); + + // get volumes + const float o2lv = m_parent->m_osc2l_vol; + const float o2rv = m_parent->m_osc2r_vol; + const float o2v_e1 = ( m_parent->m_vol2env1.value() ); + const float o2v_e2 = ( m_parent->m_vol2env2.value() ); + const float o2v_l1 = ( m_parent->m_vol2lfo2.value() ); + const float o2v_l2 = ( m_parent->m_vol2lfo2.value() ); + + + + // update osc3 + // get waveforms + const int o3w1 = m_parent->m_osc3Wave1.value(); + const int o3w2 = m_parent->m_osc3Wave2.value(); + + // get phases + const float o3lpo = m_parent->m_osc3l_po; + const float o3rpo = m_parent->m_osc3r_po; + const float o3p_e1 = ( m_parent->m_phs3env1.value() ); + const float o3p_e2 = ( m_parent->m_phs3env2.value() ); + const float o3p_l1 = ( m_parent->m_phs3lfo1.value() ); + const float o3p_l2 = ( m_parent->m_phs3lfo2.value() ); + + // get pitch modulators + const float o3fb = ( m_parent->m_osc3_freq * m_nph->frequency() ); + const float o3f_e1 = ( m_parent->m_pit3env1.value() ); + const float o3f_e2 = ( m_parent->m_pit3env2.value() ); + const float o3f_l1 = ( m_parent->m_pit3lfo1.value() ); + const float o3f_l2 = ( m_parent->m_pit3lfo2.value() ); + + // get volumes + const float o3lv = m_parent->m_osc3l_vol; + const float o3rv = m_parent->m_osc3r_vol; + const float o3v_e1 = ( m_parent->m_vol3env1.value() ); + const float o3v_e2 = ( m_parent->m_vol3env2.value() ); + const float o3v_l1 = ( m_parent->m_vol3lfo1.value() ); + const float o3v_l2 = ( m_parent->m_vol3lfo2.value() ); + + // get sub + const float o3sub = ( m_parent->m_osc3Sub.value() + 100.0f ) / 200.0f; + const float o3s_e1 = ( m_parent->m_sub3env1.value() ); + const float o3s_e2 = ( m_parent->m_sub3env2.value() ); + const float o3s_l1 = ( m_parent->m_sub3lfo1.value() ); + const float o3s_l2 = ( m_parent->m_sub3lfo2.value() ); + + + //o2-o3 modulation + + const int omod = m_parent->m_o23Mod.value(); + + // sync information + + const bool o1ssr = m_parent->m_osc1SSR.value(); + const bool o1ssf = m_parent->m_osc1SSF.value(); + const bool o2sync = m_parent->m_osc2Sync.value(); + const bool o3sync = m_parent->m_osc3Sync.value(); + + + /////////////////////////// + // // + // start buffer loop // + // // + /////////////////////////// + + + // declare variables for for loop + // osc1 vars + float o1l_f; + float o1r_f; + float o1l_p; + float o1r_p; + float o1_pw; + + // osc2 vars + float o2l_f; + float o2r_f; + float o2l_p; + float o2r_p; + + // osc3 vars + float o3l_f; + float o3r_f; + float o3l_p; + float o3r_p; + float sub; + + // begin for loop + for( f_cnt_t f = 0; f < _frames; f++ ) + { + + +/* // debug code + if( f % 10 == 0 ) { + qDebug( "env1 %f -- env1 phase %f", m_env1_buf[f], m_env1_phase ); + qDebug( "env1 pre %f att %f dec %f rel %f ", m_parent->m_env1_pre, m_parent->m_env1_att, + m_parent->m_env1_dec, m_parent->m_env1_rel ); + }*/ + + + ///////////////////////////// + // // + // OSC 1 // + // // + ///////////////////////////// + + // calc and mod frequencies + o1l_f = o1lfb; + o1r_f = o1rfb; + modulatefreq( o1l_f, o1f ) + modulatefreq( o1r_f, o1f ) + + // calc and modulate phase + o1l_p = m_osc1l_phase + o1lpo; + o1r_p = m_osc1r_phase + o1rpo; + modulatephs( o1l_p, o1p ) + modulatephs( o1r_p, o1p ) + + // calc and modulate pulse + o1_pw = pw; + modulateabs( o1_pw, o1pw ) + + // bounds check for phase + if( o1l_p < 0 ) o1l_p -= floorf( o1l_p ); + if( o1r_p < 0 ) o1r_p -= floorf( o1r_p ); + + // pulse wave osc + sample_t O1L = ( fraction( o1l_p ) < o1_pw ) ? 1.0f : -1.0f; + sample_t O1R = ( fraction( o1r_p ) < o1_pw ) ? 1.0f : -1.0f; + + // check for rise/fall, and sync if appropriate + // sync on rise + if( o1ssr ) + { + if( o2sync ) + { + if( O1L > m_osc1l_last ) m_osc2l_phase = 0.0f; + if( O1R > m_osc1r_last ) m_osc2r_phase = 0.0f; + } + if( o3sync ) + { + if( O1L > m_osc1l_last ) m_osc3l_phase = 0.0f; + if( O1R > m_osc1r_last ) m_osc3r_phase = 0.0f; + } + } + // sync on fall + if( o1ssf ) + { + if( o2sync ) + { + if( O1L < m_osc1l_last ) m_osc2l_phase = 0.0f; + if( O1R < m_osc1r_last ) m_osc2r_phase = 0.0f; + } + if( o3sync ) + { + if( O1L < m_osc1l_last ) m_osc3l_phase = 0.0f; + if( O1R < m_osc1r_last ) m_osc3r_phase = 0.0f; + } + } + + // update last before signal is touched + // also do a very simplistic amp delta cap - it's a pulse wave signal so we won't care about it too much... + const sample_t tmpl = m_osc1l_last; + const sample_t tmpr = m_osc1r_last; + + m_osc1l_last = O1L; + m_osc1r_last = O1R; + + if( tmpl != O1L ) O1L = 0.0f; + if( tmpr != O1R ) O1R = 0.0f; + + // modulate volume + O1L *= o1lv; + O1R *= o1rv; + modulatevol( O1L, o1v ) + modulatevol( O1R, o1v ) + + // update osc1 phases + m_osc1l_phase = fraction( m_osc1l_phase + 1.0f / ( static_cast( m_samplerate ) / o1l_f ) ); + m_osc1r_phase = fraction( m_osc1r_phase + 1.0f / ( static_cast( m_samplerate ) / o1r_f ) ); + + ///////////////////////////// + // // + // OSC 2 // + // // + ///////////////////////////// + + // calc and mod frequencies + o2l_f = o2lfb; + o2r_f = o2rfb; + modulatefreq( o2l_f, o2f ) + modulatefreq( o2r_f, o2f ) + + // calc and modulate phase + o2l_p = m_osc2l_phase + o2lpo; + o2r_p = m_osc2r_phase + o2rpo; + modulatephs( o2l_p, o2p ) + modulatephs( o2r_p, o2p ) + + // bounds check for phase + if( o2l_p < 0 ) o2l_p -= floorf( o2l_p ); + if( o2r_p < 0 ) o2r_p -= floorf( o2r_p ); + + // multi-wave DC Oscillator + sample_t O2L = oscillate( o2w, o2l_p ); + sample_t O2R = oscillate( o2w, o2r_p ); + + // do simple alias reduction filtering before volume is touched, by capping amplitude delta + O2L = qBound( m_osc2l_last - m_adcap1, O2L, m_osc2l_last + m_adcap1 ); + O2R = qBound( m_osc2r_last - m_adcap1, O2R, m_osc2r_last + m_adcap1 ); + m_osc2l_last = O2L; + m_osc2r_last = O2R; + + // modulate volume + O2L *= o2lv; + O2R *= o2rv; + modulatevol( O2L, o2v ) + modulatevol( O2R, o2v ) + + // update osc2 phases + m_osc2l_phase = fraction( m_osc2l_phase + 1.0f / ( static_cast( m_samplerate ) / o2l_f ) ); + m_osc2r_phase = fraction( m_osc2r_phase + 1.0f / ( static_cast( m_samplerate ) / o2r_f ) ); + + ///////////////////////////// + // // + // OSC 3 // + // // + ///////////////////////////// + + // calc and mod frequencies + o3l_f = o3fb; + o3r_f = o3fb; + modulatefreq( o3l_f, o3f ) + modulatefreq( o3r_f, o3f ) + // o2 modulation? + if( omod == MOD_FM ) + { + o3l_f = qBound( MIN_FREQ, o3l_f * powf( 2.0f, O2L * 2 ), MAX_FREQ ); + o3r_f = qBound( MIN_FREQ, o3r_f * powf( 2.0f, O2R * 2 ), MAX_FREQ ); + } + + // calc and modulate phase + o3l_p = m_osc3l_phase + o3lpo; + o3r_p = m_osc3r_phase + o3rpo; + modulatephs( o3l_p, o3p ) + modulatephs( o3r_p, o3p ) + // o2 modulation? + if( omod == MOD_PM ) + { + o3l_p = fraction( o3l_p + O2L/2 ); + o3r_p = fraction( o3r_p + O2R/2 ); + } + + // bounds check for phase + if( o3l_p < 0 ) o3l_p -= floorf( o3l_p ); + if( o3r_p < 0 ) o3r_p -= floorf( o3r_p ); + + // multi-wave DC Oscillator, sub-osc 1 + sample_t O3AL = oscillate( o3w1, o3l_p ); + sample_t O3AR = oscillate( o3w1, o3r_p ); + + // multi-wave DC Oscillator, sub-osc 2 + sample_t O3BL = oscillate( o3w2, o3l_p ); + sample_t O3BR = oscillate( o3w2, o3r_p ); + + // calc and modulate sub + sub = o3sub; + modulateabs( sub, o3s ) + + sample_t O3L = linearInterpolate( O3AL, O3BL, sub ); + sample_t O3R = linearInterpolate( O3AR, O3BR, sub ); + + // do very simple bandlimit filtering by amp delta capping, before volume is touched + O3L = qBound( m_osc3l_last - m_adcap1, O3L, m_osc3l_last + m_adcap1 ); + O3R = qBound( m_osc3r_last - m_adcap1, O3R, m_osc3r_last + m_adcap1 ); + m_osc3l_last = O3L; + m_osc3r_last = O3R; + + // modulate volume + O3L *= o3lv; + O3R *= o3rv; + modulatevol( O3L, o3v ) + modulatevol( O3R, o3v ) + // o2 modulation? + if( omod == MOD_AM ) + { + O3L = qBound( -MODCLIP, O3L * qMax( 0.0f, 1.0f + O2L ), MODCLIP ); + O3R = qBound( -MODCLIP, O3R * qMax( 0.0f, 1.0f + O2R ), MODCLIP ); + } + + // update osc3 phases + m_osc3l_phase = fraction( m_osc3l_phase + 1.0f / ( static_cast( m_samplerate ) / o3l_f ) ); + m_osc3r_phase = fraction( m_osc3r_phase + 1.0f / ( static_cast( m_samplerate ) / o3r_f ) ); + + // simple bandlimiting + sample_t L = O1L + O3L + ( omod == MOD_MIX ? O2L : 0.0f ); + sample_t R = O1R + O3R + ( omod == MOD_MIX ? O2R : 0.0f ); + + L = qBound( m_l_last - m_adcap2, L, m_l_last + m_adcap2 ); + R = qBound( m_r_last - m_adcap2, R, m_r_last + m_adcap2 ); + + _buf[f][0] = L; + _buf[f][1] = R; + + m_l_last = L; + m_r_last = R; + } +} + + +void MonstroSynth::renderModulators( fpp_t _frames ) +{ + // LFO phase offsets + const float lfo1_po = m_parent->m_lfo1Phs.value() / 360.0f; + const float lfo2_po = m_parent->m_lfo2Phs.value() / 360.0f; + + // remove cruft from phase counters to prevent overflow + m_lfo1_phase = fraction( m_lfo1_phase ); + m_lfo2_phase = fraction( m_lfo2_phase ); + + // LFO rates + const float lfo1_r = m_parent->m_lfo1Rate.value() / 1000.0f * m_samplerate; + const float lfo2_r = m_parent->m_lfo2Rate.value() / 1000.0f * m_samplerate; + + // frames played before + const f_cnt_t tfp = m_nph->totalFramesPlayed(); + + // LFOs + sample_t lfo1_s; + sample_t lfo2_s; + + // LFO 1 + + switch( m_parent->m_lfo1Wave.value() ) + { + case WAVE_SINE: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sinSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent-> m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_TRI: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::triangleSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_SAW: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sawSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_RAMP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sawSample( ph ) * -1.0f; + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_SQR: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::squareSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_SQRSOFT: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = oscillate( WAVE_SQRSOFT, ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_MOOG: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::moogSawSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_SINABS: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = oscillate( WAVE_SINABS, ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_EXP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::expSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent-> m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + case WAVE_NOISE: + default: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + if( t % static_cast( lfo1_r ) == 0 ) m_lfo1_last = Oscillator::noiseSample( 0.0f ); + lfo1_s = m_lfo1_last; + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0f / lfo1_r; + } + break; + } + + // LFO 2 + + switch( m_parent->m_lfo2Wave.value() ) + { + case WAVE_SINE: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sinSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent-> m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_TRI: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::triangleSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_SAW: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sawSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_RAMP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sawSample( ph ) * -1.0f; + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_SQR: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::squareSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_SQRSOFT: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = oscillate( WAVE_SQRSOFT, ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_MOOG: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::moogSawSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_SINABS: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = oscillate( WAVE_SINABS, ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_EXP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::expSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent-> m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + case WAVE_NOISE: + default: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + if( t % static_cast( lfo2_r ) == 0 ) m_lfo2_last = Oscillator::noiseSample( 0.0f ); + lfo2_s = m_lfo2_last; + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0f / lfo2_r; + } + break; + } + + ///////////////////////////////////////////// + // + // + // envelopes + // + // + ///////////////////////////////////////////// + + const float env1_s = m_parent-> m_env1Slope.value(); + const float env2_s = m_parent-> m_env2Slope.value(); + + const float env1_sus = m_parent-> m_env1Sus.value(); + const float env2_sus = m_parent-> m_env2Sus.value(); + + for( f_cnt_t f = 0; f < _frames; f++ ) + { + // envelope 1 + + // adjust phase for release + if( m_nph->isReleased() && m_env1_phase < 4.0f ) + { + if( m_env1_phase < 1.0f ) m_env1_phase = 5.0f; + else if( m_env1_phase < 2.0f ) m_env1_phase = 5.0f - fraction( m_env1_phase ); + else if( m_env1_phase < 3.0f ) m_env1_phase = 4.0f; + else m_env1_phase = 4.0f + fraction( m_env1_phase ); + } + + // process envelope + if( m_env1_phase < 1.0f ) // pre-delay phase + { + m_env1_buf[f] = 0.0f; + m_env1_phase = qMin( 1.0f, m_env1_phase + m_parent->m_env1_pre ); + } + else if( m_env1_phase < 2.0f ) // attack phase + { + m_env1_buf[f] = calcSlope( fraction( m_env1_phase ), env1_s ); + m_env1_phase = qMin( 2.0f, m_env1_phase + m_parent->m_env1_att ); + } + else if( m_env1_phase < 3.0f ) // hold phase + { + m_env1_buf[f] = 1.0f; + m_env1_phase = qMin( 3.0f, m_env1_phase + m_parent->m_env1_hold ); + } + else if( m_env1_phase < 4.0f ) // decay phase + { + const sample_t s = 1.0f - fraction( m_env1_phase ); + if( s <= env1_sus ) + { + m_env1_buf[f] = env1_sus; + } + else + { + m_env1_buf[f] = calcSlope( s, env1_s ); + m_env1_phase = qMin( 4.0f - env1_sus, m_env1_phase + m_parent->m_env1_dec ); + if( m_env1_phase == 4.0f ) m_env1_phase = 5.0f; // jump over release if sustain is zero - fix for clicking + } + } + else if( m_env1_phase < 5.0f ) // release phase + { + m_env1_buf[f] = calcSlope( 1.0f - fraction( m_env1_phase ), env1_s ); + m_env1_phase += m_parent->m_env1_rel; + } + else m_env1_buf[f] = 0.0f; + +// qDebug( "env1 %f", m_env1_buf[f] ); + + // envelope 2 + + + + // adjust phase for release + if( m_nph->isReleased() && m_env2_phase < 4.0f ) + { + if( m_env2_phase < 1.0f ) m_env2_phase = 5.0f; + else if( m_env2_phase < 2.0f ) m_env2_phase = 5.0f - fraction( m_env2_phase ); + else if( m_env2_phase < 3.0f ) m_env2_phase = 4.0f; + else m_env2_phase = 4.0f + fraction( m_env2_phase ); + } + + // process envelope + if( m_env2_phase < 1.0f ) // pre-delay phase + { + m_env2_buf[f] = 0.0f; + m_env2_phase = qMin( 1.0f, m_env2_phase + m_parent->m_env2_pre ); + } + else if( m_env2_phase < 2.0f ) // attack phase + { + m_env2_buf[f] = calcSlope( fraction( m_env2_phase ), env2_s ); + m_env2_phase = qMin( 2.0f, m_env2_phase + m_parent->m_env2_att ); + } + else if( m_env2_phase < 3.0f ) // hold phase + { + m_env2_buf[f] = 1.0f; + m_env2_phase = qMin( 3.0f, m_env2_phase + m_parent->m_env2_hold ); + } + else if( m_env2_phase < 4.0f ) // decay phase + { + const sample_t s = 1.0f - fraction( m_env2_phase ); + if( s <= env2_sus ) + { + m_env2_buf[f] = env2_sus; + } + else + { + m_env2_buf[f] = calcSlope( s, env2_s ); + m_env2_phase = qMin( 4.0f - env2_sus, m_env2_phase + m_parent->m_env2_dec ); + if( m_env1_phase == 4.0f ) m_env1_phase = 5.0f; // jump over release if sustain is zero - fix for clicking + } + } + else if( m_env2_phase < 5.0f ) // release phase + { + m_env2_buf[f] = calcSlope( 1.0f - fraction( m_env2_phase), env2_s ); + m_env2_phase += m_parent->m_env2_rel; + } + else m_env2_buf[f] = 0.0f; + + } + +} + + + +MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : + Instrument( _instrument_track, &monstro_plugin_descriptor ), + + m_osc1Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 1 Volume" ) ), + m_osc1Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 1 Panning" ) ), + m_osc1Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 1 Coarse detune" ) ), + m_osc1Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 Fine detune left" ) ), + m_osc1Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 Fine detune right" ) ), + m_osc1Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 1 Stereo phase offset" ) ), + m_osc1Pw( 50.0, 0.0, 100.0, 0.01, this, tr( "Osc 1 Pulse width" ) ), + m_osc1SSR( false, this, tr( "Osc 1 Sync send on rise" ) ), + m_osc1SSF( false, this, tr( "Osc 1 Sync send on fall" ) ), + + m_osc2Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 2 Volume" ) ), + m_osc2Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 2 Panning" ) ), + m_osc2Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 2 Coarse detune" ) ), + m_osc2Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 Fine detune left" ) ), + m_osc2Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 Fine detune right" ) ), + m_osc2Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 2 Stereo phase offset" ) ), + m_osc2Wave( this, tr( "Osc 2 Waveform" ) ), + m_osc2Sync( false, this, tr( "Osc 2 Sync" ) ), + + m_osc3Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 3 Volume" ) ), + m_osc3Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 Panning" ) ), + m_osc3Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 3 Coarse detune" ) ), + m_osc3Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 3 Stereo phase offset" ) ), + m_osc3Sub( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 Sub-oscillator mix" ) ), + m_osc3Wave1( this, tr( "Osc 3 Waveform 1" ) ), + m_osc3Wave2( this, tr( "Osc 3 Waveform 2" ) ), + m_osc3Sync( false, this, tr( "Osc 3 Sync" ) ), + + m_lfo1Wave( this, tr( "LFO 1 Waveform" ) ), + m_lfo1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 1 Attack" ) ), + m_lfo1Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 1 Rate" ) ), + m_lfo1Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 1 Phase" ) ), + + m_lfo2Wave( this, tr( "LFO 2 Waveform" ) ), + m_lfo2Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 2 Attack" ) ), + m_lfo2Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 2 Rate" ) ), + m_lfo2Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 2 Phase" ) ), + + m_env1Pre( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Pre-delay" ) ), + m_env1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Attack" ) ), + m_env1Hold( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 1 Hold" ) ), + m_env1Dec( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 1 Decay" ) ), + m_env1Sus( 1.0f, 0.0f, 1.0f, 0.001f, this, tr( "Env 1 Sustain" ) ), + m_env1Rel( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 1 Release" ) ), + m_env1Slope( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Env 1 Slope" ) ), + + m_env2Pre( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Pre-delay" ) ), + m_env2Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Attack" ) ), + m_env2Hold( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 2 Hold" ) ), + m_env2Dec( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 2 Decay" ) ), + m_env2Sus( 1.0f, 0.0f, 1.0f, 0.001f, this, tr( "Env 2 Sustain" ) ), + m_env2Rel( 0.0f, 0.0f, 4000.0f, 1.0f, 4000.0f, this, tr( "Env 2 Release" ) ), + m_env2Slope( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Env 2 Slope" ) ), + + m_o23Mod( 0, 0, NUM_MODS - 1, this, tr( "Osc2-3 modulation" ) ), + + m_selectedView( 0, 0, 1, this, tr( "Selected view" ) ), + + m_vol1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-Env1" ) ), + m_vol1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-Env2" ) ), + m_vol1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-LFO1" ) ), + m_vol1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-LFO2" ) ), + + m_vol2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-Env1" ) ), + m_vol2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-Env2" ) ), + m_vol2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-LFO1" ) ), + m_vol2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-LFO2" ) ), + + m_vol3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-Env1" ) ), + m_vol3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-Env2" ) ), + m_vol3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-LFO1" ) ), + m_vol3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-LFO2" ) ), + + m_phs1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-Env1" ) ), + m_phs1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-Env2" ) ), + m_phs1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-LFO1" ) ), + m_phs1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-LFO2" ) ), + + m_phs2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-Env1" ) ), + m_phs2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-Env2" ) ), + m_phs2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-LFO1" ) ), + m_phs2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-LFO2" ) ), + + m_phs3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-Env1" ) ), + m_phs3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-Env2" ) ), + m_phs3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-LFO1" ) ), + m_phs3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-LFO2" ) ), + + m_pit1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-Env1" ) ), + m_pit1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-Env2" ) ), + m_pit1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-LFO1" ) ), + m_pit1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-LFO2" ) ), + + m_pit2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-Env1" ) ), + m_pit2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-Env2" ) ), + m_pit2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-LFO1" ) ), + m_pit2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-LFO2" ) ), + + m_pit3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-Env1" ) ), + m_pit3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-Env2" ) ), + m_pit3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-LFO1" ) ), + m_pit3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-LFO2" ) ), + + m_pw1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-Env1" ) ), + m_pw1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-Env2" ) ), + m_pw1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-LFO1" ) ), + m_pw1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-LFO2" ) ), + + m_sub3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-Env1" ) ), + m_sub3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-Env2" ) ), + m_sub3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-LFO1" ) ), + m_sub3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-LFO2" ) ) + +{ +// setup waveboxes + setwavemodel( m_osc2Wave ) + setwavemodel( m_osc3Wave1 ) + setwavemodel( m_osc3Wave2 ) + setlfowavemodel( m_lfo1Wave ) + setlfowavemodel( m_lfo2Wave ) + +// make connections: + +// updateVolumes + + connect( &m_osc1Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc1Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc2Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc2Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc3Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc3Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + +// updateFreq + + connect( &m_osc1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + + connect( &m_osc1Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + + connect( &m_osc1Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + +// updatePO + connect( &m_osc1Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + connect( &m_osc2Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + connect( &m_osc3Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + +// updateEnvelope1 + + connect( &m_env1Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + +// updateEnvelope2 + + connect( &m_env2Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + +// updateLFOAtts + + connect( &m_lfo1Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); + connect( &m_lfo2Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); + +// updateSampleRate + + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSamplerate() ) ); + + m_fpp = engine::mixer()->framesPerPeriod(); + + updateSamplerate(); + updateVolumes(); + updateFreq(); + updatePO(); +} + + +MonstroInstrument::~MonstroInstrument() +{ +} + + +void MonstroInstrument::playNote( NotePlayHandle * _n, + sampleFrame * _working_buffer ) +{ + if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) + { + const sample_rate_t samplerate = m_samplerate; + _n->m_pluginData = new MonstroSynth( this, _n, samplerate, m_fpp ); + } + + const fpp_t frames = _n->framesLeftForCurrentPeriod(); + + MonstroSynth * ms = static_cast( _n->m_pluginData ); + + ms->renderOutput( frames, _working_buffer ); + + applyRelease( _working_buffer, _n ); + + instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); +} + +void MonstroInstrument::deleteNotePluginData( NotePlayHandle * _n ) +{ + delete static_cast( _n->m_pluginData ); +} + + +void MonstroInstrument::saveSettings( QDomDocument & _doc, + QDomElement & _this ) +{ + m_osc1Vol.saveSettings( _doc, _this, "o1vol" ); + m_osc1Pan.saveSettings( _doc, _this, "o1pan" ); + m_osc1Crs.saveSettings( _doc, _this, "o1crs" ); + m_osc1Ftl.saveSettings( _doc, _this, "o1ftl" ); + m_osc1Ftr.saveSettings( _doc, _this, "o1ftr" ); + m_osc1Spo.saveSettings( _doc, _this, "o1spo" ); + m_osc1Pw.saveSettings( _doc, _this, "o1pw" ); + m_osc1SSR.saveSettings( _doc, _this, "o1ssr" ); + m_osc1SSF.saveSettings( _doc, _this, "o1ssf" ); + + m_osc2Vol.saveSettings( _doc, _this, "o2vol" ); + m_osc2Pan.saveSettings( _doc, _this, "o2pan" ); + m_osc2Crs.saveSettings( _doc, _this, "o2crs" ); + m_osc2Ftl.saveSettings( _doc, _this, "o2ftl" ); + m_osc2Ftr.saveSettings( _doc, _this, "o2ftr" ); + m_osc2Spo.saveSettings( _doc, _this, "o2spo" ); + m_osc2Wave.saveSettings( _doc, _this, "o2wav" ); + m_osc2Sync.saveSettings( _doc, _this, "o2syn" ); + + m_osc3Vol.saveSettings( _doc, _this, "o3vol" ); + m_osc3Pan.saveSettings( _doc, _this, "o3pan" ); + m_osc3Crs.saveSettings( _doc, _this, "o3crs" ); + m_osc3Spo.saveSettings( _doc, _this, "o3spo" ); + m_osc3Sub.saveSettings( _doc, _this, "o3sub" ); + m_osc3Wave1.saveSettings( _doc, _this, "o3wav1" ); + m_osc3Wave2.saveSettings( _doc, _this, "o3wav2" ); + m_osc3Sync.saveSettings( _doc, _this, "o3syn" ); + + m_lfo1Wave.saveSettings( _doc, _this, "l1wav" ); + m_lfo1Att.saveSettings( _doc, _this, "l1att" ); + m_lfo1Rate.saveSettings( _doc, _this, "l1rat" ); + m_lfo1Phs.saveSettings( _doc, _this, "l1phs" ); + + m_lfo2Wave.saveSettings( _doc, _this, "l2wav" ); + m_lfo2Att.saveSettings( _doc, _this, "l2att" ); + m_lfo2Rate.saveSettings( _doc, _this, "l2rat" ); + m_lfo2Phs.saveSettings( _doc, _this, "l2phs" ); + + m_env1Pre.saveSettings( _doc, _this, "e1pre" ); + m_env1Att.saveSettings( _doc, _this, "e1att" ); + m_env1Hold.saveSettings( _doc, _this, "e1hol" ); + m_env1Dec.saveSettings( _doc, _this, "e1dec" ); + m_env1Sus.saveSettings( _doc, _this, "e1sus" ); + m_env1Rel.saveSettings( _doc, _this, "e1rel" ); + m_env1Slope.saveSettings( _doc, _this, "e1slo" ); + + m_env2Pre.saveSettings( _doc, _this, "e2pre" ); + m_env2Att.saveSettings( _doc, _this, "e2att" ); + m_env2Hold.saveSettings( _doc, _this, "e2hol" ); + m_env2Dec.saveSettings( _doc, _this, "e2dec" ); + m_env2Sus.saveSettings( _doc, _this, "e2sus" ); + m_env2Rel.saveSettings( _doc, _this, "e2rel" ); + m_env2Slope.saveSettings( _doc, _this, "e2slo" ); + + m_o23Mod.saveSettings( _doc, _this, "o23mo" ); + + m_vol1env1.saveSettings( _doc, _this, "v1e1" ); + m_vol1env2.saveSettings( _doc, _this, "v1e2" ); + m_vol1lfo1.saveSettings( _doc, _this, "v1l1" ); + m_vol1lfo2.saveSettings( _doc, _this, "v1l2" ); + + m_vol2env1.saveSettings( _doc, _this, "v2e1" ); + m_vol2env2.saveSettings( _doc, _this, "v2e2" ); + m_vol2lfo1.saveSettings( _doc, _this, "v2l1" ); + m_vol2lfo2.saveSettings( _doc, _this, "v2l2" ); + + m_vol3env1.saveSettings( _doc, _this, "v3e1" ); + m_vol3env2.saveSettings( _doc, _this, "v3e2" ); + m_vol3lfo1.saveSettings( _doc, _this, "v3l1" ); + m_vol3lfo2.saveSettings( _doc, _this, "v3l2" ); + + m_phs1env1.saveSettings( _doc, _this, "p1e1" ); + m_phs1env2.saveSettings( _doc, _this, "p1e2" ); + m_phs1lfo1.saveSettings( _doc, _this, "p1l1" ); + m_phs1lfo2.saveSettings( _doc, _this, "p1l2" ); + + m_phs2env1.saveSettings( _doc, _this, "p2e1" ); + m_phs2env2.saveSettings( _doc, _this, "p2e2" ); + m_phs2lfo1.saveSettings( _doc, _this, "p2l1" ); + m_phs2lfo2.saveSettings( _doc, _this, "p2l2" ); + + m_phs3env1.saveSettings( _doc, _this, "p3e1" ); + m_phs3env2.saveSettings( _doc, _this, "p3e2" ); + m_phs3lfo1.saveSettings( _doc, _this, "p3l1" ); + m_phs3lfo2.saveSettings( _doc, _this, "p3l2" ); + + m_pit1env1.saveSettings( _doc, _this, "f1e1" ); + m_pit1env2.saveSettings( _doc, _this, "f1e2" ); + m_pit1lfo1.saveSettings( _doc, _this, "f1l1" ); + m_pit1lfo2.saveSettings( _doc, _this, "f1l2" ); + + m_pit2env1.saveSettings( _doc, _this, "f2e1" ); + m_pit2env2.saveSettings( _doc, _this, "f2e2" ); + m_pit2lfo1.saveSettings( _doc, _this, "f2l1" ); + m_pit2lfo2.saveSettings( _doc, _this, "f2l2" ); + + m_pit3env1.saveSettings( _doc, _this, "f3e1" ); + m_pit3env2.saveSettings( _doc, _this, "f3e2" ); + m_pit3lfo1.saveSettings( _doc, _this, "f3l1" ); + m_pit3lfo2.saveSettings( _doc, _this, "f3l2" ); + + m_pw1env1.saveSettings( _doc, _this, "w1e1" ); + m_pw1env2.saveSettings( _doc, _this, "w1e2" ); + m_pw1lfo1.saveSettings( _doc, _this, "w1l1" ); + m_pw1lfo2.saveSettings( _doc, _this, "w1l2" ); + + m_sub3env1.saveSettings( _doc, _this, "s3e1" ); + m_sub3env2.saveSettings( _doc, _this, "s3e2" ); + m_sub3lfo1.saveSettings( _doc, _this, "s3l1" ); + m_sub3lfo2.saveSettings( _doc, _this, "s3l2" ); + +} + +void MonstroInstrument::loadSettings( const QDomElement & _this ) +{ + m_osc1Vol.loadSettings( _this, "o1vol" ); + m_osc1Pan.loadSettings( _this, "o1pan" ); + m_osc1Crs.loadSettings( _this, "o1crs" ); + m_osc1Ftl.loadSettings( _this, "o1ftl" ); + m_osc1Ftr.loadSettings( _this, "o1ftr" ); + m_osc1Spo.loadSettings( _this, "o1spo" ); + m_osc1Pw.loadSettings( _this, "o1pw" ); + m_osc1SSR.loadSettings( _this, "o1ssr" ); + m_osc1SSF.loadSettings( _this, "o1ssf" ); + + m_osc2Vol.loadSettings( _this, "o2vol" ); + m_osc2Pan.loadSettings( _this, "o2pan" ); + m_osc2Crs.loadSettings( _this, "o2crs" ); + m_osc2Ftl.loadSettings( _this, "o2ftl" ); + m_osc2Ftr.loadSettings( _this, "o2ftr" ); + m_osc2Spo.loadSettings( _this, "o2spo" ); + m_osc2Wave.loadSettings( _this, "o2wav" ); + m_osc2Sync.loadSettings( _this, "o2syn" ); + + m_osc3Vol.loadSettings( _this, "o3vol" ); + m_osc3Pan.loadSettings( _this, "o3pan" ); + m_osc3Crs.loadSettings( _this, "o3crs" ); + m_osc3Spo.loadSettings( _this, "o3spo" ); + m_osc3Sub.loadSettings( _this, "o3sub" ); + m_osc3Wave1.loadSettings( _this, "o3wav1" ); + m_osc3Wave2.loadSettings( _this, "o3wav2" ); + m_osc3Sync.loadSettings( _this, "o3syn" ); + + m_lfo1Wave.loadSettings( _this, "l1wav" ); + m_lfo1Att.loadSettings( _this, "l1att" ); + m_lfo1Rate.loadSettings( _this, "l1rat" ); + m_lfo1Phs.loadSettings( _this, "l1phs" ); + + m_lfo2Wave.loadSettings( _this, "l2wav" ); + m_lfo2Att.loadSettings( _this, "l2att" ); + m_lfo2Rate.loadSettings( _this, "l2rat" ); + m_lfo2Phs.loadSettings( _this, "l2phs" ); + + m_env1Pre.loadSettings( _this, "e1pre" ); + m_env1Att.loadSettings( _this, "e1att" ); + m_env1Hold.loadSettings( _this, "e1hol" ); + m_env1Dec.loadSettings( _this, "e1dec" ); + m_env1Sus.loadSettings( _this, "e1sus" ); + m_env1Rel.loadSettings( _this, "e1rel" ); + m_env1Slope.loadSettings( _this, "e1slo" ); + + m_env2Pre.loadSettings( _this, "e2pre" ); + m_env2Att.loadSettings( _this, "e2att" ); + m_env2Hold.loadSettings( _this, "e2hol" ); + m_env2Dec.loadSettings( _this, "e2dec" ); + m_env2Sus.loadSettings( _this, "e2sus" ); + m_env2Rel.loadSettings( _this, "e2rel" ); + m_env2Slope.loadSettings( _this, "e2slo" ); + + m_o23Mod.loadSettings( _this, "o23mo" ); + + m_vol1env1.loadSettings( _this, "v1e1" ); + m_vol1env2.loadSettings( _this, "v1e2" ); + m_vol1lfo1.loadSettings( _this, "v1l1" ); + m_vol1lfo2.loadSettings( _this, "v1l2" ); + + m_vol2env1.loadSettings( _this, "v2e1" ); + m_vol2env2.loadSettings( _this, "v2e2" ); + m_vol2lfo1.loadSettings( _this, "v2l1" ); + m_vol2lfo2.loadSettings( _this, "v2l2" ); + + m_vol3env1.loadSettings( _this, "v3e1" ); + m_vol3env2.loadSettings( _this, "v3e2" ); + m_vol3lfo1.loadSettings( _this, "v3l1" ); + m_vol3lfo2.loadSettings( _this, "v3l2" ); + + m_phs1env1.loadSettings( _this, "p1e1" ); + m_phs1env2.loadSettings( _this, "p1e2" ); + m_phs1lfo1.loadSettings( _this, "p1l1" ); + m_phs1lfo2.loadSettings( _this, "p1l2" ); + + m_phs2env1.loadSettings( _this, "p2e1" ); + m_phs2env2.loadSettings( _this, "p2e2" ); + m_phs2lfo1.loadSettings( _this, "p2l1" ); + m_phs2lfo2.loadSettings( _this, "p2l2" ); + + m_phs3env1.loadSettings( _this, "p3e1" ); + m_phs3env2.loadSettings( _this, "p3e2" ); + m_phs3lfo1.loadSettings( _this, "p3l1" ); + m_phs3lfo2.loadSettings( _this, "p3l2" ); + + m_pit1env1.loadSettings( _this, "f1e1" ); + m_pit1env2.loadSettings( _this, "f1e2" ); + m_pit1lfo1.loadSettings( _this, "f1l1" ); + m_pit1lfo2.loadSettings( _this, "f1l2" ); + + m_pit2env1.loadSettings( _this, "f2e1" ); + m_pit2env2.loadSettings( _this, "f2e2" ); + m_pit2lfo1.loadSettings( _this, "f2l1" ); + m_pit2lfo2.loadSettings( _this, "f2l2" ); + + m_pit3env1.loadSettings( _this, "f3e1" ); + m_pit3env2.loadSettings( _this, "f3e2" ); + m_pit3lfo1.loadSettings( _this, "f3l1" ); + m_pit3lfo2.loadSettings( _this, "f3l2" ); + + m_pw1env1.loadSettings( _this, "w1e1" ); + m_pw1env2.loadSettings( _this, "w1e2" ); + m_pw1lfo1.loadSettings( _this, "w1l1" ); + m_pw1lfo2.loadSettings( _this, "w1l2" ); + + m_sub3env1.loadSettings( _this, "s3e1" ); + m_sub3env2.loadSettings( _this, "s3e2" ); + m_sub3lfo1.loadSettings( _this, "s3l1" ); + m_sub3lfo2.loadSettings( _this, "s3l2" ); + +} + + +QString MonstroInstrument::nodeName() const +{ + return monstro_plugin_descriptor.name; +} + + +f_cnt_t MonstroInstrument::desiredReleaseFrames() const +{ + return 64; +} + + +PluginView * MonstroInstrument::instantiateView( QWidget * _parent ) +{ + return( new MonstroView( this, _parent ) ); +} + + +void MonstroInstrument::updateVolumes() +{ + m_osc1l_vol = leftCh( m_osc1Vol.value(), m_osc1Pan.value() ); + m_osc1r_vol = rightCh( m_osc1Vol.value(), m_osc1Pan.value() ); + + m_osc2l_vol = leftCh( m_osc2Vol.value(), m_osc2Pan.value() ); + m_osc2r_vol = rightCh( m_osc2Vol.value(), m_osc2Pan.value() ); + + m_osc3l_vol = leftCh( m_osc3Vol.value(), m_osc3Pan.value() ); + m_osc3r_vol = rightCh( m_osc3Vol.value(), m_osc3Pan.value() ); +} + + +void MonstroInstrument::updateFreq() +{ + m_osc1l_freq = powf( 2.0f, m_osc1Crs.value() / 12.0f ) * + powf( 2.0f, m_osc1Ftl.value() / 1200.0f ); + m_osc1r_freq = powf( 2.0f, m_osc1Crs.value() / 12.0f ) * + powf( 2.0f, m_osc1Ftr.value() / 1200.0f ); + + m_osc2l_freq = powf( 2.0f, m_osc2Crs.value() / 12.0f ) * + powf( 2.0f, m_osc2Ftl.value() / 1200.0f ); + m_osc2r_freq = powf( 2.0f, m_osc2Crs.value() / 12.0f ) * + powf( 2.0f, m_osc2Ftr.value() / 1200.0f ); + + m_osc3_freq = powf( 2.0f, m_osc3Crs.value() / 12.0f ); +} + + +void MonstroInstrument::updatePO() +{ + m_osc1l_po = m_osc1Spo.value() / 720.0; + m_osc1r_po = ( m_osc1Spo.value() * -1.0 ) / 720.0; + + m_osc2l_po = m_osc2Spo.value() / 720.0; + m_osc2r_po = ( m_osc2Spo.value() * -1.0 ) / 720.0; + + m_osc3l_po = m_osc3Spo.value() / 720.0; + m_osc3r_po = ( m_osc3Spo.value() * -1.0 ) / 720.0; +} + +void MonstroInstrument::updateEnvelope1() +{ + if( m_env1Pre.value() == 0.0f ) m_env1_pre = 1.0; + else m_env1_pre = 1.0f / ( m_env1Pre.value() / 1000.0f ) / m_samplerate; + if( m_env1Att.value() == 0.0f ) m_env1_att = 1.0; + else m_env1_att = 1.0f / ( m_env1Att.value() / 1000.0f ) / m_samplerate; + if( m_env1Hold.value() == 0.0f ) m_env1_hold = 1.0; + else m_env1_hold = 1.0f / ( m_env1Hold.value() / 1000.0f ) / m_samplerate; + if( m_env1Dec.value() == 0.0f ) m_env1_dec = 1.0; + else m_env1_dec = 1.0f / ( m_env1Dec.value() / 1000.0f ) / m_samplerate; + if( m_env1Rel.value() == 0.0f ) m_env1_rel = 1.0; + else m_env1_rel = 1.0f / ( m_env1Rel.value() / 1000.0f ) / m_samplerate; + + m_env1_len = ( m_env1Pre.value() + m_env1Att.value() + m_env1Hold.value() + m_env1Dec.value() ) * m_samplerate / 1000.0f; + m_env1_relF = m_env1Rel.value() * m_samplerate / 1000.0f; +} +void MonstroInstrument::updateEnvelope2() +{ + if( m_env2Pre.value() == 0.0f ) m_env2_pre = 1.0; + else m_env2_pre = 1.0f / ( m_env2Pre.value() / 1000.0f ) / m_samplerate; + if( m_env2Att.value() == 0.0f ) m_env2_att = 1.0; + else m_env2_att = 1.0f / ( m_env2Att.value() / 1000.0f ) / m_samplerate; + if( m_env2Hold.value() == 0.0f ) m_env2_hold = 1.0; + else m_env2_hold = 1.0f / ( m_env2Hold.value() / 1000.0f ) / m_samplerate; + if( m_env2Dec.value() == 0.0f ) m_env2_dec = 1.0; + else m_env2_dec = 1.0f / ( m_env2Dec.value() / 1000.0f ) / m_samplerate; + if( m_env2Rel.value() == 0.0f ) m_env2_rel = 1.0; + else m_env2_rel = 1.0f / ( m_env2Rel.value() / 1000.0f ) / m_samplerate; + + m_env2_len = ( m_env2Pre.value() + m_env2Att.value() + m_env2Hold.value() + m_env2Dec.value() ) * m_samplerate / 1000.0f; + m_env2_relF = m_env2Rel.value() * m_samplerate / 1000.0f; +} + + +void MonstroInstrument::updateLFOAtts() +{ + m_lfo1_att = m_lfo1Att.value() * m_samplerate / 1000.0f; + m_lfo2_att = m_lfo2Att.value() * m_samplerate / 1000.0f; +} + + +void MonstroInstrument::updateSamplerate() +{ + m_samplerate = engine::mixer()->processingSampleRate(); + updateEnvelope1(); + updateEnvelope2(); + updateLFOAtts(); +} + + +MonstroView::MonstroView( Instrument * _instrument, + QWidget * _parent ) : + InstrumentView( _instrument, _parent ) +{ + m_operatorsView = setupOperatorsView( this ); + setWidgetBackground( m_operatorsView, "artwork_op" ); + m_operatorsView->show(); + m_operatorsView->move( 0, 0 ); + + m_matrixView = setupMatrixView( this ); + setWidgetBackground( m_matrixView, "artwork_mat" ); + m_matrixView->hide(); + m_matrixView->move( 0, 0 ); + +// "tab buttons" + + pixmapButton * m_opViewButton = new pixmapButton( this, NULL ); + m_opViewButton -> move( 0,0 ); + m_opViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_active" ) ); + m_opViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_inactive" ) ); + toolTip::add( m_opViewButton, tr( "Operators view" ) ); + + pixmapButton * m_matViewButton = new pixmapButton( this, NULL ); + m_matViewButton -> move( 125,0 ); + m_matViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_active" ) ); + m_matViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_inactive" ) ); + toolTip::add( m_matViewButton, tr( "Matrix view" ) ); + + m_selectedViewGroup = new automatableButtonGroup( this ); + m_selectedViewGroup -> addButton( m_opViewButton ); + m_selectedViewGroup -> addButton( m_matViewButton ); + + connect( m_opViewButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); + connect( m_matViewButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); +} + + +MonstroView::~MonstroView() +{ +} + + +void MonstroView::updateLayout() +{ + switch( m_selectedViewGroup->model()->value() ) + { + case OPVIEW: + m_operatorsView->show(); + m_matrixView->hide(); + break; + case MATVIEW: + m_operatorsView->hide(); + m_matrixView->show(); + break; + } +} + + +void MonstroView::modelChanged() +{ + MonstroInstrument * m = castModel(); + + m_osc1VolKnob-> setModel( &m-> m_osc1Vol ); + m_osc1PanKnob-> setModel( &m-> m_osc1Pan ); + m_osc1CrsKnob-> setModel( &m-> m_osc1Crs ); + m_osc1FtlKnob-> setModel( &m-> m_osc1Ftl ); + m_osc1FtrKnob-> setModel( &m-> m_osc1Ftr ); + m_osc1SpoKnob-> setModel( &m-> m_osc1Spo ); + m_osc1PwKnob-> setModel( &m-> m_osc1Pw ); + m_osc1SSRButton-> setModel( &m-> m_osc1SSR ); + m_osc1SSFButton-> setModel( &m-> m_osc1SSF ); + + m_osc2VolKnob-> setModel( &m-> m_osc2Vol ); + m_osc2PanKnob-> setModel( &m-> m_osc2Pan ); + m_osc2CrsKnob-> setModel( &m-> m_osc2Crs ); + m_osc2FtlKnob-> setModel( &m-> m_osc2Ftl ); + m_osc2FtrKnob-> setModel( &m-> m_osc2Ftr ); + m_osc2SpoKnob-> setModel( &m-> m_osc2Spo ); + m_osc2WaveBox-> setModel( &m-> m_osc2Wave ); + m_osc2SyncButton-> setModel( &m-> m_osc2Sync ); + + m_osc3VolKnob-> setModel( &m-> m_osc3Vol ); + m_osc3PanKnob-> setModel( &m-> m_osc3Pan ); + m_osc3CrsKnob-> setModel( &m-> m_osc3Crs ); + m_osc3SpoKnob-> setModel( &m-> m_osc3Spo ); + m_osc3SubKnob-> setModel( &m-> m_osc3Sub ); + m_osc3Wave1Box-> setModel( &m-> m_osc3Wave1 ); + m_osc3Wave2Box-> setModel( &m-> m_osc3Wave2 ); + m_osc3SyncButton-> setModel( &m-> m_osc3Sync ); + + m_lfo1WaveBox-> setModel( &m-> m_lfo1Wave ); + m_lfo1AttKnob-> setModel( &m-> m_lfo1Att ); + m_lfo1RateKnob-> setModel( &m-> m_lfo1Rate ); + m_lfo1PhsKnob-> setModel( &m-> m_lfo1Phs ); + + m_lfo2WaveBox-> setModel( &m-> m_lfo2Wave ); + m_lfo2AttKnob-> setModel( &m-> m_lfo2Att ); + m_lfo2RateKnob-> setModel( &m-> m_lfo2Rate ); + m_lfo2PhsKnob-> setModel( &m-> m_lfo2Phs ); + + m_env1PreKnob-> setModel( &m-> m_env1Pre ); + m_env1AttKnob-> setModel( &m-> m_env1Att ); + m_env1HoldKnob-> setModel( &m-> m_env1Hold ); + m_env1DecKnob-> setModel( &m-> m_env1Dec ); + m_env1SusKnob-> setModel( &m-> m_env1Sus ); + m_env1RelKnob-> setModel( &m-> m_env1Rel ); + m_env1SlopeKnob-> setModel( &m-> m_env1Slope ); + + m_env2PreKnob-> setModel( &m-> m_env2Pre ); + m_env2AttKnob-> setModel( &m-> m_env2Att ); + m_env2HoldKnob-> setModel( &m-> m_env2Hold ); + m_env2DecKnob-> setModel( &m-> m_env2Dec ); + m_env2SusKnob-> setModel( &m-> m_env2Sus ); + m_env2RelKnob-> setModel( &m-> m_env2Rel ); + m_env2SlopeKnob-> setModel( &m-> m_env2Slope ); + + m_o23ModGroup-> setModel( &m-> m_o23Mod ); + m_selectedViewGroup-> setModel( &m-> m_selectedView ); + + m_vol1env1Knob-> setModel( &m-> m_vol1env1 ); + m_vol1env2Knob-> setModel( &m-> m_vol1env2 ); + m_vol1lfo1Knob-> setModel( &m-> m_vol1lfo1 ); + m_vol1lfo2Knob-> setModel( &m-> m_vol1lfo2 ); + + m_vol2env1Knob-> setModel( &m-> m_vol2env1 ); + m_vol2env2Knob-> setModel( &m-> m_vol2env2 ); + m_vol2lfo1Knob-> setModel( &m-> m_vol2lfo1 ); + m_vol2lfo2Knob-> setModel( &m-> m_vol2lfo2 ); + + m_vol3env1Knob-> setModel( &m-> m_vol3env1 ); + m_vol3env2Knob-> setModel( &m-> m_vol3env2 ); + m_vol3lfo1Knob-> setModel( &m-> m_vol3lfo1 ); + m_vol3lfo2Knob-> setModel( &m-> m_vol3lfo2 ); + + m_phs1env1Knob-> setModel( &m-> m_phs1env1 ); + m_phs1env2Knob-> setModel( &m-> m_phs1env2 ); + m_phs1lfo1Knob-> setModel( &m-> m_phs1lfo1 ); + m_phs1lfo2Knob-> setModel( &m-> m_phs1lfo2 ); + + m_phs2env1Knob-> setModel( &m-> m_phs2env1 ); + m_phs2env2Knob-> setModel( &m-> m_phs2env2 ); + m_phs2lfo1Knob-> setModel( &m-> m_phs2lfo1 ); + m_phs2lfo2Knob-> setModel( &m-> m_phs2lfo2 ); + + m_phs3env1Knob-> setModel( &m-> m_phs3env1 ); + m_phs3env2Knob-> setModel( &m-> m_phs3env2 ); + m_phs3lfo1Knob-> setModel( &m-> m_phs3lfo1 ); + m_phs3lfo2Knob-> setModel( &m-> m_phs3lfo2 ); + + m_pit1env1Knob-> setModel( &m-> m_pit1env1 ); + m_pit1env2Knob-> setModel( &m-> m_pit1env2 ); + m_pit1lfo1Knob-> setModel( &m-> m_pit1lfo1 ); + m_pit1lfo2Knob-> setModel( &m-> m_pit1lfo2 ); + + m_pit2env1Knob-> setModel( &m-> m_pit2env1 ); + m_pit2env2Knob-> setModel( &m-> m_pit2env2 ); + m_pit2lfo1Knob-> setModel( &m-> m_pit2lfo1 ); + m_pit2lfo2Knob-> setModel( &m-> m_pit2lfo2 ); + + m_pit3env1Knob-> setModel( &m-> m_pit3env1 ); + m_pit3env2Knob-> setModel( &m-> m_pit3env2 ); + m_pit3lfo1Knob-> setModel( &m-> m_pit3lfo1 ); + m_pit3lfo2Knob-> setModel( &m-> m_pit3lfo2 ); + + m_pw1env1Knob-> setModel( &m-> m_pw1env1 ); + m_pw1env2Knob-> setModel( &m-> m_pw1env2 ); + m_pw1lfo1Knob-> setModel( &m-> m_pw1lfo1 ); + m_pw1lfo2Knob-> setModel( &m-> m_pw1lfo2 ); + + m_sub3env1Knob-> setModel( &m-> m_sub3env1 ); + m_sub3env2Knob-> setModel( &m-> m_sub3env2 ); + m_sub3lfo1Knob-> setModel( &m-> m_sub3lfo1 ); + m_sub3lfo2Knob-> setModel( &m-> m_sub3lfo2 ); + +} + + +void MonstroView::setWidgetBackground( QWidget * _widget, const QString & _pic ) +{ + _widget->setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( _widget->backgroundRole(), + PLUGIN_NAME::getIconPixmap( _pic.toAscii().constData() ) ); + _widget->setPalette( pal ); +} + + +QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) +{ + // operators view + + QWidget * view = new QWidget( _parent ); + view-> setFixedSize( 250, 250 ); + + makeknob( m_osc1VolKnob, KNOBCOL1, O1ROW, "Volume", "%", "osc1Knob" ) + makeknob( m_osc1PanKnob, KNOBCOL2, O1ROW, "Panning", "", "osc1Knob" ) + makeknob( m_osc1CrsKnob, KNOBCOL3, O1ROW, "Coarse detune", " seminotes", "osc1Knob" ) + makeknob( m_osc1FtlKnob, KNOBCOL4, O1ROW, "Finetune left", " cents", "osc1Knob" ) + makeknob( m_osc1FtrKnob, KNOBCOL5, O1ROW, "Finetune right", " cents", "osc1Knob" ) + makeknob( m_osc1SpoKnob, KNOBCOL6, O1ROW, "Stereo phase offset", " deg", "osc1Knob" ) + makeknob( m_osc1PwKnob, KNOBCOL7, O1ROW, "Pulse width", "%", "osc1Knob" ) + + m_osc1VolKnob -> setVolumeKnob( true ); + + maketinyled( m_osc1SSRButton, 230, 34, "Send sync on pulse rise" ) + maketinyled( m_osc1SSFButton, 230, 44, "Send sync on pulse fall" ) + + makeknob( m_osc2VolKnob, KNOBCOL1, O2ROW, "Volume", "%", "osc2Knob" ) + makeknob( m_osc2PanKnob, KNOBCOL2, O2ROW, "Panning", "", "osc2Knob" ) + makeknob( m_osc2CrsKnob, KNOBCOL3, O2ROW, "Coarse detune", " seminotes", "osc2Knob" ) + makeknob( m_osc2FtlKnob, KNOBCOL4, O2ROW, "Finetune left", " cents", "osc2Knob" ) + makeknob( m_osc2FtrKnob, KNOBCOL5, O2ROW, "Finetune right", " cents", "osc2Knob" ) + makeknob( m_osc2SpoKnob, KNOBCOL6, O2ROW, "Stereo phase offset", " deg", "osc2Knob" ) + + m_osc2VolKnob -> setVolumeKnob( true ); + + m_osc2WaveBox = new comboBox( view ); + m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, 22 ); + m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); + + maketinyled( m_osc2SyncButton, 204, O2ROW - 3, "Sync oscillator 2" ) + + makeknob( m_osc3VolKnob, KNOBCOL1, O3ROW, "Volume", "%", "osc3Knob" ) + makeknob( m_osc3PanKnob, KNOBCOL2, O3ROW, "Panning", "", "osc3Knob" ) + makeknob( m_osc3CrsKnob, KNOBCOL3, O3ROW, "Coarse detune", " seminotes", "osc3Knob" ) + makeknob( m_osc3SpoKnob, KNOBCOL4, O3ROW, "Stereo phase offset", " deg", "osc3Knob" ) + makeknob( m_osc3SubKnob, KNOBCOL5, O3ROW, "Sub-osc mix", "", "osc3Knob" ) + + m_osc3Wave1Box = new comboBox( view ); + m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); + m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); + + m_osc3Wave2Box = new comboBox( view ); + m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, 22 ); + m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); + + maketinyled( m_osc3SyncButton, 204, O3ROW - 3, "Sync oscillator 3" ) + + m_lfo1WaveBox = new comboBox( view ); + m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, 22 ); + m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); + + maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, "Attack", " ms", "lfoKnob" ) + maketsknob( m_lfo1RateKnob, LFOCOL2, LFOROW, "Rate", " ms", "lfoKnob" ) + makeknob( m_lfo1PhsKnob, LFOCOL3, LFOROW, "Phase", " deg", "lfoKnob" ) + + m_lfo2WaveBox = new comboBox( view ); + m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, 22 ); + m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); + + maketsknob( m_lfo2AttKnob, LFOCOL4, LFOROW, "Attack", " ms", "lfoKnob" ) + maketsknob( m_lfo2RateKnob, LFOCOL5, LFOROW, "Rate", " ms", "lfoKnob" ) + makeknob( m_lfo2PhsKnob, LFOCOL6, LFOROW, "Phase", " deg", "lfoKnob" ) + + maketsknob( m_env1PreKnob, KNOBCOL1, E1ROW, "Pre-delay", " ms", "envKnob" ) + maketsknob( m_env1AttKnob, KNOBCOL2, E1ROW, "Attack", " ms", "envKnob" ) + maketsknob( m_env1HoldKnob, KNOBCOL3, E1ROW, "Hold", " ms", "envKnob" ) + maketsknob( m_env1DecKnob, KNOBCOL4, E1ROW, "Decay", " ms", "envKnob" ) + makeknob( m_env1SusKnob, KNOBCOL5, E1ROW, "Sustain", "", "envKnob" ) + maketsknob( m_env1RelKnob, KNOBCOL6, E1ROW, "Release", " ms", "envKnob" ) + makeknob( m_env1SlopeKnob, KNOBCOL7, E1ROW, "Slope", "", "envKnob" ) + + maketsknob( m_env2PreKnob, KNOBCOL1, E2ROW, "Pre-delay", " ms", "envKnob" ) + maketsknob( m_env2AttKnob, KNOBCOL2, E2ROW, "Attack", " ms", "envKnob" ) + maketsknob( m_env2HoldKnob, KNOBCOL3, E2ROW, "Hold", " ms", "envKnob" ) + maketsknob( m_env2DecKnob, KNOBCOL4, E2ROW, "Decay", " ms", "envKnob" ) + makeknob( m_env2SusKnob, KNOBCOL5, E2ROW, "Sustain", "", "envKnob" ) + maketsknob( m_env2RelKnob, KNOBCOL6, E2ROW, "Release", " ms", "envKnob" ) + makeknob( m_env2SlopeKnob, KNOBCOL7, E2ROW, "Slope", "", "envKnob" ) + + // mod selector + pixmapButton * m_mixButton = new pixmapButton( view, NULL ); + m_mixButton -> move( 225, 185 ); + m_mixButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_active" ) ); + m_mixButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_inactive" ) ); + toolTip::add( m_mixButton, tr( "Mix Osc2 with Osc3" ) ); + + pixmapButton * m_amButton = new pixmapButton( view, NULL ); + m_amButton -> move( 225, 185 + 15 ); + m_amButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "am_active" ) ); + m_amButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "am_inactive" ) ); + toolTip::add( m_amButton, tr( "Modulate amplitude of Osc3 with Osc2" ) ); + + pixmapButton * m_fmButton = new pixmapButton( view, NULL ); + m_fmButton -> move( 225, 185 + 15*2 ); + m_fmButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_active" ) ); + m_fmButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_inactive" ) ); + toolTip::add( m_fmButton, tr( "Modulate frequency of Osc3 with Osc2" ) ); + + pixmapButton * m_pmButton = new pixmapButton( view, NULL ); + m_pmButton -> move( 225, 185 + 15*3 ); + m_pmButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "pm_active" ) ); + m_pmButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "pm_inactive" ) ); + toolTip::add( m_pmButton, tr( "Modulate phase of Osc3 with Osc2" ) ); + + m_o23ModGroup = new automatableButtonGroup( view ); + m_o23ModGroup-> addButton( m_mixButton ); + m_o23ModGroup-> addButton( m_amButton ); + m_o23ModGroup-> addButton( m_fmButton ); + m_o23ModGroup-> addButton( m_pmButton ); + + return( view ); +} + + +QWidget * MonstroView::setupMatrixView( QWidget * _parent ) +{ + // matrix view + + QWidget * view = new QWidget( _parent ); + view-> setFixedSize( 250, 250 ); + + makeknob( m_vol1env1Knob, MATCOL1, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1env2Knob, MATCOL2, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1lfo1Knob, MATCOL3, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1lfo2Knob, MATCOL4, MATROW1, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_vol2env1Knob, MATCOL1, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2env2Knob, MATCOL2, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2lfo1Knob, MATCOL3, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2lfo2Knob, MATCOL4, MATROW3, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_vol3env1Knob, MATCOL1, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3env2Knob, MATCOL2, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3lfo1Knob, MATCOL3, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3lfo2Knob, MATCOL4, MATROW5, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs1env1Knob, MATCOL1, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1env2Knob, MATCOL2, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1lfo1Knob, MATCOL3, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1lfo2Knob, MATCOL4, MATROW2, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs2env1Knob, MATCOL1, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2env2Knob, MATCOL2, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2lfo1Knob, MATCOL3, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2lfo2Knob, MATCOL4, MATROW4, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs3env1Knob, MATCOL1, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3env2Knob, MATCOL2, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3lfo1Knob, MATCOL3, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3lfo2Knob, MATCOL4, MATROW6, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit1env1Knob, MATCOL5, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1env2Knob, MATCOL6, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1lfo1Knob, MATCOL7, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1lfo2Knob, MATCOL8, MATROW1, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit2env1Knob, MATCOL5, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2env2Knob, MATCOL6, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2lfo1Knob, MATCOL7, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2lfo2Knob, MATCOL8, MATROW3, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit3env1Knob, MATCOL5, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3env2Knob, MATCOL6, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3lfo1Knob, MATCOL7, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3lfo2Knob, MATCOL8, MATROW5, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pw1env1Knob, MATCOL5, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1env2Knob, MATCOL6, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1lfo1Knob, MATCOL7, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1lfo2Knob, MATCOL8, MATROW2, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_sub3env1Knob, MATCOL5, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3env2Knob, MATCOL6, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3lfo1Knob, MATCOL7, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3lfo2Knob, MATCOL8, MATROW6, "Modulation amount", "", "matrixKnob" ) + + return( view ); +} + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return new MonstroInstrument( static_cast( _data ) ); +} + + +} + + + +#include "moc_Monstro.cxx" diff --git a/plugins/monstro/Monstro.h b/plugins/monstro/Monstro.h new file mode 100644 index 000000000..45cf71778 --- /dev/null +++ b/plugins/monstro/Monstro.h @@ -0,0 +1,651 @@ +/* + * Monstro.h - a semi-modular 3-osc synth with modulation matrix + * + * Copyright (c) 2014 Vesa Kivimäki + * + * 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 MONSTRO_H +#define MONSTRO_H + + +#include "Instrument.h" +#include "InstrumentView.h" +#include "AutomatableModel.h" +#include "automatable_button.h" +#include "TempoSyncKnob.h" +#include "NotePlayHandle.h" +#include "pixmap_button.h" +#include "combobox.h" +#include "Oscillator.h" +#include "lmms_math.h" + +// +// UI Macros +// + +#define makeknob( name, x, y, hint, unit, oname ) \ + name = new knob( knobStyled, view ); \ + name ->move( x, y ); \ + name ->setHintText( tr( hint ) + " ", unit ); \ + name ->setObjectName( oname ); \ + name ->setFixedSize( 20, 20 ); + +#define maketsknob( name, x, y, hint, unit, oname ) \ + name = new TempoSyncKnob( knobStyled, view ); \ + name ->move( x, y ); \ + name ->setHintText( tr( hint ) + " ", unit ); \ + name ->setObjectName( oname ); \ + name ->setFixedSize( 20, 20 ); + +#define maketinyled( name, x, y, ttip ) \ + name = new pixmapButton( view, NULL ); \ + name -> setCheckable( true ); \ + name -> move( x, y ); \ + name -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "tinyled_on" ) ); \ + name -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "tinyled_off" ) ); \ + toolTip::add( name, tr( ttip ) ); + +#define setwavemodel( name ) \ + name .addItem( tr( "Sine wave" ), static_cast( new PluginPixmapLoader( "sin" ) ) ); \ + name .addItem( tr( "Triangle wave" ), static_cast( new PluginPixmapLoader( "tri" ) ) ); \ + name .addItem( tr( "Saw wave" ), static_cast( new PluginPixmapLoader( "saw" ) ) ); \ + name .addItem( tr( "Ramp wave" ), static_cast( new PluginPixmapLoader( "ramp" ) ) ); \ + name .addItem( tr( "Square wave" ), static_cast( new PluginPixmapLoader( "sqr" ) ) ); \ + name .addItem( tr( "Soft square wave" ), static_cast( new PluginPixmapLoader( "sqrsoft" ) ) ); \ + name .addItem( tr( "Moog saw wave" ), static_cast( new PluginPixmapLoader( "moog" ) ) ); \ + name .addItem( tr( "Abs. sine wave" ), static_cast( new PluginPixmapLoader( "sinabs" ) ) ); \ + name .addItem( tr( "Exponential wave" ), static_cast( new PluginPixmapLoader( "exp" ) ) ); \ + name .addItem( tr( "White noise" ), static_cast( new PluginPixmapLoader( "noise" ) ) ); + +#define setlfowavemodel( name ) \ + name .addItem( tr( "Sine wave" ), static_cast( new PluginPixmapLoader( "sin" ) ) ); \ + name .addItem( tr( "Triangle wave" ), static_cast( new PluginPixmapLoader( "tri" ) ) ); \ + name .addItem( tr( "Saw wave" ), static_cast( new PluginPixmapLoader( "saw" ) ) ); \ + name .addItem( tr( "Ramp wave" ), static_cast( new PluginPixmapLoader( "ramp" ) ) ); \ + name .addItem( tr( "Square wave" ), static_cast( new PluginPixmapLoader( "sqr" ) ) ); \ + name .addItem( tr( "Soft square wave" ), static_cast( new PluginPixmapLoader( "sqrsoft" ) ) ); \ + name .addItem( tr( "Moog saw wave" ), static_cast( new PluginPixmapLoader( "moog" ) ) ); \ + name .addItem( tr( "Abs. sine wave" ), static_cast( new PluginPixmapLoader( "sinabs" ) ) ); \ + name .addItem( tr( "Exponential wave" ), static_cast( new PluginPixmapLoader( "exp" ) ) ); \ + name .addItem( tr( "Random" ), static_cast( new PluginPixmapLoader( "rand" ) ) ); + +// UI constants +const int O1ROW = 22; +const int O2ROW = 22 + 39; +const int O3ROW = 22 + 39 * 2; +const int LFOROW = 22 + 39 * 3; +const int E1ROW = 22 + 39 * 4; +const int E2ROW = 22 + 39 * 5; + +const int KNOBCOL1 = 16; +const int KNOBCOL2 = 16 + 30; +const int KNOBCOL3 = 16 + 30 * 2; +const int KNOBCOL4 = 16 + 30 * 3; +const int KNOBCOL5 = 16 + 30 * 4; +const int KNOBCOL6 = 16 + 30 * 5; +const int KNOBCOL7 = 16 + 30 * 6; + +const int LFOCOL1 = KNOBCOL2; +const int LFOCOL2 = KNOBCOL2 + 26; +const int LFOCOL3 = KNOBCOL2 + 26*2; +const int LFOCOL4 = 171; +const int LFOCOL5 = 171 + 26; +const int LFOCOL6 = 171 + 26*2; + +const int MATCOL1 = 32; +const int MATCOL2 = 32 + 25; +const int MATCOL3 = 32 + 25*2; +const int MATCOL4 = 32 + 25*3; +const int MATCOL5 = 149; +const int MATCOL6 = 149 + 25; +const int MATCOL7 = 149 + 25*2; +const int MATCOL8 = 149 + 25*3; + +const int MATROW1 = 22; +const int MATROW2 = 22 + 39; +const int MATROW3 = 22 + 39*2; +const int MATROW4 = 22 + 39*3; +const int MATROW5 = 22 + 39*4; +const int MATROW6 = 22 + 39*5; + +const int OPVIEW = 0; +const int MATVIEW = 1; + +// waveform enumerators +const int WAVE_SINE = 0; +const int WAVE_TRI = 1; +const int WAVE_SAW = 2; +const int WAVE_RAMP = 3; +const int WAVE_SQR = 4; +const int WAVE_SQRSOFT = 5; +const int WAVE_MOOG = 6; +const int WAVE_SINABS = 7; +const int WAVE_EXP = 8; +const int WAVE_NOISE = 9; +const int NUM_WAVES = 10; + +// modulation enumerators +const int MOD_MIX = 0; +const int MOD_AM = 1; +const int MOD_FM = 2; +const int MOD_PM = 3; +const int NUM_MODS = 4; + +const float MODCLIP = 2.0; + +const float MIN_FREQ = 18.0f; +const float MAX_FREQ = 48000.0f; + +// constants for amp delta capping - these will be divided by samplerate by the synth +const float ADCAP1 = 44100 / 4; +const float ADCAP2 = 44100 / 4.5; + + +class MonstroInstrument; +class MonstroView; + + +class MonstroSynth +{ +public: + MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph, + const sample_rate_t _samplerate, fpp_t _frames ); + virtual ~MonstroSynth(); + + void renderOutput( fpp_t _frames, sampleFrame * _buf ); + + inline sample_rate_t samplerate() const + { + return m_samplerate; + } + +private: + + MonstroInstrument * m_parent; + NotePlayHandle * m_nph; + const sample_rate_t m_samplerate; + fpp_t m_fpp; + + sample_t * m_env1_buf; + sample_t * m_env2_buf; + sample_t * m_lfo1_buf; + sample_t * m_lfo2_buf; + + void renderModulators( fpp_t _frames ); + + // linear interpolation +/* inline sample_t interpolate( sample_t s1, sample_t s2, float x ) + { + return s1 + ( s2 - s1 ) * x; + }*/ // using interpolation.h from now on + + inline sample_t calcSlope( sample_t _s, float _slope ) + { + if( _slope == 0.0f ) return _s; + const double exp = fastPow( 10.0, static_cast( _slope * -1.0 ) ); + return fastPow( _s, exp ); + } + + inline sample_t oscillate( int _wave, const float _ph ) + { + switch( _wave ) + { + case WAVE_SINE: + return Oscillator::sinSample( _ph ); + break; + case WAVE_TRI: + return Oscillator::triangleSample( _ph ); + break; + case WAVE_SAW: + return Oscillator::sawSample( _ph ); + break; + case WAVE_RAMP: + return Oscillator::sawSample( _ph ) * -1.0; + break; + case WAVE_SQR: + return Oscillator::squareSample( _ph ); + break; + case WAVE_SQRSOFT: + { + const float ph = fraction( _ph ); + if( ph < 0.1 ) return Oscillator::sinSample( ph * 5 + 0.75 ); + else if( ph < 0.5 ) return 1.0f; + else if( ph < 0.6 ) return Oscillator::sinSample( ph * 5 + 0.75 ); + else return -1.0f; + break; + } + case WAVE_MOOG: + return Oscillator::moogSawSample( _ph ); + break; + case WAVE_SINABS: + return qAbs( Oscillator::sinSample( _ph ) ); + break; + case WAVE_EXP: + return Oscillator::expSample( _ph ); + break; + case WAVE_NOISE: + default: + return Oscillator::noiseSample( _ph ); + break; + } + return 0.0; + } + + + float m_osc1l_phase; + float m_osc1r_phase; + float m_osc2l_phase; + float m_osc2r_phase; + float m_osc3l_phase; + float m_osc3r_phase; + + sample_t m_env1_phase; + sample_t m_env2_phase; + + float m_lfo1_phase; + float m_lfo2_phase; + + sample_t m_lfo1_last; + sample_t m_lfo2_last; + + sample_t m_osc1l_last; + sample_t m_osc1r_last; + + sample_t m_osc2l_last; + sample_t m_osc2r_last; + + sample_t m_osc3l_last; + sample_t m_osc3r_last; + + sample_t m_l_last; + sample_t m_r_last; + + float m_adcap1; + float m_adcap2; +}; + +class MonstroInstrument : public Instrument +{ + Q_OBJECT +public: + MonstroInstrument( InstrumentTrack * _instrument_track ); + virtual ~MonstroInstrument(); + + virtual void playNote( NotePlayHandle * _n, + sampleFrame * _working_buffer ); + virtual void deleteNotePluginData( NotePlayHandle * _n ); + + virtual void saveSettings( QDomDocument & _doc, + QDomElement & _this ); + virtual void loadSettings( const QDomElement & _this ); + + virtual QString nodeName() const; + + virtual f_cnt_t desiredReleaseFrames() const; + + virtual PluginView * instantiateView( QWidget * _parent ); + +public slots: + void updateVolumes(); + void updateFreq(); + void updatePO(); + void updateEnvelope1(); + void updateEnvelope2(); + void updateLFOAtts(); + void updateSamplerate(); + +protected: + float m_osc1l_vol; + float m_osc1r_vol; + float m_osc2l_vol; + float m_osc2r_vol; + float m_osc3l_vol; + float m_osc3r_vol; + + float m_osc1l_freq; + float m_osc1r_freq; + float m_osc2l_freq; + float m_osc2r_freq; + float m_osc3_freq; + + float m_osc1l_po; + float m_osc1r_po; + float m_osc2l_po; + float m_osc2r_po; + float m_osc3l_po; + float m_osc3r_po; + + float m_env1_pre; + float m_env1_att; + float m_env1_hold; + float m_env1_dec; + float m_env1_rel; + + float m_env2_pre; + float m_env2_att; + float m_env2_hold; + float m_env2_dec; + float m_env2_rel; + + f_cnt_t m_env1_len; + f_cnt_t m_env2_len; + + f_cnt_t m_env1_relF; + f_cnt_t m_env2_relF; + + f_cnt_t m_lfo1_att; + f_cnt_t m_lfo2_att; + + sample_rate_t m_samplerate; + fpp_t m_fpp; + +private: + inline float leftCh( float _vol, float _pan ) + { + return ( _pan <= 0 ? 1.0 : 1.0 - ( _pan / 100.0 ) ) * _vol / 100.0; + } + + inline float rightCh( float _vol, float _pan ) + { + return ( _pan >= 0 ? 1.0 : 1.0 + ( _pan / 100.0 ) ) * _vol / 100.0; + } + +////////////////////////////////////// +// models of // +// operator view knobs // +// // +////////////////////////////////////// + + FloatModel m_osc1Vol; + FloatModel m_osc1Pan; + FloatModel m_osc1Crs; + FloatModel m_osc1Ftl; + FloatModel m_osc1Ftr; + FloatModel m_osc1Spo; + FloatModel m_osc1Pw; + BoolModel m_osc1SSR; + BoolModel m_osc1SSF; + + FloatModel m_osc2Vol; + FloatModel m_osc2Pan; + FloatModel m_osc2Crs; + FloatModel m_osc2Ftl; + FloatModel m_osc2Ftr; + FloatModel m_osc2Spo; + ComboBoxModel m_osc2Wave; + BoolModel m_osc2Sync; + + FloatModel m_osc3Vol; + FloatModel m_osc3Pan; + FloatModel m_osc3Crs; + FloatModel m_osc3Spo; + FloatModel m_osc3Sub; + ComboBoxModel m_osc3Wave1; + ComboBoxModel m_osc3Wave2; + BoolModel m_osc3Sync; + + ComboBoxModel m_lfo1Wave; + TempoSyncKnobModel m_lfo1Att; + TempoSyncKnobModel m_lfo1Rate; + FloatModel m_lfo1Phs; + + ComboBoxModel m_lfo2Wave; + TempoSyncKnobModel m_lfo2Att; + TempoSyncKnobModel m_lfo2Rate; + FloatModel m_lfo2Phs; + + TempoSyncKnobModel m_env1Pre; + TempoSyncKnobModel m_env1Att; + TempoSyncKnobModel m_env1Hold; + TempoSyncKnobModel m_env1Dec; + FloatModel m_env1Sus; + TempoSyncKnobModel m_env1Rel; + FloatModel m_env1Slope; + + TempoSyncKnobModel m_env2Pre; + TempoSyncKnobModel m_env2Att; + TempoSyncKnobModel m_env2Hold; + TempoSyncKnobModel m_env2Dec; + FloatModel m_env2Sus; + TempoSyncKnobModel m_env2Rel; + FloatModel m_env2Slope; + + IntModel m_o23Mod; + + IntModel m_selectedView; + +////////////////////////////////////// +// models of // +// modulation matrix view knobs // +// // +////////////////////////////////////// + + FloatModel m_vol1env1; + FloatModel m_vol1env2; + FloatModel m_vol1lfo1; + FloatModel m_vol1lfo2; + + FloatModel m_vol2env1; + FloatModel m_vol2env2; + FloatModel m_vol2lfo1; + FloatModel m_vol2lfo2; + + FloatModel m_vol3env1; + FloatModel m_vol3env2; + FloatModel m_vol3lfo1; + FloatModel m_vol3lfo2; + + FloatModel m_phs1env1; + FloatModel m_phs1env2; + FloatModel m_phs1lfo1; + FloatModel m_phs1lfo2; + + FloatModel m_phs2env1; + FloatModel m_phs2env2; + FloatModel m_phs2lfo1; + FloatModel m_phs2lfo2; + + FloatModel m_phs3env1; + FloatModel m_phs3env2; + FloatModel m_phs3lfo1; + FloatModel m_phs3lfo2; + + FloatModel m_pit1env1; + FloatModel m_pit1env2; + FloatModel m_pit1lfo1; + FloatModel m_pit1lfo2; + + FloatModel m_pit2env1; + FloatModel m_pit2env2; + FloatModel m_pit2lfo1; + FloatModel m_pit2lfo2; + + FloatModel m_pit3env1; + FloatModel m_pit3env2; + FloatModel m_pit3lfo1; + FloatModel m_pit3lfo2; + + FloatModel m_pw1env1; + FloatModel m_pw1env2; + FloatModel m_pw1lfo1; + FloatModel m_pw1lfo2; + + FloatModel m_sub3env1; + FloatModel m_sub3env2; + FloatModel m_sub3lfo1; + FloatModel m_sub3lfo2; + + friend class MonstroSynth; + friend class MonstroView; + +}; + +class MonstroView : public InstrumentView +{ + Q_OBJECT +public: + MonstroView( Instrument * _instrument, + QWidget * _parent ); + virtual ~MonstroView(); + +protected slots: + void updateLayout(); + +private: + virtual void modelChanged(); + + void setWidgetBackground( QWidget * _widget, const QString & _pic ); + QWidget * setupOperatorsView( QWidget * _parent ); + QWidget * setupMatrixView( QWidget * _parent ); + +////////////////////////////////////// +// // +// operator view knobs // +// // +////////////////////////////////////// + + knob * m_osc1VolKnob; + knob * m_osc1PanKnob; + knob * m_osc1CrsKnob; + knob * m_osc1FtlKnob; + knob * m_osc1FtrKnob; + knob * m_osc1SpoKnob; + knob * m_osc1PwKnob; + pixmapButton * m_osc1SSRButton; + pixmapButton * m_osc1SSFButton; + + knob * m_osc2VolKnob; + knob * m_osc2PanKnob; + knob * m_osc2CrsKnob; + knob * m_osc2FtlKnob; + knob * m_osc2FtrKnob; + knob * m_osc2SpoKnob; + comboBox * m_osc2WaveBox; + pixmapButton * m_osc2SyncButton; + + knob * m_osc3VolKnob; + knob * m_osc3PanKnob; + knob * m_osc3CrsKnob; + knob * m_osc3SpoKnob; + knob * m_osc3SubKnob; + comboBox * m_osc3Wave1Box; + comboBox * m_osc3Wave2Box; + pixmapButton * m_osc3SyncButton; + + comboBox * m_lfo1WaveBox; + TempoSyncKnob * m_lfo1AttKnob; + TempoSyncKnob * m_lfo1RateKnob; + knob * m_lfo1PhsKnob; + + comboBox * m_lfo2WaveBox; + TempoSyncKnob * m_lfo2AttKnob; + TempoSyncKnob * m_lfo2RateKnob; + knob * m_lfo2PhsKnob; + + TempoSyncKnob * m_env1PreKnob; + TempoSyncKnob * m_env1AttKnob; + TempoSyncKnob * m_env1HoldKnob; + TempoSyncKnob * m_env1DecKnob; + knob * m_env1SusKnob; + TempoSyncKnob * m_env1RelKnob; + knob * m_env1SlopeKnob; + + TempoSyncKnob * m_env2PreKnob; + TempoSyncKnob * m_env2AttKnob; + TempoSyncKnob * m_env2HoldKnob; + TempoSyncKnob * m_env2DecKnob; + knob * m_env2SusKnob; + TempoSyncKnob * m_env2RelKnob; + knob * m_env2SlopeKnob; + + automatableButtonGroup * m_o23ModGroup; + + automatableButtonGroup * m_selectedViewGroup; + + QWidget * m_operatorsView; + QWidget * m_matrixView; + +///////////////////////////////// +// // +// matrix view knobs // +// // +///////////////////////////////// + + knob * m_vol1env1Knob; + knob * m_vol1env2Knob; + knob * m_vol1lfo1Knob; + knob * m_vol1lfo2Knob; + + knob * m_vol2env1Knob; + knob * m_vol2env2Knob; + knob * m_vol2lfo1Knob; + knob * m_vol2lfo2Knob; + + knob * m_vol3env1Knob; + knob * m_vol3env2Knob; + knob * m_vol3lfo1Knob; + knob * m_vol3lfo2Knob; + + knob * m_phs1env1Knob; + knob * m_phs1env2Knob; + knob * m_phs1lfo1Knob; + knob * m_phs1lfo2Knob; + + knob * m_phs2env1Knob; + knob * m_phs2env2Knob; + knob * m_phs2lfo1Knob; + knob * m_phs2lfo2Knob; + + knob * m_phs3env1Knob; + knob * m_phs3env2Knob; + knob * m_phs3lfo1Knob; + knob * m_phs3lfo2Knob; + + knob * m_pit1env1Knob; + knob * m_pit1env2Knob; + knob * m_pit1lfo1Knob; + knob * m_pit1lfo2Knob; + + knob * m_pit2env1Knob; + knob * m_pit2env2Knob; + knob * m_pit2lfo1Knob; + knob * m_pit2lfo2Knob; + + knob * m_pit3env1Knob; + knob * m_pit3env2Knob; + knob * m_pit3lfo1Knob; + knob * m_pit3lfo2Knob; + + knob * m_pw1env1Knob; + knob * m_pw1env2Knob; + knob * m_pw1lfo1Knob; + knob * m_pw1lfo2Knob; + + knob * m_sub3env1Knob; + knob * m_sub3env2Knob; + knob * m_sub3lfo1Knob; + knob * m_sub3lfo2Knob; + +}; + + +#endif diff --git a/plugins/monstro/am_active.png b/plugins/monstro/am_active.png new file mode 100644 index 000000000..785ac4fae Binary files /dev/null and b/plugins/monstro/am_active.png differ diff --git a/plugins/monstro/am_inactive.png b/plugins/monstro/am_inactive.png new file mode 100644 index 000000000..4b1771930 Binary files /dev/null and b/plugins/monstro/am_inactive.png differ diff --git a/plugins/monstro/artwork_mat.png b/plugins/monstro/artwork_mat.png new file mode 100644 index 000000000..1a4240b51 Binary files /dev/null and b/plugins/monstro/artwork_mat.png differ diff --git a/plugins/monstro/artwork_op.png b/plugins/monstro/artwork_op.png new file mode 100644 index 000000000..2f7b69ca0 Binary files /dev/null and b/plugins/monstro/artwork_op.png differ diff --git a/plugins/monstro/exp.png b/plugins/monstro/exp.png new file mode 100644 index 000000000..3f9cc47a0 Binary files /dev/null and b/plugins/monstro/exp.png differ diff --git a/plugins/monstro/fm_active.png b/plugins/monstro/fm_active.png new file mode 100644 index 000000000..cd4ee859d Binary files /dev/null and b/plugins/monstro/fm_active.png differ diff --git a/plugins/monstro/fm_inactive.png b/plugins/monstro/fm_inactive.png new file mode 100644 index 000000000..a96128b7d Binary files /dev/null and b/plugins/monstro/fm_inactive.png differ diff --git a/plugins/monstro/logo.png b/plugins/monstro/logo.png new file mode 100644 index 000000000..aace6a3ce Binary files /dev/null and b/plugins/monstro/logo.png differ diff --git a/plugins/monstro/matview_active.png b/plugins/monstro/matview_active.png new file mode 100644 index 000000000..c9697dc64 Binary files /dev/null and b/plugins/monstro/matview_active.png differ diff --git a/plugins/monstro/matview_inactive.png b/plugins/monstro/matview_inactive.png new file mode 100644 index 000000000..3cba41bac Binary files /dev/null and b/plugins/monstro/matview_inactive.png differ diff --git a/plugins/monstro/mix_active.png b/plugins/monstro/mix_active.png new file mode 100644 index 000000000..a355b290c Binary files /dev/null and b/plugins/monstro/mix_active.png differ diff --git a/plugins/monstro/mix_inactive.png b/plugins/monstro/mix_inactive.png new file mode 100644 index 000000000..5896ca3e4 Binary files /dev/null and b/plugins/monstro/mix_inactive.png differ diff --git a/plugins/monstro/moog.png b/plugins/monstro/moog.png new file mode 100644 index 000000000..1a657b161 Binary files /dev/null and b/plugins/monstro/moog.png differ diff --git a/plugins/monstro/noise.png b/plugins/monstro/noise.png new file mode 100644 index 000000000..cd22af02b Binary files /dev/null and b/plugins/monstro/noise.png differ diff --git a/plugins/monstro/opview_active.png b/plugins/monstro/opview_active.png new file mode 100644 index 000000000..d993b111b Binary files /dev/null and b/plugins/monstro/opview_active.png differ diff --git a/plugins/monstro/opview_inactive.png b/plugins/monstro/opview_inactive.png new file mode 100644 index 000000000..dc5a0a493 Binary files /dev/null and b/plugins/monstro/opview_inactive.png differ diff --git a/plugins/monstro/pm_active.png b/plugins/monstro/pm_active.png new file mode 100644 index 000000000..b62cd9d5c Binary files /dev/null and b/plugins/monstro/pm_active.png differ diff --git a/plugins/monstro/pm_inactive.png b/plugins/monstro/pm_inactive.png new file mode 100644 index 000000000..c09cdb361 Binary files /dev/null and b/plugins/monstro/pm_inactive.png differ diff --git a/plugins/monstro/ramp.png b/plugins/monstro/ramp.png new file mode 100644 index 000000000..94f23b59f Binary files /dev/null and b/plugins/monstro/ramp.png differ diff --git a/plugins/monstro/rand.png b/plugins/monstro/rand.png new file mode 100644 index 000000000..b857b9882 Binary files /dev/null and b/plugins/monstro/rand.png differ diff --git a/plugins/monstro/saw.png b/plugins/monstro/saw.png new file mode 100644 index 000000000..7435f68aa Binary files /dev/null and b/plugins/monstro/saw.png differ diff --git a/plugins/monstro/sin.png b/plugins/monstro/sin.png new file mode 100644 index 000000000..f9441bdfb Binary files /dev/null and b/plugins/monstro/sin.png differ diff --git a/plugins/monstro/sinabs.png b/plugins/monstro/sinabs.png new file mode 100644 index 000000000..067fa0ddb Binary files /dev/null and b/plugins/monstro/sinabs.png differ diff --git a/plugins/monstro/sqr.png b/plugins/monstro/sqr.png new file mode 100644 index 000000000..47748c668 Binary files /dev/null and b/plugins/monstro/sqr.png differ diff --git a/plugins/monstro/sqrsoft.png b/plugins/monstro/sqrsoft.png new file mode 100644 index 000000000..d04a73947 Binary files /dev/null and b/plugins/monstro/sqrsoft.png differ diff --git a/plugins/monstro/tinyled_off.png b/plugins/monstro/tinyled_off.png new file mode 100644 index 000000000..882ac32e8 Binary files /dev/null and b/plugins/monstro/tinyled_off.png differ diff --git a/plugins/monstro/tinyled_on.png b/plugins/monstro/tinyled_on.png new file mode 100644 index 000000000..3b1d97db5 Binary files /dev/null and b/plugins/monstro/tinyled_on.png differ diff --git a/plugins/monstro/tri.png b/plugins/monstro/tri.png new file mode 100644 index 000000000..9764a36e8 Binary files /dev/null and b/plugins/monstro/tri.png differ diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index f5f24335f..58cf987bc 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -149,7 +149,7 @@ void patmanInstrument::playNote( NotePlayHandle * _n, hdata->sample->frequency(); if( hdata->sample->play( _working_buffer, hdata->state, frames, - play_freq, m_loopedModel.value() ) ) + play_freq, m_loopedModel.value() ? SampleBuffer::LoopOn : SampleBuffer::LoopOff ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index 3b7c93265..ab1393aeb 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -31,6 +31,7 @@ #include "tooltip.h" #include "song.h" #include "lmms_math.h" +#include "interpolation.h" #include "embed.cpp" @@ -132,10 +133,10 @@ void WatsynObject::renderOutput( fpp_t _frames ) if( A1_rphase < 0 ) A1_rphase += WAVELEN; } // A1 - sample_t A1_L = interpolate( m_A1wave[ static_cast( A1_lphase ) ], + sample_t A1_L = linearInterpolate( m_A1wave[ static_cast( A1_lphase ) ], m_A1wave[ static_cast( A1_lphase + 1 ) % WAVELEN ], fraction( A1_lphase ) ) * m_parent->m_lvol[A1_OSC]; - sample_t A1_R = interpolate( m_A1wave[ static_cast( A1_rphase ) ], + sample_t A1_R = linearInterpolate( m_A1wave[ static_cast( A1_rphase ) ], m_A1wave[ static_cast( A1_rphase + 1 ) % WAVELEN ], fraction( A1_rphase ) ) * m_parent->m_rvol[A1_OSC]; @@ -168,10 +169,10 @@ void WatsynObject::renderOutput( fpp_t _frames ) if( B1_rphase < 0 ) B1_rphase += WAVELEN; } // B1 - sample_t B1_L = interpolate( m_B1wave[ static_cast( B1_lphase ) % WAVELEN ], + sample_t B1_L = linearInterpolate( m_B1wave[ static_cast( B1_lphase ) % WAVELEN ], m_B1wave[ static_cast( B1_lphase + 1 ) % WAVELEN ], fraction( B1_lphase ) ) * m_parent->m_lvol[B1_OSC]; - sample_t B1_R = interpolate( m_B1wave[ static_cast( B1_rphase ) % WAVELEN ], + sample_t B1_R = linearInterpolate( m_B1wave[ static_cast( B1_rphase ) % WAVELEN ], m_B1wave[ static_cast( B1_rphase + 1 ) % WAVELEN ], fraction( B1_rphase ) ) * m_parent->m_rvol[B1_OSC]; diff --git a/plugins/watsyn/Watsyn.h b/plugins/watsyn/Watsyn.h index 66a2b4757..37c607d0f 100644 --- a/plugins/watsyn/Watsyn.h +++ b/plugins/watsyn/Watsyn.h @@ -103,10 +103,10 @@ public: private: // linear interpolation - inline sample_t interpolate( sample_t s1, sample_t s2, float x ) +/* inline sample_t interpolate( sample_t s1, sample_t s2, float x ) { return s1 + ( s2 - s1 ) * x; - } + }*/ // we use the one in interpolation.h now int m_amod; int m_bmod; diff --git a/plugins/waveshaper/waveshaper.cpp b/plugins/waveshaper/waveshaper.cpp index 905e37af5..293d97d29 100644 --- a/plugins/waveshaper/waveshaper.cpp +++ b/plugins/waveshaper/waveshaper.cpp @@ -25,8 +25,9 @@ #include "waveshaper.h" -#include +#include "lmms_math.h" #include "embed.cpp" +#include "interpolation.h" extern "C" @@ -77,9 +78,6 @@ bool waveShaperEffect::processAudioBuffer( sampleFrame * _buf, // variables for effect int i = 0; - float lookup; - float frac; - float posneg; double out_sum = 0.0; const float d = dryLevel(); @@ -101,23 +99,20 @@ bool waveShaperEffect::processAudioBuffer( sampleFrame * _buf, // start effect - for ( i=0; i <= 1; ++i ) + for( i=0; i <= 1; ++i ) { - lookup = fabsf( s[i] ) * 200.0f; - posneg = s[i] < 0 ? -1.0f : 1.0f; + const int lookup = static_cast( fabsf( s[i] ) * 200.0f ); + const float frac = fraction( fabsf( s[i] ) * 200.0f ); + const float posneg = s[i] < 0 ? -1.0f : 1.0f; - if ( lookup < 1 ) + if( lookup < 1 ) { - frac = lookup - truncf(lookup); s[i] = frac * m_wsControls.m_wavegraphModel.samples()[0] * posneg; } - else - if ( lookup < 200 ) - { - frac = lookup - truncf(lookup); - s[i] = - (( (1.0f-frac) * m_wsControls.m_wavegraphModel.samples()[ (int)truncf(lookup) - 1 ] ) + - ( frac * m_wsControls.m_wavegraphModel.samples()[ (int)truncf(lookup) ] )) + else if( lookup < 200 ) + { + s[i] = linearInterpolate( m_wsControls.m_wavegraphModel.samples()[ lookup - 1 ], + m_wsControls.m_wavegraphModel.samples()[ lookup ], frac ) * posneg; } else diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 2b656cb7a..9540fb29b 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -186,6 +186,7 @@ void SampleBuffer::update( bool _keep_settings ) char * f = qstrdup( file.toUtf8().constData() ); #endif int_sample_t * buf = NULL; + sample_t * fbuf = NULL; ch_cnt_t channels = DEFAULT_CHANNELS; sample_rate_t samplerate = engine::mixer()->baseSampleRate(); m_frames = 0; @@ -210,7 +211,7 @@ void SampleBuffer::update( bool _keep_settings ) #endif if( m_frames == 0 ) { - m_frames = decodeSampleSF( f, buf, channels, + m_frames = decodeSampleSF( f, fbuf, channels, samplerate ); } #ifdef LMMS_HAVE_OGGVORBIS @@ -377,7 +378,7 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, f_cnt_t SampleBuffer::decodeSampleSF( const char * _f, - int_sample_t * & _buf, + sample_t * & _buf, ch_cnt_t & _channels, sample_rate_t & _samplerate ) { @@ -385,29 +386,19 @@ f_cnt_t SampleBuffer::decodeSampleSF( const char * _f, SF_INFO sf_info; f_cnt_t frames = 0; bool sf_rr = false; - sample_t * fbuf = 0; if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL ) { frames = sf_info.frames; - // check if float - if ( (sf_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ) // if yes, use float format for buffer - { - fbuf = new sample_t[sf_info.channels * frames]; - sf_rr = sf_read_float( snd_file, fbuf, sf_info.channels * frames ); - } - else // otherwise, use int - { - _buf = new int_sample_t[sf_info.channels * frames]; - sf_rr = sf_read_short( snd_file, _buf, sf_info.channels * frames ); - } + _buf = new sample_t[sf_info.channels * frames]; + sf_rr = sf_read_float( snd_file, _buf, sf_info.channels * frames ); if( sf_rr < sf_info.channels * frames ) { #ifdef DEBUG_LMMS - printf( "SampleBuffer::decodeSampleSF(): could not read" - " sample %s: %s\n", _f, sf_strerror( NULL ) ); + qDebug( "SampleBuffer::decodeSampleSF(): could not read" + " sample %s: %s", _f, sf_strerror( NULL ) ); #endif } _channels = sf_info.channels; @@ -418,19 +409,15 @@ f_cnt_t SampleBuffer::decodeSampleSF( const char * _f, else { #ifdef DEBUG_LMMS - printf( "SampleBuffer::decodeSampleSF(): could not load " - "sample %s: %s\n", _f, sf_strerror( NULL ) ); + qDebug( "SampleBuffer::decodeSampleSF(): could not load " + "sample %s: %s", _f, sf_strerror( NULL ) ); #endif } //write down either directly or convert i->f depending on file type - if ( frames > 0 && fbuf != NULL ) + if ( frames > 0 && _buf != NULL ) { - directFloatWrite ( fbuf, frames, _channels); - } - else if ( frames > 0 && _buf != NULL ) - { - convertIntToFloat ( _buf, frames, _channels); + directFloatWrite ( _buf, frames, _channels); } return frames; @@ -610,23 +597,29 @@ f_cnt_t SampleBuffer::decodeSampleDS( const char * _f, bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, const fpp_t _frames, const float _freq, - const bool _looped ) + const LoopMode _loopmode ) { QMutexLocker ml( &m_varLock ); - engine::mixer()->clearAudioBuffer( _ab, _frames ); + f_cnt_t startFrame = m_startFrame; + f_cnt_t endFrame = m_endFrame; + f_cnt_t loopStartFrame = m_loopStartFrame; + f_cnt_t loopEndFrame = m_loopEndFrame; - if( m_endFrame == 0 || _frames == 0 ) + if( endFrame == 0 || _frames == 0 ) { return false; } + // variable for determining if we should currently be playing backwards in a ping-pong loop + bool is_backwards = _state->isBackwards(); + const double freq_factor = (double) _freq / (double) m_frequency * m_sampleRate / engine::mixer()->processingSampleRate(); // calculate how many frames we have in requested pitch const f_cnt_t total_frames_for_current_pitch = static_cast( ( - m_endFrame - m_startFrame ) / + endFrame - startFrame ) / freq_factor ); if( total_frames_for_current_pitch == 0 ) @@ -634,37 +627,36 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, return false; } + // this holds the number of the first frame to play f_cnt_t play_frame = _state->m_frameIndex; - if( play_frame < m_startFrame ) + + if( play_frame < startFrame ) { - play_frame = m_startFrame; + play_frame = startFrame; } - // this holds the number of remaining frames in current loop - f_cnt_t frames_for_loop; - if( _looped ) + if( _loopmode == LoopOff ) { - play_frame = getLoopedIndex( play_frame ); - frames_for_loop = static_cast( - ( m_loopEndFrame - play_frame ) / - freq_factor ); + if( play_frame >= endFrame ) + { + return false; + } + + if( ( endFrame - play_frame ) / freq_factor == 0 ) return false; } + + else if( _loopmode == LoopOn ) + { + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + } + else { - if( play_frame >= m_endFrame ) - { - return false; - } - frames_for_loop = static_cast( - ( m_endFrame - play_frame ) / - freq_factor ); - if( frames_for_loop == 0 ) - { - return false; - } + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); } + sampleFrame * tmp = NULL; // check whether we have to change pitch... @@ -673,10 +665,10 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, SRC_DATA src_data; // Generate output const f_cnt_t margin = 64; - f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) - + margin; - src_data.data_in = getSampleFragment( play_frame, - fragment_size, _looped, &tmp )[0]; + f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + margin; + src_data.data_in = + getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards, + loopStartFrame, loopEndFrame, endFrame )[0]; src_data.data_out = _ab[0]; src_data.input_frames = fragment_size; src_data.output_frames = _frames; @@ -695,10 +687,32 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, src_data.output_frames_gen, _frames ); } // Advance - play_frame += src_data.input_frames_used; - if( _looped ) + switch( _loopmode ) { - play_frame = getLoopedIndex( play_frame ); + case LoopOff: + play_frame += src_data.input_frames_used; + break; + case LoopOn: + play_frame += src_data.input_frames_used; + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + break; + case LoopPingPong: + { + f_cnt_t left = src_data.input_frames_used; + if( _state->isBackwards() ) + { + play_frame -= src_data.input_frames_used; + if( play_frame < loopStartFrame ) + { + left -= ( loopStartFrame - play_frame ); + play_frame = loopStartFrame; + } + else left = 0; + } + play_frame += left; + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); + break; + } } } else @@ -708,19 +722,43 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // Generate output memcpy( _ab, - getSampleFragment( play_frame, _frames, _looped, &tmp ), + getSampleFragment( play_frame, _frames, _loopmode, &tmp, &is_backwards, + loopStartFrame, loopEndFrame, endFrame ), _frames * BYTES_PER_FRAME ); // Advance - play_frame += _frames; - if( _looped ) + switch( _loopmode ) { - play_frame = getLoopedIndex( play_frame ); + case LoopOff: + play_frame += _frames; + break; + case LoopOn: + play_frame += _frames; + play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + break; + case LoopPingPong: + { + f_cnt_t left = _frames; + if( _state->isBackwards() ) + { + play_frame -= _frames; + if( play_frame < loopStartFrame ) + { + left -= ( loopStartFrame - play_frame ); + play_frame = loopStartFrame; + } + else left = 0; + } + play_frame += left; + play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); + break; + } } } - delete[] tmp; + if( tmp != NULL ) delete[] tmp; - _state->m_frameIndex = play_frame; + _state->setBackwards( is_backwards ); + _state->setFrameIndex( play_frame ); return true; @@ -729,45 +767,104 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, -sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, - f_cnt_t _frames, bool _looped, sampleFrame * * _tmp ) const +sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _index, + f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, bool * _backwards, + f_cnt_t _loopstart, f_cnt_t _loopend, f_cnt_t _end ) const { - if( _looped ) + + if( _loopmode == LoopOff ) { - if( _start + _frames <= m_loopEndFrame ) + if( _index + _frames <= _end ) { - return m_data + _start; + return m_data + _index; + } + } + else if( _loopmode == LoopOn ) + { + if( _index + _frames <= _loopend ) + { + return m_data + _index; } } else { - if( _start + _frames <= m_endFrame ) - { - return m_data + _start; - } + if( ! *_backwards && _index + _frames < _loopend ) + return m_data + _index; } *_tmp = new sampleFrame[_frames]; - if( _looped ) + if( _loopmode == LoopOff ) { - f_cnt_t copied = m_loopEndFrame - _start; - memcpy( *_tmp, m_data + _start, copied * BYTES_PER_FRAME ); - f_cnt_t loop_frames = m_loopEndFrame - m_loopStartFrame; - while( _frames - copied > 0 ) + f_cnt_t available = _end - _index; + memcpy( *_tmp, m_data + _index, available * BYTES_PER_FRAME ); + memset( *_tmp + available, 0, ( _frames - available ) * + BYTES_PER_FRAME ); + } + else if( _loopmode == LoopOn ) + { + f_cnt_t copied = qMin( _frames, _loopend - _index ); + memcpy( *_tmp, m_data + _index, copied * BYTES_PER_FRAME ); + f_cnt_t loop_frames = _loopend - _loopstart; + while( copied < _frames ) { f_cnt_t todo = qMin( _frames - copied, loop_frames ); - memcpy( *_tmp + copied, m_data + m_loopStartFrame, - todo * BYTES_PER_FRAME ); + memcpy( *_tmp + copied, m_data + _loopstart, todo * BYTES_PER_FRAME ); copied += todo; } } else { - f_cnt_t available = m_endFrame - _start; - memcpy( *_tmp, m_data + _start, available * BYTES_PER_FRAME ); - memset( *_tmp + available, 0, ( _frames - available ) * - BYTES_PER_FRAME ); + f_cnt_t pos = _index; + bool backwards = pos < _loopstart + ? false + : *_backwards; + f_cnt_t copied = 0; + + + if( backwards ) + { + copied = qMin( _frames, pos - _loopstart ); + for( int i=0; i < copied; i++ ) + { + (*_tmp)[i][0] = m_data[ pos - i ][0]; + (*_tmp)[i][1] = m_data[ pos - i ][1]; + } + pos -= copied; + if( pos == _loopstart ) backwards = false; + } + else + { + copied = qMin( _frames, _loopend - pos ); + memcpy( *_tmp, m_data + pos, copied * BYTES_PER_FRAME ); + pos += copied; + if( pos == _loopend ) backwards = true; + } + + while( copied < _frames ) + { + if( backwards ) + { + f_cnt_t todo = qMin( _frames - copied, pos - _loopstart ); + for ( int i=0; i < todo; i++ ) + { + (*_tmp)[ copied + i ][0] = m_data[ pos - i ][0]; + (*_tmp)[ copied + i ][1] = m_data[ pos - i ][1]; + } + pos -= todo; + copied += todo; + if( pos <= _loopstart ) backwards = false; + } + else + { + f_cnt_t todo = qMin( _frames - copied, _loopend - pos ); + memcpy( *_tmp + copied, m_data + pos, todo * BYTES_PER_FRAME ); + pos += todo; + copied += todo; + if( pos >= _loopend ) backwards = true; + } + } + *_backwards = backwards; } return *_tmp; @@ -776,17 +873,30 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start, -f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index ) const +f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const { - if( _index < m_loopEndFrame ) + if( _index < _endf ) { return _index; } - return m_loopStartFrame + ( _index - m_loopStartFrame ) - % ( m_loopEndFrame - m_loopStartFrame ); + return _startf + ( _index - _startf ) + % ( _endf - _startf ); } +f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const +{ + if( _index < _endf ) + { + return _index; + } + const f_cnt_t looplen = _endf - _startf; + const f_cnt_t looppos = ( _index - _endf ) % ( looplen*2 ); + + return ( looppos < looplen ) + ? _endf - looppos + : _startf + ( looppos - looplen ); +} void SampleBuffer::visualize( QPainter & _p, const QRect & _dr, @@ -915,19 +1025,19 @@ QString SampleBuffer::openAndSetWaveformFile() { m_audioFile = configManager::inst()->factorySamplesDir() + "waveforms/10saw.flac"; } - + QString fileName = this->openAudioFile(); if(!fileName.isEmpty()) { this->setAudioFile( fileName ); - } - else + } + else { m_audioFile = ""; } - return fileName; + return fileName; } @@ -1328,7 +1438,8 @@ QString SampleBuffer::tryToMakeAbsolute( const QString & _file ) SampleBuffer::handleState::handleState( bool _varying_pitch ) : m_frameIndex( 0 ), - m_varyingPitch( _varying_pitch ) + m_varyingPitch( _varying_pitch ), + m_isBackwards( false ) { int error; if( ( m_resamplingData = src_new(/* diff --git a/src/core/timeline.cpp b/src/core/timeline.cpp index 6a417f57e..1aa932acf 100644 --- a/src/core/timeline.cpp +++ b/src/core/timeline.cpp @@ -47,8 +47,8 @@ QPixmap * timeLine::s_timeLinePixmap = NULL; QPixmap * timeLine::s_posMarkerPixmap = NULL; -QPixmap * timeLine::s_loopPointPixmap = NULL; - +QPixmap * timeLine::s_loopPointBeginPixmap = NULL; +QPixmap * timeLine::s_loopPointEndPixmap = NULL; timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt, song::playPos & _pos, const MidiTime & _begin, @@ -81,10 +81,15 @@ timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt, s_posMarkerPixmap = new QPixmap( embed::getIconPixmap( "playpos_marker" ) ); } - if( s_loopPointPixmap == NULL ) + if( s_loopPointBeginPixmap == NULL ) { - s_loopPointPixmap = new QPixmap( embed::getIconPixmap( - "loop_point" ) ); + s_loopPointBeginPixmap = new QPixmap( embed::getIconPixmap( + "loop_point_b" ) ); + } + if( s_loopPointEndPixmap == NULL ) + { + s_loopPointEndPixmap = new QPixmap( embed::getIconPixmap( + "loop_point_e" ) ); } setAttribute( Qt::WA_OpaquePaintEvent, true ); @@ -235,8 +240,8 @@ void timeLine::paintEvent( QPaintEvent * ) p.setPen( QColor( 0, 0, 0 ) ); p.setOpacity( loopPointsEnabled() ? 0.9 : 0.2 ); - p.drawPixmap( markerX( loopBegin() )+2, 2, *s_loopPointPixmap ); - p.drawPixmap( markerX( loopEnd() )+2, 2, *s_loopPointPixmap ); + p.drawPixmap( markerX( loopBegin() )+2, 2, *s_loopPointBeginPixmap ); + p.drawPixmap( markerX( loopEnd() )+2, 2, *s_loopPointEndPixmap ); p.setOpacity( 1.0 ); diff --git a/src/core/track.cpp b/src/core/track.cpp index 82d040eca..0f2dabc4f 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -249,7 +249,9 @@ trackContentObjectView::trackContentObjectView( trackContentObject * _tco, m_action( NoAction ), m_autoResize( false ), m_initialMouseX( 0 ), - m_hint( NULL ) + m_hint( NULL ), + m_fgColor( NULL ), + m_textColor( NULL ) { if( s_textFloat == NULL ) { @@ -275,6 +277,9 @@ trackContentObjectView::trackContentObjectView( trackContentObject * _tco, connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) ); setModel( m_tco ); + setFgColor( QColor( 0,0,0 ) ); + setTextColor( QColor( 0,0,0 ) ); + m_trackView->getTrackContentWidget()->addTCOView( this ); } @@ -314,6 +319,23 @@ bool trackContentObjectView::fixedTCOs() +// qproperty access functions, to be inherited & used by TCOviews +//! \brief CSS theming qproperty access method +QColor trackContentObjectView::fgColor() const +{ if( m_fgColor ) return *m_fgColor; else return QColor( 0,0,0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentObjectView::textColor() const +{ if( m_textColor ) return *m_textColor; else return QColor( 0,0,0 ); } + +//! \brief CSS theming qproperty access method +void trackContentObjectView::setFgColor( const QColor & _c ) +{ if( m_fgColor ) *m_fgColor = _c; else m_fgColor = new QColor( 0,0,0 ); } + +//! \brief CSS theming qproperty access method +void trackContentObjectView::setTextColor( const QColor & _c ) +{ if( m_textColor ) *m_textColor = _c; else m_textColor = new QColor( 0,0,0 ); } + /*! \brief Close a trackContentObjectView * @@ -817,7 +839,14 @@ void trackContentObjectView::setAutoResizeEnabled( bool _e ) */ trackContentWidget::trackContentWidget( trackView * _parent ) : QWidget( _parent ), - m_trackView( _parent ) + m_trackView( _parent ), + m_darkerColor1( NULL ), + m_darkerColor2( NULL ), + m_darkerColor3( NULL ), + m_lighterColor1( NULL ), + m_lighterColor2( NULL ), + m_lighterColor3( NULL ), + m_gradMidPoint( 0.0f ) { setAcceptDrops( true ); @@ -825,6 +854,16 @@ trackContentWidget::trackContentWidget( trackView * _parent ) : SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( changePosition( const MidiTime & ) ) ); +//initialize qproperties + setDarkerColor1( QColor( 0, 0, 0 ) ); + setDarkerColor2( QColor( 0, 0, 0 ) ); + setDarkerColor3( QColor( 0, 0, 0 ) ); + setLighterColor1( QColor( 0, 0, 0 ) ); + setLighterColor2( QColor( 0, 0, 0 ) ); + setLighterColor3( QColor( 0, 0, 0 ) ); + + setStyle( QApplication::style() ); + updateBackground(); } @@ -856,15 +895,15 @@ void trackContentWidget::updateBackground() QPainter pmp( &m_background ); QLinearGradient grad( 0,0, 0, h ); - grad.setColorAt( 0.0, QColor( 50, 50, 50 ) ); - grad.setColorAt( 0.33, QColor( 20, 20, 20 ) ); - grad.setColorAt( 1.0, QColor( 15, 15, 15 ) ); + grad.setColorAt( 0.0, darkerColor1() ); + grad.setColorAt( gradMidPoint(), darkerColor2() ); + grad.setColorAt( 1.0, darkerColor3() ); pmp.fillRect( 0, 0, w, h, grad ); QLinearGradient grad2( 0,0, 0, h ); - grad2.setColorAt( 0.0, QColor( 50, 50, 50 ) ); - grad2.setColorAt( 0.33, QColor( 40, 40, 40 ) ); - grad2.setColorAt( 1.0, QColor( 30, 30, 30 ) ); + grad2.setColorAt( 0.0, lighterColor1() ); + grad2.setColorAt( gradMidPoint(), lighterColor2() ); + grad2.setColorAt( 1.0, lighterColor3() ); pmp.fillRect( w, 0, w , h, grad2 ); // draw lines @@ -1195,6 +1234,62 @@ MidiTime trackContentWidget::endPosition( const MidiTime & _pos_start ) } +// qproperty access methods +//! \brief CSS theming qproperty access method +QColor trackContentWidget::darkerColor1() const +{ if( m_darkerColor1 ) return *m_darkerColor1; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentWidget::darkerColor2() const +{ if( m_darkerColor2 ) return *m_darkerColor2; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentWidget::darkerColor3() const +{ if( m_darkerColor3 ) return *m_darkerColor3; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentWidget::lighterColor1() const +{ if( m_lighterColor1 ) return *m_lighterColor1; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentWidget::lighterColor2() const +{ if( m_lighterColor2 ) return *m_lighterColor2; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +QColor trackContentWidget::lighterColor3() const +{ if( m_lighterColor3 ) return *m_lighterColor3; else return QColor( 0, 0, 0 ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setDarkerColor1( const QColor & _c ) +{ if( m_darkerColor1 ) *m_darkerColor1 = _c; else m_darkerColor1 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setDarkerColor2( const QColor & _c ) +{ if( m_darkerColor2 ) *m_darkerColor2 = _c; else m_darkerColor2 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setDarkerColor3( const QColor & _c ) +{ if( m_darkerColor3 ) *m_darkerColor3 = _c; else m_darkerColor3 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setLighterColor1( const QColor & _c ) +{ if( m_lighterColor1 ) *m_lighterColor1 = _c; else m_lighterColor1 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setLighterColor2( const QColor & _c ) +{ if( m_lighterColor2 ) *m_lighterColor2 = _c; else m_lighterColor2 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setLighterColor3( const QColor & _c ) +{ if( m_lighterColor3 ) *m_lighterColor3 = _c; else m_lighterColor3 = new QColor( _c ); } + +//! \brief CSS theming qproperty access method +float trackContentWidget::gradMidPoint() const +{ return m_gradMidPoint; } + +//! \brief CSS theming qproperty access method +void trackContentWidget::setGradMidPoint( float _g ) +{ m_gradMidPoint = _g; } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 9cec7920c..baa19376c 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -273,8 +273,9 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) QLinearGradient lin2grad( 0, min, 0, max ); - lin2grad.setColorAt( 1, c.lighter( 200 ) ); - lin2grad.setColorAt( 0, c ); + lin2grad.setColorAt( 1, fgColor().lighter( 150 ) ); + lin2grad.setColorAt( 0.5, fgColor() ); + lin2grad.setColorAt( 0, fgColor().darker( 150 ) ); for( AutomationPattern::timeMap::const_iterator it = m_pat->getTimeMap().begin(); @@ -313,7 +314,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) QColor text_color = ( m_pat->isMuted() || m_pat->getTrack()->isMuted() ) ? QColor( 30, 30, 30 ) - : QColor( 255, 255, 255 ); + : textColor(); p.setPen( QColor( 0, 0, 0 ) ); p.drawText( 4, p.fontMetrics().height()+1, m_pat->name() ); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 6c3e69f15..1d972a01f 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -71,15 +71,15 @@ FxMixerView::FxMixerView() : // Channel area m_channelAreaWidget = new QWidget; - chLayout = new QHBoxLayout(m_channelAreaWidget); - chLayout->setSizeConstraint(QLayout::SetMinimumSize); + chLayout = new QHBoxLayout( m_channelAreaWidget ); + chLayout->setSizeConstraint( QLayout::SetMinimumSize ); chLayout->setSpacing( 0 ); chLayout->setMargin( 0 ); m_channelAreaWidget->setLayout(chLayout); // add master channel - m_fxChannelViews.resize(m->numChannels()); - m_fxChannelViews[0] = new FxChannelView(this, this, 0); + m_fxChannelViews.resize( m->numChannels() ); + m_fxChannelViews[0] = new FxChannelView( this, this, 0 ); FxChannelView * masterView = m_fxChannelViews[0]; ml->addWidget( masterView->m_fxLine, 0, Qt::AlignTop ); @@ -90,7 +90,7 @@ FxMixerView::FxMixerView() : for( int i = 1; i < m_fxChannelViews.size(); ++i ) { m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); - chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); + chLayout->addWidget( m_fxChannelViews[i]->m_fxLine ); } // add the scrolling section to the main layout @@ -98,22 +98,22 @@ FxMixerView::FxMixerView() : class ChannelArea : public QScrollArea { public: - ChannelArea(QWidget * parent, FxMixerView * mv) : - QScrollArea(parent), m_mv(mv) {} + ChannelArea( QWidget * parent, FxMixerView * mv ) : + QScrollArea( parent ), m_mv( mv ) {} ~ChannelArea() {} - virtual void keyPressEvent(QKeyEvent * e) + virtual void keyPressEvent( QKeyEvent * e ) { - m_mv->keyPressEvent(e); + m_mv->keyPressEvent( e ); } private: FxMixerView * m_mv; }; - channelArea = new ChannelArea(this, this); - channelArea->setWidget(m_channelAreaWidget); + channelArea = new ChannelArea( this, this ); + channelArea->setWidget( m_channelAreaWidget ); channelArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); channelArea->setFrameStyle( QFrame::NoFrame ); channelArea->setMinimumWidth( fxLineSize.width() * 6 ); - channelArea->setFixedHeight( fxLineSize.height() + + channelArea->setFixedHeight( fxLineSize.height() + style()->pixelMetric( QStyle::PM_ScrollBarExtent ) ); ml->addWidget(channelArea); @@ -124,10 +124,12 @@ FxMixerView::FxMixerView() : connect( newChannelBtn, SIGNAL(clicked()), this, SLOT(addNewChannel())); ml->addWidget( newChannelBtn, 0, Qt::AlignTop ); - + // Create EffectRack and set initial index to master channel m_rackView = new EffectRackView( &m->m_fxChannels[0]->m_fxChain, this ); + m_rackView->setFixedSize( 245, fxLineSize.height() ); ml->addWidget( m_rackView, 0, Qt::AlignTop ); + setCurrentFxLine( m_fxChannelViews[0]->m_fxLine ); setLayout( ml ); @@ -344,7 +346,7 @@ void FxMixerView::deleteChannel(int index) -void FxMixerView::moveChannelLeft(int index) +void FxMixerView::moveChannelLeft(int index) { // can't move master or first channel left or last channel right if( index <= 1 || index >= m_fxChannelViews.size() ) return; diff --git a/src/gui/file_browser.cpp b/src/gui/file_browser.cpp index 2d5159766..d49d4c04f 100644 --- a/src/gui/file_browser.cpp +++ b/src/gui/file_browser.cpp @@ -225,6 +225,7 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter void fileBrowser::reloadTree( void ) { + const QString text = m_filterEdit->text(); m_filterEdit->clear(); m_l->clear(); QStringList paths = m_directories.split( '*' ); @@ -232,6 +233,8 @@ void fileBrowser::reloadTree( void ) { addItems( *it ); } + m_filterEdit->setText( text ); + filterItems( text ); } diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index e30f70c80..87b929bbb 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -47,8 +47,8 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) ); // mixer sends knob - m_sendKnob = new knob(0, this, tr("Channel send amount")); - m_sendKnob->move(0, 22); + m_sendKnob = new knob( knobBright_26, this, tr("Channel send amount") ); + m_sendKnob->move(3, 22); m_sendKnob->setVisible(false); // send button indicator @@ -59,7 +59,7 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : // channel number m_lcd = new LcdWidget( 2, this ); m_lcd->setValue( m_channelIndex ); - m_lcd->move( 2, 58 ); + m_lcd->move( 4, 58 ); m_lcd->setMarginWidth( 1 ); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index a886b5303..e71479ad4 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -358,7 +358,7 @@ void SampleTCOView::paintEvent( QPaintEvent * _pe ) } else { - p.setPen( c.lighter( 200 ) ); + p.setPen( fgColor() ); } QRect r = QRect( 1, 1, qMax( static_cast( m_tco->sampleLength() * @@ -383,10 +383,10 @@ void SampleTCOView::paintEvent( QPaintEvent * _pe ) p.setPen( QColor( 0, 0, 0 ) ); p.drawText( 10, p.fontMetrics().height()+1, "Rec" ); - p.setPen( QColor( 255, 60, 60 ) ); + p.setPen( textColor() ); p.drawText( 9, p.fontMetrics().height(), "Rec" ); - p.setBrush( QBrush( QColor( 255, 60, 60 ) ) ); + p.setBrush( QBrush( textColor() ) ); p.drawEllipse( 4, 5, 4, 4 ); } } diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index cc156599e..15bace3fc 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -219,7 +219,7 @@ void bbTCOView::paintEvent( QPaintEvent * ) p.setPen( QColor( 0, 0, 0 ) ); p.drawText( 4, p.fontMetrics().height()+1, m_bbTCO->name() ); - p.setPen( QColor( 255, 255, 255 ) ); + p.setPen( textColor() ); p.drawText( 3, p.fontMetrics().height(), m_bbTCO->name() ); if( m_bbTCO->isMuted() ) diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp index c1022d991..6310eb7b7 100644 --- a/src/tracks/pattern.cpp +++ b/src/tracks/pattern.cpp @@ -1019,7 +1019,7 @@ void patternView::paintEvent( QPaintEvent * ) } else { - p.setPen( QColor( 255, 255, 255 ) ); /// \todo make this a qproperty + p.setPen( fgColor() ); } // scan through all the notes and draw them on the pattern @@ -1133,7 +1133,7 @@ void patternView::paintEvent( QPaintEvent * ) QColor text_color = ( m_pat->isMuted() || m_pat->getTrack()->isMuted() ) ? QColor( 30, 30, 30 ) - : QColor( 255, 255, 255 ); + : textColor(); if( m_pat->name() != m_pat->instrumentTrack()->name() ) {