diff --git a/AUTHORS b/AUTHORS index 22dc31bb9..1f6dc8039 100644 --- a/AUTHORS +++ b/AUTHORS @@ -26,6 +26,10 @@ Andrew Kelley Development +Wong Cho Ching + + Development + Andreas Brandmaier BitInvader plugin @@ -43,7 +47,7 @@ Gabriel Additional artwork Vesa Kivimäki - + UI Lead developer Gurjot Singh @@ -73,3 +77,11 @@ Rubén Ibarra Pastor LocoMatt 3osc skin developer + +Chrissy McManus + + UI developer + +Oskar Wallgren + + Development diff --git a/data/samples/CMakeLists.txt b/data/samples/CMakeLists.txt index 96c7cd0d8..bb8e15ef7 100644 --- a/data/samples/CMakeLists.txt +++ b/data/samples/CMakeLists.txt @@ -1,4 +1,4 @@ INCLUDE(InstallHelpers) -INSTALL_DATA_SUBDIRS("samples" "*.wav;*.ogg;*.ds") +INSTALL_DATA_SUBDIRS("samples" "*.wav;*.ogg;*.ds;*.flac") diff --git a/data/samples/waveforms/10saw.flac b/data/samples/waveforms/10saw.flac new file mode 100644 index 000000000..49f04d906 Binary files /dev/null and b/data/samples/waveforms/10saw.flac differ diff --git a/data/samples/waveforms/10sine.flac b/data/samples/waveforms/10sine.flac new file mode 100644 index 000000000..3cd8f2fed Binary files /dev/null and b/data/samples/waveforms/10sine.flac differ diff --git a/data/samples/waveforms/10sqr.flac b/data/samples/waveforms/10sqr.flac new file mode 100644 index 000000000..53aa21d5f Binary files /dev/null and b/data/samples/waveforms/10sqr.flac differ diff --git a/data/samples/waveforms/10tri.flac b/data/samples/waveforms/10tri.flac new file mode 100644 index 000000000..942d881cb Binary files /dev/null and b/data/samples/waveforms/10tri.flac differ diff --git a/data/samples/waveforms/analogsqr.flac b/data/samples/waveforms/analogsqr.flac new file mode 100644 index 000000000..01b984286 Binary files /dev/null and b/data/samples/waveforms/analogsqr.flac differ diff --git a/data/samples/waveforms/fmsaw1.flac b/data/samples/waveforms/fmsaw1.flac new file mode 100644 index 000000000..7aaf864e2 Binary files /dev/null and b/data/samples/waveforms/fmsaw1.flac differ diff --git a/data/samples/waveforms/fmsine.flac b/data/samples/waveforms/fmsine.flac new file mode 100644 index 000000000..2b4c23771 Binary files /dev/null and b/data/samples/waveforms/fmsine.flac differ diff --git a/data/samples/waveforms/fmsine2.flac b/data/samples/waveforms/fmsine2.flac new file mode 100644 index 000000000..26ad79d3c Binary files /dev/null and b/data/samples/waveforms/fmsine2.flac differ diff --git a/data/samples/waveforms/halfsine.flac b/data/samples/waveforms/halfsine.flac new file mode 100644 index 000000000..9b19025b0 Binary files /dev/null and b/data/samples/waveforms/halfsine.flac differ diff --git a/data/samples/waveforms/impulse.flac b/data/samples/waveforms/impulse.flac new file mode 100644 index 000000000..85a31488d Binary files /dev/null and b/data/samples/waveforms/impulse.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_quarter.flac b/data/samples/waveforms/lfo_trancegate_quarter.flac new file mode 100644 index 000000000..5f4766010 Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_quarter.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_sawdecay_quarter.flac b/data/samples/waveforms/lfo_trancegate_sawdecay_quarter.flac new file mode 100644 index 000000000..76a356870 Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_sawdecay_quarter.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_sinedecay_quarter.flac b/data/samples/waveforms/lfo_trancegate_sinedecay_quarter.flac new file mode 100644 index 000000000..dbaae7748 Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_sinedecay_quarter.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_triplet_half.flac b/data/samples/waveforms/lfo_trancegate_triplet_half.flac new file mode 100644 index 000000000..347d8ba0e Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_triplet_half.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_triplet_half_2.flac b/data/samples/waveforms/lfo_trancegate_triplet_half_2.flac new file mode 100644 index 000000000..48f79063e Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_triplet_half_2.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_triplet_quarter.flac b/data/samples/waveforms/lfo_trancegate_triplet_quarter.flac new file mode 100644 index 000000000..5d2ceb0de Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_triplet_quarter.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_whole.flac b/data/samples/waveforms/lfo_trancegate_whole.flac new file mode 100644 index 000000000..e4850e91d Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_whole.flac differ diff --git a/data/samples/waveforms/lfo_trancegate_whole_2.flac b/data/samples/waveforms/lfo_trancegate_whole_2.flac new file mode 100644 index 000000000..f60c7e118 Binary files /dev/null and b/data/samples/waveforms/lfo_trancegate_whole_2.flac differ diff --git a/data/samples/waveforms/modsqr.flac b/data/samples/waveforms/modsqr.flac new file mode 100644 index 000000000..811e62c63 Binary files /dev/null and b/data/samples/waveforms/modsqr.flac differ diff --git a/data/samples/waveforms/saw1.flac b/data/samples/waveforms/saw1.flac new file mode 100644 index 000000000..9bae8d265 Binary files /dev/null and b/data/samples/waveforms/saw1.flac differ diff --git a/data/samples/waveforms/saw2.flac b/data/samples/waveforms/saw2.flac new file mode 100644 index 000000000..f29a9effa Binary files /dev/null and b/data/samples/waveforms/saw2.flac differ diff --git a/data/samples/waveforms/sinesaw.flac b/data/samples/waveforms/sinesaw.flac new file mode 100644 index 000000000..4dc6bb463 Binary files /dev/null and b/data/samples/waveforms/sinesaw.flac differ diff --git a/data/samples/waveforms/w2_addsyn.flac b/data/samples/waveforms/w2_addsyn.flac new file mode 100644 index 000000000..50ee71144 Binary files /dev/null and b/data/samples/waveforms/w2_addsyn.flac differ diff --git a/data/samples/waveforms/w2_angrysaw.flac b/data/samples/waveforms/w2_angrysaw.flac new file mode 100644 index 000000000..763b6d85e Binary files /dev/null and b/data/samples/waveforms/w2_angrysaw.flac differ diff --git a/data/samples/waveforms/w2_hexagon.flac b/data/samples/waveforms/w2_hexagon.flac new file mode 100644 index 000000000..998d63980 Binary files /dev/null and b/data/samples/waveforms/w2_hexagon.flac differ diff --git a/data/samples/waveforms/w2_invsine.flac b/data/samples/waveforms/w2_invsine.flac new file mode 100644 index 000000000..b5844f952 Binary files /dev/null and b/data/samples/waveforms/w2_invsine.flac differ diff --git a/data/samples/waveforms/w2_invsineabs.flac b/data/samples/waveforms/w2_invsineabs.flac new file mode 100644 index 000000000..70d09211f Binary files /dev/null and b/data/samples/waveforms/w2_invsineabs.flac differ diff --git a/data/samples/waveforms/w2_invsinehalf.flac b/data/samples/waveforms/w2_invsinehalf.flac new file mode 100644 index 000000000..dfed2fd36 Binary files /dev/null and b/data/samples/waveforms/w2_invsinehalf.flac differ diff --git a/data/samples/waveforms/w2_noisy.flac b/data/samples/waveforms/w2_noisy.flac new file mode 100644 index 000000000..30d49b894 Binary files /dev/null and b/data/samples/waveforms/w2_noisy.flac differ diff --git a/data/samples/waveforms/w2_rad.flac b/data/samples/waveforms/w2_rad.flac new file mode 100644 index 000000000..4971abb3d Binary files /dev/null and b/data/samples/waveforms/w2_rad.flac differ diff --git a/data/samples/waveforms/w2_sawsine.flac b/data/samples/waveforms/w2_sawsine.flac new file mode 100644 index 000000000..b267c3e0c Binary files /dev/null and b/data/samples/waveforms/w2_sawsine.flac differ diff --git a/data/samples/waveforms/w2_sharp.flac b/data/samples/waveforms/w2_sharp.flac new file mode 100644 index 000000000..647500088 Binary files /dev/null and b/data/samples/waveforms/w2_sharp.flac differ diff --git a/data/samples/waveforms/w2_w-wave.flac b/data/samples/waveforms/w2_w-wave.flac new file mode 100644 index 000000000..40db66f14 Binary files /dev/null and b/data/samples/waveforms/w2_w-wave.flac differ diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 123370dc6..3219cdcc8 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -153,7 +153,7 @@ private: } ; // some constants... - static const int INITIAL_WIDTH = 740; + static const int INITIAL_WIDTH = 860; static const int INITIAL_HEIGHT = 480; static const int SCROLLBAR_SIZE = 16; @@ -249,6 +249,7 @@ private: bool m_scrollBack; void drawCross( QPainter & _p ); + void drawAutomationPoint( QPainter & p, timeMap::iterator it ); bool inBBEditor(); diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 858ec131e..2eaaeba4d 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -111,6 +111,16 @@ public: return m_tangents; } + inline float getMin() const + { + return firstObject()->minValue(); + } + + inline float getMax() const + { + return firstObject()->maxValue(); + } + inline bool hasAutomation() const { return m_timeMap.isEmpty() == false; @@ -169,6 +179,8 @@ private: bool m_dragging; + static const float DEFAULT_MIN_VALUE = 0; + static const float DEFAULT_MAX_VALUE = 1; friend class AutomationPatternView; diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 4a58cc30c..4fec18513 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -66,6 +66,8 @@ private: QPixmap m_paintPixmap; bool m_needsUpdate; + void scaleTimemapToFit( float oldMin, float oldMax ); + } ; diff --git a/include/PeakController.h b/include/PeakController.h index 96240c6df..89691aa11 100644 --- a/include/PeakController.h +++ b/include/PeakController.h @@ -53,6 +53,9 @@ public: virtual void loadSettings( const QDomElement & _this ); virtual QString nodeName() const; + static void initGetControllerBySetting(); + static PeakController * getControllerBySetting( const QDomElement & _this ); + static PeakControllerEffectVector s_effects; @@ -67,6 +70,12 @@ protected: PeakControllerEffect * m_peakEffect; friend class PeakControllerDialog; + +private: + //backward compatibility for <= 0.4.15 + static int m_getCount; + static int m_loadCount; + static bool m_buggedFile; } ; diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index e42d3690e..54c444fc5 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -174,7 +174,8 @@ public: QString openAudioFile() const; QString openAndSetAudioFile(); - + QString openAndSetWaveformFile(); + QString & toBase64( QString & _dst ) const; diff --git a/plugins/peak_controller_effect/peak_controller_effect.h b/plugins/peak_controller_effect/peak_controller_effect.h index c35287f0a..acbe58e2f 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.h +++ b/plugins/peak_controller_effect/peak_controller_effect.h @@ -48,6 +48,11 @@ public: return m_lastSample; } + PeakController * controller() + { + return m_autoController; + } + int m_effectId; private: @@ -57,7 +62,7 @@ private: float m_lastRMS; bool m_lastRMSavail; - Controller * m_autoController; + PeakController * m_autoController; friend class PeakControllerEffectControls; diff --git a/plugins/peak_controller_effect/peak_controller_effect_controls.cpp b/plugins/peak_controller_effect/peak_controller_effect_controls.cpp index 6104df138..836b72b1f 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_controls.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect_controls.cpp @@ -49,6 +49,7 @@ PeakControllerEffectControls( PeakControllerEffect * _eff ) : + void PeakControllerEffectControls::loadSettings( const QDomElement & _this ) { m_baseModel.loadSettings( _this, "base" ); @@ -77,7 +78,7 @@ void PeakControllerEffectControls::loadSettings( const QDomElement & _this ) m_effect->m_effectId = rand(); } - if( m_effect->m_autoController && ( engine::getSong()->isLoadingProject() == true || PresetPreviewPlayHandle::isPreviewing() == false ) ) + if( m_effect->m_autoController && PresetPreviewPlayHandle::isPreviewing() == true ) { delete m_effect->m_autoController; m_effect->m_autoController = 0; diff --git a/plugins/sfxr/artwork.png b/plugins/sfxr/artwork.png index bedfc4ec3..0f6820d0c 100644 Binary files a/plugins/sfxr/artwork.png and b/plugins/sfxr/artwork.png differ diff --git a/plugins/sfxr/logo.png b/plugins/sfxr/logo.png index a39e4e865..c686f9fbd 100644 Binary files a/plugins/sfxr/logo.png and b/plugins/sfxr/logo.png differ diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 62695c819..65f5db59e 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -319,36 +319,38 @@ bool SfxrSynth::isPlaying() const } + + sfxrInstrument::sfxrInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &sfxr_plugin_descriptor ), - m_attModel(0.0f, this), - m_holdModel(0.3f, this), - m_susModel(0.0f, this), - m_decModel(0.4f, this), + m_attModel(0.0f, this, "Attack Time"), + m_holdModel(0.3f, this, "Sustain Time"), + m_susModel(0.0f, this, "Sustain Punch"), + m_decModel(0.4f, this, "Decay Time"), - m_startFreqModel(0.3f, this), - m_minFreqModel(0.0f, this), - m_slideModel(0.0f, this), - m_dSlideModel(0.0f, this), - m_vibDepthModel(0.0f, this), - m_vibSpeedModel(0.0f, this), + m_startFreqModel(0.3f, this, "Start Frequency"), + m_minFreqModel(0.0f, this, "Min Frequency"), + m_slideModel(0.0f, this, "Slide"), + m_dSlideModel(0.0f, this, "Delta Slide"), + m_vibDepthModel(0.0f, this, "Vibrato Depth"), + m_vibSpeedModel(0.0f, this, "Vibrato Speed"), - m_changeAmtModel(0.0f, this), - m_changeSpeedModel(0.0f, this), + m_changeAmtModel(0.0f, this, "Change Amount"), + m_changeSpeedModel(0.0f, this, "Change Speed"), - m_sqrDutyModel(0.0f, this), - m_sqrSweepModel(0.0f, this), + m_sqrDutyModel(0.0f, this, "Squre Duty"), + m_sqrSweepModel(0.0f, this, "Squre Sweep"), - m_repeatSpeedModel(0.0f, this), + m_repeatSpeedModel(0.0f, this, "Repeat Speed"), - m_phaserOffsetModel(0.0f, this), - m_phaserSweepModel(0.0f, this), + m_phaserOffsetModel(0.0f, this, "Phaser Offset"), + m_phaserSweepModel(0.0f, this, "Phaser Sweep"), - m_lpFilCutModel(1.0f, this), - m_lpFilCutSweepModel(0.0f, this), - m_lpFilResoModel(0.0f, this), - m_hpFilCutModel(0.0f, this), - m_hpFilCutSweepModel(0.0f, this), + m_lpFilCutModel(1.0f, this, "LP Filter Cutoff"), + m_lpFilCutSweepModel(0.0f, this, "LP Filter Cutoff Sweep"), + m_lpFilResoModel(0.0f, this, "LP Filter Resonance"), + m_hpFilCutModel(0.0f, this, "HP Filter Cutoff"), + m_hpFilCutSweepModel(0.0f, this, "HP Filter Cutoff Sweep"), m_waveFormModel( SQR_WAVE, 0, WAVES_NUM-1, this, tr( "Wave Form" ) ) { } @@ -551,7 +553,7 @@ public: -#define createKnob(_knob, _x, _y, _name)\ +#define createKnob( _knob, _x, _y, _name )\ _knob = new sfxrKnob( this ); \ _knob->setHintText( tr( _name ":" ), "" ); \ _knob->move( _x, _y ); \ @@ -560,8 +562,8 @@ public: -#define createButton(_button, _x, _y, _name, _resName)\ - _button = new pixmapButton( this, tr( "Sine wave" ) );\ +#define createButton( _button, _x, _y, _name, _resName )\ + _button = new pixmapButton( this, tr( _name ) );\ _button->move( _x, _y );\ _button->setActiveGraphic( embed::getIconPixmap( _resName "_active" ) );\ _button->setInactiveGraphic( embed::getIconPixmap( _resName "_inactive" ) );\ @@ -570,8 +572,8 @@ public: -#define createButtonLocalGraphic(_button, _x, _y, _name, _resName)\ - _button = new pixmapButton( this, tr( "Sine wave" ) );\ +#define createButtonLocalGraphic( _button, _x, _y, _name, _resName )\ + _button = new pixmapButton( this, tr( _name ) );\ _button->move( _x, _y );\ _button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( _resName "_active" ) );\ _button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( _resName "_inactive" ) );\ diff --git a/plugins/sfxr/sfxr.h b/plugins/sfxr/sfxr.h index aae42c051..a3e989375 100644 --- a/plugins/sfxr/sfxr.h +++ b/plugins/sfxr/sfxr.h @@ -42,11 +42,11 @@ enum SfxrWaves }; const int WAVEFORM_BASE_X = 20; -const int WAVEFORM_BASE_Y = 14; +const int WAVEFORM_BASE_Y = 15; const int WAVEFORM_BUTTON_WIDTH = 16; const int GENERATOR_BASE_X = 110; -const int GENERATOR_BASE_Y = 24; +const int GENERATOR_BASE_Y = 25; const int GENERATOR_BUTTON_WIDTH = 16; const int RAND_BUTTON_X = 160; @@ -126,8 +126,8 @@ private: class SfxrZeroToOneFloatModel : public FloatModel { public: - SfxrZeroToOneFloatModel(float val, Model * parent): - FloatModel( val, 0.0, 1.0, 0.001, parent) + SfxrZeroToOneFloatModel( float val, Model * parent, const QString& displayName ): + FloatModel( val, 0.0, 1.0, 0.001, parent, displayName ) { } /* purpose: prevent the initial value of the model from being changed */ @@ -147,8 +147,8 @@ public: class SfxrNegPosOneFloatModel : public FloatModel { public: - SfxrNegPosOneFloatModel(float val, Model * parent): - FloatModel( val, -1.0, 1.0, 0.001, parent) + SfxrNegPosOneFloatModel(float val, Model * parent, const QString& displayName ): + FloatModel( val, -1.0, 1.0, 0.001, parent, displayName ) { } /* purpose: prevent the initial value of the model from being changed */ diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index d675b7b83..0b91fba22 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -138,7 +138,7 @@ OscillatorObject::~OscillatorObject() void OscillatorObject::oscUserDefWaveDblClick() { - QString af = m_sampleBuffer->openAndSetAudioFile(); + QString af = m_sampleBuffer->openAndSetWaveformFile(); if( af != "" ) { // TODO: diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 382f906fc..8f928a8d1 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -84,8 +84,6 @@ AutomationPattern::~AutomationPattern() void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) { - bool addIt = true; - if( _search_dup ) { for( objectVector::iterator it = m_objects.begin(); @@ -95,27 +93,26 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) { // Already exists // TODO: Maybe let the user know in some non-annoying way - addIt = false; - break; + return; } } } - if( addIt ) + // the automation track is unconnected and there is nothing in the track + if( m_objects.isEmpty() && hasAutomation() == false ) { - // been empty before and model's current value is not its init value? - if( m_objects.isEmpty() && hasAutomation() == false && _obj->isAtInitValue() == false ) - { - // then initialize first value - putValue( 0, _obj->value(), false ); - } - - m_objects += _obj; - - connect( _obj, SIGNAL( destroyed( jo_id_t ) ), - this, SLOT( objectDestroyed( jo_id_t ) ), - Qt::DirectConnection ); + // then initialize first value + putValue( MidiTime(0), _obj->value(), false ); } + + m_objects += _obj; + + connect( _obj, SIGNAL( destroyed( jo_id_t ) ), + this, SLOT( objectDestroyed( jo_id_t ) ), + Qt::DirectConnection ); + + emit dataChanged(); + } @@ -158,7 +155,7 @@ const AutomatableModel * AutomationPattern::firstObject() const return m; } - static FloatModel _fm( 0, 0, 1, 0.001 ); + static FloatModel _fm( 0, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, 0.001 ); return &_fm; } @@ -584,18 +581,18 @@ void AutomationPattern::resolveAllIDs() AutomationPattern * a = dynamic_cast( *j ); if( a ) { - for( QVector::Iterator k = a->m_idsToResolve.begin(); - k != a->m_idsToResolve.end(); ++k ) - { - JournallingObject * o = engine::projectJournal()-> - journallingObject( *k ); - if( o && dynamic_cast( o ) ) - { - a->addObject( dynamic_cast( o ), false ); - } - } - a->m_idsToResolve.clear(); - a->dataChanged(); + for( QVector::Iterator k = a->m_idsToResolve.begin(); + k != a->m_idsToResolve.end(); ++k ) + { + JournallingObject * o = engine::projectJournal()-> + journallingObject( *k ); + if( o && dynamic_cast( o ) ) + { + a->addObject( dynamic_cast( o ), false ); + } + } + a->m_idsToResolve.clear(); + a->dataChanged(); } } } @@ -639,6 +636,20 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) // case we had to remove ourselves if we're the global automation // pattern of the destroyed object m_idsToResolve += _id; + + for( objectVector::Iterator objIt = m_objects.begin(); + objIt != m_objects.end(); objIt++ ) + { + Q_ASSERT( !(*objIt).isNull() ); + if( (*objIt)->id() == _id ) + { + //Assign to objIt so that this loop work even break; is removed. + objIt = m_objects.erase( objIt ); + break; + } + } + + emit dataChanged(); } @@ -703,4 +714,6 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, } + + #include "moc_AutomationPattern.cxx" diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index e11c2255d..4356cb9b3 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -177,12 +177,13 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent ) QString() ); break; - case Controller::LfoController: - c = new ::LfoController( _parent ); + case Controller::LfoController: + c = new ::LfoController( _parent ); break; case Controller::PeakController: - c = new ::PeakController( _parent ); + //Already instantiated in EffectChain::loadSettings() + Q_ASSERT( false ); break; case Controller::MidiController: @@ -200,9 +201,18 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent ) Controller * Controller::create( const QDomElement & _this, Model * _parent ) { - Controller * c = create( - static_cast( _this.attribute( "type" ).toInt() ), - _parent ); + Controller * c; + if( _this.attribute( "type" ).toInt() == Controller::PeakController ) + { + c = PeakController::getControllerBySetting( _this ); + } + else + { + c = create( + static_cast( _this.attribute( "type" ).toInt() ), + _parent ); + } + if( c != NULL ) { c->restoreState( _this ); diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 2ba7d4e18..30f2b7322 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -198,6 +198,7 @@ void LfoController::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_phaseModel.saveSettings( _doc, _this, "phase" ); m_waveModel.saveSettings( _doc, _this, "wave" ); m_multiplierModel.saveSettings( _doc, _this, "multiplier" ); + _this.setAttribute( "userwavefile" , m_userDefSampleBuffer->audioFile() ); } @@ -212,6 +213,7 @@ void LfoController::loadSettings( const QDomElement & _this ) m_phaseModel.loadSettings( _this, "phase" ); m_waveModel.loadSettings( _this, "wave" ); m_multiplierModel.loadSettings( _this, "multiplier" ); + m_userDefSampleBuffer->setAudioFile( _this.attribute("userwavefile" ) ); updateSampleFunction(); } diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index b75edbc93..508174897 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "song.h" @@ -37,8 +38,12 @@ #include "EffectChain.h" #include "ControllerDialog.h" #include "plugins/peak_controller_effect/peak_controller_effect.h" +#include "PresetPreviewPlayHandle.h" PeakControllerEffectVector PeakController::s_effects; +int PeakController::m_getCount; +int PeakController::m_loadCount; +bool PeakController::m_buggedFile; PeakController::PeakController( Model * _parent, @@ -58,7 +63,11 @@ PeakController::PeakController( Model * _parent, PeakController::~PeakController() { - if( m_peakEffect != NULL && m_peakEffect->effectChain() != NULL ) + //EffectChain::loadSettings() appends effect to EffectChain::m_effects + //When it's previewing, EffectChain::loadSettings() is not called + //Therefore, we shouldn't call removeEffect() as it is not even appended. + //NB: Most XML setting are loaded on preview, except controller fx. + if( m_peakEffect != NULL && m_peakEffect->effectChain() != NULL && PresetPreviewPlayHandle::isPreviewing() == false ) { m_peakEffect->effectChain()->removeEffect( m_peakEffect ); } @@ -103,7 +112,13 @@ void PeakController::saveSettings( QDomDocument & _doc, QDomElement & _this ) void PeakController::loadSettings( const QDomElement & _this ) { + Controller::loadSettings( _this ); + int effectId = _this.attribute( "effectId" ).toInt(); + if( m_buggedFile == true ) + { + effectId = m_loadCount++; + } PeakControllerEffectVector::Iterator i; for( i = s_effects.begin(); i != s_effects.end(); ++i ) @@ -118,6 +133,76 @@ void PeakController::loadSettings( const QDomElement & _this ) + +//Backward compatibility function for bug in <= 0.4.15 +void PeakController::initGetControllerBySetting() +{ + m_loadCount = 0; + m_getCount = 0; + m_buggedFile = false; +} + + + + +PeakController * PeakController::getControllerBySetting(const QDomElement & _this ) +{ + int effectId = _this.attribute( "effectId" ).toInt(); + + PeakControllerEffectVector::Iterator i; + + //Backward compatibility for bug in <= 0.4.15 . For >= 1.0.0 , + //foundCount should always be 1 because m_effectId is initialized with rand() + int foundCount = 0; + if( m_buggedFile == false ) + { + for( i = s_effects.begin(); i != s_effects.end(); ++i ) + { + if( (*i)->m_effectId == effectId ) + { + foundCount++; + } + } + if( foundCount >= 2 ) + { + m_buggedFile = true; + int newEffectId = 0; + for( i = s_effects.begin(); i != s_effects.end(); ++i ) + { + (*i)->m_effectId = newEffectId++; + } + QMessageBox msgBox; + msgBox.setIcon( QMessageBox::Information ); + msgBox.setWindowTitle( tr("Peak Controller Bug") ); + msgBox.setText( tr("Due to a bug in older version of LMMS, the peak " + "controllers may not be connect properly. " + "Please ensure that peak controllers are connected " + "properly and re-save this file. " + "Sorry for any inconvenience caused.") ); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + } + + if( m_buggedFile == true ) + { + effectId = m_getCount; + } + m_getCount++; //NB: m_getCount should be increased even m_buggedFile is false + + for( i = s_effects.begin(); i != s_effects.end(); ++i ) + { + if( (*i)->m_effectId == effectId ) + { + return (*i)->controller(); + } + } + + return NULL; +} + + + QString PeakController::nodeName() const { return( "Peakcontroller" ); diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index bbeeceaef..8e91fd131 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -88,8 +88,8 @@ public: bool isPreviewing() { - bool ret = m_dataMutex.tryLock(); - if( ret == true ) + bool ret = !m_dataMutex.tryLock(); + if( ret == false ) { m_dataMutex.unlock(); } diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 674a0a101..238d282fd 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -908,6 +908,27 @@ QString SampleBuffer::openAndSetAudioFile() } +QString SampleBuffer::openAndSetWaveformFile() +{ + if( m_audioFile.isEmpty() ) + { + m_audioFile = configManager::inst()->factorySamplesDir() + "waveforms/10saw.flac"; + } + + QString fileName = this->openAudioFile(); + + if(!fileName.isEmpty()) + { + this->setAudioFile( fileName ); + } + else + { + m_audioFile = ""; + } + + return fileName; +} + #undef LMMS_HAVE_FLAC_STREAM_ENCODER_H /* not yet... */ diff --git a/src/core/song.cpp b/src/core/song.cpp index c1dfc7392..b1a3763e5 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -61,6 +61,7 @@ #include "templates.h" #include "text_float.h" #include "timeline.h" +#include "PeakController.h" #ifdef LMMS_BUILD_WIN32 #ifndef USE_QT_SHMEM @@ -1141,6 +1142,10 @@ void song::loadProject( const QString & _file_name ) m_globalAutomationTrack->restoreState( mmp.content(). firstChildElement( "track" ) ); } + + //Backward compatibility for LMMS <= 0.4.15 + PeakController::initGetControllerBySetting(); + QDomNode node = mmp.content().firstChild(); while( !node.isNull() ) { @@ -1354,7 +1359,16 @@ void song::restoreControllerStates( const QDomElement & _this ) QDomNode node = _this.firstChild(); while( !node.isNull() ) { - addController( Controller::create( node.toElement(), this ) ); + Controller * c = Controller::create( node.toElement(), this ); + Q_ASSERT( c != NULL ); + + /* For PeakController, addController() was called in + * PeakControllerEffect::PeakControllerEffect(). + * This line removes the previously added controller for PeakController + * without affecting the order of controllers in Controller Rack + */ + engine::getSong()->removeController( c ); + addController( c ); node = node.nextSibling(); } diff --git a/src/core/timeline.cpp b/src/core/timeline.cpp index 3c0aa6f11..54e1baff1 100644 --- a/src/core/timeline.cpp +++ b/src/core/timeline.cpp @@ -283,14 +283,14 @@ void timeLine::mousePressEvent( QMouseEvent* event ) m_moveXOff = s_posMarkerPixmap->width() / 2; } } - else if( event->button() == Qt::RightButton ) + else if( event->button() == Qt::RightButton || event->button() == Qt::MiddleButton ) { const MidiTime t = m_begin + static_cast( event->x() * MidiTime::ticksPerTact() / m_ppt ); if( m_loopPos[0] > m_loopPos[1] ) { qSwap( m_loopPos[0], m_loopPos[1] ); } - if( event->modifiers() & Qt::ShiftModifier ) + if( ( event->modifiers() & Qt::ShiftModifier ) || event->button() == Qt::MiddleButton ) { m_action = MoveLoopBegin; } diff --git a/src/gui/AutomationEditor.cpp b/src/gui/AutomationEditor.cpp index 7129d199a..31e582a7c 100644 --- a/src/gui/AutomationEditor.cpp +++ b/src/gui/AutomationEditor.cpp @@ -72,6 +72,10 @@ QPixmap * AutomationEditor::s_toolSelect = NULL; QPixmap * AutomationEditor::s_toolMove = NULL; +const QColor DRAGGABLE_PIN_COLOR = QColor( 0xFF, 0x00, 0x00 ); +const QColor DRAGGABLE_PIN_BORDER_COLOR = QColor( 0xFF, 0xFF, 0xFF ); + + AutomationEditor::AutomationEditor() : QWidget(), m_zoomingXModel(), @@ -1348,6 +1352,22 @@ inline void AutomationEditor::drawCross( QPainter & _p ) +inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterator it ) +{ + int x = xCoordOfTick( it.key() ); + int y = yCoordOfLevel( it.value() ); + int outerRadius = qMin( 8, m_ppt/quantization() ); + int innerRadius = qMax( 0, outerRadius-2 ); + p.setBrush( QBrush( DRAGGABLE_PIN_BORDER_COLOR ) ); + p.drawEllipse( x-outerRadius/2, y-outerRadius/2, outerRadius, outerRadius ); + p.setBrush( QBrush( DRAGGABLE_PIN_COLOR ) ); + p.drawEllipse( x-innerRadius/2, y-innerRadius/2, innerRadius, innerRadius ); + p.setBrush( QBrush() ); +} + + + + void AutomationEditor::paintEvent( QPaintEvent * _pe ) { QMutexLocker m( &m_patternMutex ); @@ -1526,71 +1546,75 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) { int len_ticks = 4; timeMap & time_map = m_pattern->getTimeMap(); - timeMap::iterator it = time_map.begin(); - p.setPen( QColor( 0xCF, 0xD9, 0xFF ) ); - while( it+1 != time_map.end() ) + //Don't bother doing/rendering anything if there is no automation points + if( time_map.size() > 0 ) { - // skip this section if it occurs completely before the - // visible area - int next_x = xCoordOfTick( (it+1).key() ); - if( next_x < 0 ) + timeMap::iterator it = time_map.begin(); + p.setPen( QColor( 0xCF, 0xD9, 0xFF ) ); + while( it+1 != time_map.end() ) { - ++it; - continue; - } + // skip this section if it occurs completely before the + // visible area + int next_x = xCoordOfTick( (it+1).key() ); + if( next_x < 0 ) + { + ++it; + continue; + } - int x = xCoordOfTick( it.key() ); - if( x > width() ) - { - break; - } + int x = xCoordOfTick( it.key() ); + if( x > width() ) + { + break; + } - bool is_selected = FALSE; - // if we're in move-mode, we may only draw - // values in selected area, that have originally - // been selected and not values that are now in - // selection because the user moved it... - if( m_editMode == MOVE ) - { - if( m_selValuesForMove.contains( it.key() ) ) + bool is_selected = FALSE; + // if we're in move-mode, we may only draw + // values in selected area, that have originally + // been selected and not values that are now in + // selection because the user moved it... + if( m_editMode == MOVE ) + { + if( m_selValuesForMove.contains( it.key() ) ) + { + is_selected = TRUE; + } + } + else if( it.value() >= selLevel_start && + it.value() <= selLevel_end && + it.key() >= sel_pos_start && + it.key() + len_ticks <= sel_pos_end ) { is_selected = TRUE; } + + float *values = m_pattern->valuesAfter( it.key() ); + for( int i = 0; i < (it+1).key() - it.key(); i++ ) + { + drawLevelTick( p, it.key() + i, values[i], + is_selected ); + } + delete [] values; + + // Draw circle + drawAutomationPoint(p, it); + + ++it; } - else if( it.value() >= selLevel_start && - it.value() <= selLevel_end && - it.key() >= sel_pos_start && - it.key() + len_ticks <= sel_pos_end ) + + Q_ASSERT( it == time_map.end()-1 ); + + for( int i = it.key(), x = xCoordOfTick( i ); x <= width(); + i++, x = xCoordOfTick( i ) ) { - is_selected = TRUE; + // TODO: Find out if the section after the last control + // point is able to be selected and if so set this + // boolean correctly + drawLevelTick( p, i, it.value(), false ); } - - float *values = m_pattern->valuesAfter( it.key() ); - for( int i = 0; i < (it+1).key() - it.key(); i++ ) - { - drawLevelTick( p, it.key() + i, values[i], - is_selected ); - } - delete [] values; - - // Draw cross - int y = yCoordOfLevel( it.value() ); - p.drawLine( x - 1, y, x + 1, y ); - p.drawLine( x, y - 1, x, y + 1 ); - // _p.setPen( QColor( 0xFF, 0x9F, 0x00 ) ); - // _p.setPen( QColor( 0xFF, 0xFF, 0x40 ) ); - - ++it; - } - - for( int i = it.key(), x = xCoordOfTick( i ); x <= width(); - i++, x = xCoordOfTick( i ) ) - { - // TODO: Find out if the section after the last control - // point is able to be selected and if so set this - // boolean correctly - drawLevelTick( p, i, it.value(), false ); + // Draw circle(the last one) + drawAutomationPoint(p, it); } } else diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index e66c728b4..922e60230 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -107,10 +107,26 @@ void AutomationPatternView::disconnectObject( QAction * _a ) journallingObject( _a->data().toInt() ); if( j && dynamic_cast( j ) ) { + float oldMin = m_pat->getMin(); + float oldMax = m_pat->getMax(); + m_pat->m_objects.erase( qFind( m_pat->m_objects.begin(), m_pat->m_objects.end(), dynamic_cast( j ) ) ); update(); + + //If automation editor is opened, update its display after disconnection + if( engine::automationEditor() ) + { + engine::automationEditor()->updateAfterPatternChange(); + } + + //if there is no more connection connected to the AutomationPattern + if( m_pat->m_objects.size() == 0 ) + { + //scale the points to fit the new min. and max. value + this->scaleTimemapToFit( oldMin, oldMax ); + } } } @@ -333,6 +349,14 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) { engine::automationEditor()->setCurrentPattern( m_pat ); } + + //This is the only model that's just added to AutomationPattern. + if( m_pat->m_objects.size() == 1 ) + { + //scale the points to fit the new min. and max. value + this->scaleTimemapToFit( AutomationPattern::DEFAULT_MIN_VALUE, + AutomationPattern::DEFAULT_MAX_VALUE ); + } } else { @@ -343,5 +367,37 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) +/** + * @brief Preserves the auto points over different scale + */ +void AutomationPatternView::scaleTimemapToFit( float oldMin, float oldMax ) +{ + float newMin = m_pat->getMin(); + float newMax = m_pat->getMax(); + + if( oldMin == newMin && oldMax == newMax ) + { + return; + } + + for( AutomationPattern::timeMap::iterator it = m_pat->m_timeMap.begin(); + it != m_pat->m_timeMap.end(); ++it ) + { + if( *it < oldMin ) + { + *it = oldMin; + } + else if( *it > oldMax ) + { + *it = oldMax; + } + *it = (*it-oldMin)*(newMax-newMin)/(oldMax-oldMin)+newMin; + } + + m_pat->generateTangents(); +} + + + #include "moc_AutomationPatternView.cxx" diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index 3aa9f7c0a..03b0b2d01 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -252,7 +252,7 @@ void LfoControllerDialog::askUserDefWave() { SampleBuffer * sampleBuffer = dynamic_cast(this->model())-> m_userDefSampleBuffer; - QString fileName = sampleBuffer->openAndSetAudioFile(); + QString fileName = sampleBuffer->openAndSetWaveformFile(); if( fileName.isEmpty() == false ) { // TODO: diff --git a/src/gui/bb_editor.cpp b/src/gui/bb_editor.cpp index ec6de9034..3c60ffd74 100644 --- a/src/gui/bb_editor.cpp +++ b/src/gui/bb_editor.cpp @@ -68,12 +68,12 @@ bbEditor::bbEditor( bbTrackContainer* tc ) : "compacttrackbuttons" ).toInt() ) { setMinimumWidth( TRACK_OP_WIDTH_COMPACT + DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT - + 2 * TCO_BORDER_WIDTH + 192 ); + + 2 * TCO_BORDER_WIDTH + 264 ); } else { setMinimumWidth( TRACK_OP_WIDTH + DEFAULT_SETTINGS_WIDGET_WIDTH - + 2 * TCO_BORDER_WIDTH + 192 ); + + 2 * TCO_BORDER_WIDTH + 264 ); } diff --git a/src/gui/widgets/graph.cpp b/src/gui/widgets/graph.cpp index 9a4347647..dc0246eb0 100644 --- a/src/gui/widgets/graph.cpp +++ b/src/gui/widgets/graph.cpp @@ -485,7 +485,7 @@ void graphModel::setWaveToNoise() QString graphModel::setWaveToUser() { SampleBuffer * sampleBuffer = new SampleBuffer; - QString fileName = sampleBuffer->openAndSetAudioFile(); + QString fileName = sampleBuffer->openAndSetWaveformFile(); if( fileName.isEmpty() == false ) { for( int i = 0; i < length(); i++ ) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index c4cc26a8f..6d491af29 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1332,6 +1332,7 @@ void InstrumentTrackWindow::modelChanged() { m_pitchKnob->hide(); m_pitchKnob->setModel( NULL ); + m_pitchRangeSpinBox->hide(); } m_ssView->setModel( &m_track->m_soundShaping );