From dcf7245fe8223d3d13dbdd7839b18c53ba2e15fc Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 6 Jan 2013 22:40:08 +0100 Subject: [PATCH 01/46] Presets: fixed track names to reflect actual preset names Thanks to Tres Finocchiaro for pointing out this issue and many thanks to Mike Choi for providing a helper script for automatically fixing the preset files. Signed-off-by: Tobias Doerffel --- data/presets/BitInvader/alien_strings.xpf | 2 +- data/presets/BitInvader/beehive.xpf | 2 +- data/presets/BitInvader/bell.xpf | 2 +- data/presets/BitInvader/cello.xpf | 2 +- data/presets/BitInvader/drama.xpf | 2 +- data/presets/BitInvader/epiano.xpf | 2 +- data/presets/BitInvader/soft_pad.xpf | 2 +- data/presets/BitInvader/spacefx.xpf | 2 +- data/presets/BitInvader/subbass.xpf | 2 +- data/presets/BitInvader/sweep_pad.xpf | 2 +- data/presets/BitInvader/toy_piano.xpf | 2 +- data/presets/BitInvader/wah_synth.xpf | 2 +- data/presets/LB302/Oh Synth.xpf | 2 +- data/presets/LB302/STrash.xpf | 2 +- data/presets/Organic/organ_blues.xpf | 2 +- data/presets/Organic/organ_risingsun.xpf | 2 +- data/presets/Organic/organ_swish.xpf | 2 +- data/presets/Organic/pad_ethereal.xpf | 2 +- data/presets/Organic/pad_rich.xpf | 2 +- data/presets/Organic/pad_sweep.xpf | 2 +- data/presets/Organic/puresine.xpf | 2 +- data/presets/Organic/sequencer_64.xpf | 2 +- data/presets/TripleOscillator/SBass.xpf | 2 +- data/presets/TripleOscillator/SBass2.xpf | 2 +- data/presets/TripleOscillator/SEGuitar.xpf | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/data/presets/BitInvader/alien_strings.xpf b/data/presets/BitInvader/alien_strings.xpf index 1e9f9fa50..62c5163f8 100644 --- a/data/presets/BitInvader/alien_strings.xpf +++ b/data/presets/BitInvader/alien_strings.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/beehive.xpf b/data/presets/BitInvader/beehive.xpf index 69295aca0..a4c5840e4 100644 --- a/data/presets/BitInvader/beehive.xpf +++ b/data/presets/BitInvader/beehive.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/bell.xpf b/data/presets/BitInvader/bell.xpf index 53626aa36..ce2abec88 100644 --- a/data/presets/BitInvader/bell.xpf +++ b/data/presets/BitInvader/bell.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/cello.xpf b/data/presets/BitInvader/cello.xpf index 6e958d044..44990bb68 100644 --- a/data/presets/BitInvader/cello.xpf +++ b/data/presets/BitInvader/cello.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/drama.xpf b/data/presets/BitInvader/drama.xpf index d2241d879..7ed801dff 100644 --- a/data/presets/BitInvader/drama.xpf +++ b/data/presets/BitInvader/drama.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/epiano.xpf b/data/presets/BitInvader/epiano.xpf index 37daa83f7..c6e6e22e0 100644 --- a/data/presets/BitInvader/epiano.xpf +++ b/data/presets/BitInvader/epiano.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/soft_pad.xpf b/data/presets/BitInvader/soft_pad.xpf index 3123be10e..c82e0510f 100644 --- a/data/presets/BitInvader/soft_pad.xpf +++ b/data/presets/BitInvader/soft_pad.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/spacefx.xpf b/data/presets/BitInvader/spacefx.xpf index 5a1e3c3d9..8e7b93fcb 100644 --- a/data/presets/BitInvader/spacefx.xpf +++ b/data/presets/BitInvader/spacefx.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/subbass.xpf b/data/presets/BitInvader/subbass.xpf index 16bb9547d..d4d93734f 100644 --- a/data/presets/BitInvader/subbass.xpf +++ b/data/presets/BitInvader/subbass.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/sweep_pad.xpf b/data/presets/BitInvader/sweep_pad.xpf index eb0cfe04a..e4f4ee48c 100644 --- a/data/presets/BitInvader/sweep_pad.xpf +++ b/data/presets/BitInvader/sweep_pad.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/toy_piano.xpf b/data/presets/BitInvader/toy_piano.xpf index 706d0ae40..3976affe3 100644 --- a/data/presets/BitInvader/toy_piano.xpf +++ b/data/presets/BitInvader/toy_piano.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/wah_synth.xpf b/data/presets/BitInvader/wah_synth.xpf index 51f7cf0f6..ee8877b7c 100644 --- a/data/presets/BitInvader/wah_synth.xpf +++ b/data/presets/BitInvader/wah_synth.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/LB302/Oh Synth.xpf b/data/presets/LB302/Oh Synth.xpf index 3b9027fee..5b548c16c 100644 --- a/data/presets/LB302/Oh Synth.xpf +++ b/data/presets/LB302/Oh Synth.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/LB302/STrash.xpf b/data/presets/LB302/STrash.xpf index b03fd228b..f001b9b55 100644 --- a/data/presets/LB302/STrash.xpf +++ b/data/presets/LB302/STrash.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/Organic/organ_blues.xpf b/data/presets/Organic/organ_blues.xpf index 7f57a805c..4f102cc04 100644 --- a/data/presets/Organic/organ_blues.xpf +++ b/data/presets/Organic/organ_blues.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/organ_risingsun.xpf b/data/presets/Organic/organ_risingsun.xpf index c69ee05d3..346b59562 100644 --- a/data/presets/Organic/organ_risingsun.xpf +++ b/data/presets/Organic/organ_risingsun.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/organ_swish.xpf b/data/presets/Organic/organ_swish.xpf index bc5022f98..3917962bc 100644 --- a/data/presets/Organic/organ_swish.xpf +++ b/data/presets/Organic/organ_swish.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_ethereal.xpf b/data/presets/Organic/pad_ethereal.xpf index 4ca87e676..e0aa2854d 100644 --- a/data/presets/Organic/pad_ethereal.xpf +++ b/data/presets/Organic/pad_ethereal.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_rich.xpf b/data/presets/Organic/pad_rich.xpf index 9aa11c2e4..62d8de950 100644 --- a/data/presets/Organic/pad_rich.xpf +++ b/data/presets/Organic/pad_rich.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_sweep.xpf b/data/presets/Organic/pad_sweep.xpf index 7c63b6608..ed2c7f87e 100644 --- a/data/presets/Organic/pad_sweep.xpf +++ b/data/presets/Organic/pad_sweep.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/puresine.xpf b/data/presets/Organic/puresine.xpf index 06fb0bcab..3e0d6c0f6 100644 --- a/data/presets/Organic/puresine.xpf +++ b/data/presets/Organic/puresine.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/sequencer_64.xpf b/data/presets/Organic/sequencer_64.xpf index 4be35bd5f..3d3fab9fe 100644 --- a/data/presets/Organic/sequencer_64.xpf +++ b/data/presets/Organic/sequencer_64.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/TripleOscillator/SBass.xpf b/data/presets/TripleOscillator/SBass.xpf index 982bf5498..ae45def69 100644 --- a/data/presets/TripleOscillator/SBass.xpf +++ b/data/presets/TripleOscillator/SBass.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/TripleOscillator/SBass2.xpf b/data/presets/TripleOscillator/SBass2.xpf index 62cc71bf0..b0df645d7 100644 --- a/data/presets/TripleOscillator/SBass2.xpf +++ b/data/presets/TripleOscillator/SBass2.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/TripleOscillator/SEGuitar.xpf b/data/presets/TripleOscillator/SEGuitar.xpf index 4778f50b4..bd6ec8df8 100644 --- a/data/presets/TripleOscillator/SEGuitar.xpf +++ b/data/presets/TripleOscillator/SEGuitar.xpf @@ -2,7 +2,7 @@ - + From 716146848c6d6b220d59100ea77a8cc16e6677c3 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 6 Jan 2013 22:48:41 +0100 Subject: [PATCH 02/46] ZynAddSubFx: when loading a preset set preset name as track name Based upon the suggestion of Tres Finocchiaro we use the filename of a ZASF preset for determining the preset name and set it as track name. --- plugins/zynaddsubfx/ZynAddSubFx.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 78733faed..f1483c19b 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -301,6 +301,9 @@ void ZynAddSubFxInstrument::loadFile( const QString & _file ) m_pluginMutex.unlock(); } + instrumentTrack()->setName( QFileInfo( _file ).baseName(). + replace( QRegExp( "^[0-9]{4}-" ), QString() ) ); + m_modifiedControllers.clear(); emit settingsChanged(); From 2960f67bebf3c530793983ee6835c416481fe408 Mon Sep 17 00:00:00 2001 From: NoiseByNorthwest Date: Wed, 2 Jan 2013 22:10:46 +0100 Subject: [PATCH 03/46] AudioFileProcessor: fixed crash for samples with zero length This is a fix for #3598536. Closes #3598536. Signed-off-by: Tobias Doerffel --- plugins/audio_file_processor/audio_file_processor.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index c97aa2c7a..208a03d9d 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -542,7 +542,15 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ void AudioFileProcessorWaveView::isPlaying( f_cnt_t _frames_played ) { - m_framesPlayed = _frames_played % ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ); + const f_cnt_t nb_frames = m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame(); + if( nb_frames < 1 ) + { + m_framesPlayed = 0; + } + else + { + m_framesPlayed = _frames_played % nb_frames; + } update(); } From 3cc01560be6cd62122b1a853638e2bf3395e432a Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 17:22:26 +0100 Subject: [PATCH 04/46] VST Automation: lock prevention src/core/RemotePlugin.cpp @ RemotePlugin::process Above function should be now more thread safe, but functionality remains. This prevent lmms locks, when automation is connected to lmms VST plugin wrappers controler / knob on Linux. On win32 build whenever is lmms wrapper parameter controler / knob amended. plugins/vst_base/VstPlugin.cpp @ VstPlugin::setParam plugins/vst_base/RemoteVstPlugin.cpp @ RemoteVstPlugin::processMessage In above functions we dont wait for message confirmation when parameter setter function finish, and dont send such confirmations. This workaround prevent locks on Linux, whenever there is automation connected and if you try to move with VST plugin (not via lmms plugins wrapper, but with VSTs internal window) than it freezes, on win32 build it will freeze whenever you connect automation and than if you move your mouse above this VST plugin window. Signed-off-by: Tobias Doerffel --- plugins/vst_base/RemoteVstPlugin.cpp | 2 +- plugins/vst_base/VstPlugin.cpp | 2 +- src/core/RemotePlugin.cpp | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index aaae0c270..40d02cacb 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -451,7 +451,7 @@ bool RemoteVstPlugin::processMessage( const message & _m ) lock(); m_plugin->setParameter( m_plugin, _m.getInt( 0 ), _m.getFloat( 1 ) ); unlock(); - sendMessage( IdVstSetParameter ); + //sendMessage( IdVstSetParameter ); break; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 59d75fb53..c986de2d8 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -574,7 +574,7 @@ void VstPlugin::setParam( int i, float f ) { lock(); sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); - waitForMessage( IdVstSetParameter ); + //waitForMessage( IdVstSetParameter ); unlock(); } diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index dd6a237dd..bb755bf40 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -230,14 +230,13 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, lock(); sendMessage( IdStartProcessing ); - unlock(); if( m_failed || _out_buf == NULL || m_outputCount == 0 ) { + unlock(); return false; } - lock(); waitForMessage( IdProcessingDone ); unlock(); From c2e9918c8ab402c04b29c54ae72ad2f88e48e635 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 19:28:47 +0100 Subject: [PATCH 05/46] VST Automation: crash prevention Fixed destuctors for various types of VST automation panel removal. Automated signals correct disconection on panel removal. When you load new VST plugin in the place of old one, while automation is already connected and active there, this should not result in LMMS crash or reuse of that old automation connections and automation window destructor should not leave zombie VST plugins on application exit. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 102 +++++++++++++++++++---- plugins/vestige/vestige.h | 1 + plugins/vst_effect/VstEffectControls.cpp | 78 +++++++++++++---- plugins/vst_effect/VstEffectControls.h | 1 + 4 files changed, 151 insertions(+), 31 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 46630d740..514d93f1e 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -117,13 +117,14 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) m_plugin->loadSettings( _this ); const QMap & dump = m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - vstKnobs = new knob *[paramCount]; - knobFModel = new FloatModel *[paramCount]; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; QStringList list1; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < paramCount; i++ ) + { sprintf( paramStr, "param%d", i); list1 = dump[paramStr].split(":"); @@ -167,9 +168,10 @@ void vestigeInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_plugin->saveSettings( _doc, _this ); if (knobFModel != NULL) { const QMap & dump = m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < paramCount; i++ ) + { if (knobFModel[i]->isAutomated() || knobFModel[i]->getControllerConnection()) { sprintf( paramStr, "param%d", i); knobFModel[i]->saveSettings( _doc, _this, paramStr ); @@ -223,7 +225,10 @@ void vestigeInstrument::loadFile( const QString & _file ) InstrumentTrack::tr( "Default preset" ); m_pluginMutex.unlock(); - closePlugin(); + if ( m_plugin != NULL ) + { + closePlugin(); + } m_pluginDLL = _file; textFloat * tf = textFloat::displayMessage( @@ -308,6 +313,51 @@ bool vestigeInstrument::handleMidiEvent( const midiEvent & _me, void vestigeInstrument::closePlugin( void ) { + // disconnect all signals + if( knobFModel != NULL ) + { + for( int i = 0; i < paramCount; i++ ) + { + delete knobFModel[ i ]; + delete vstKnobs[ i ]; + } + } + + if( vstKnobs != NULL ) + { + delete [] vstKnobs; + vstKnobs = NULL; + } + + if( knobFModel != NULL ) + { + delete [] knobFModel; + knobFModel = NULL; + } + + if( m_scrollArea != NULL ) + { +// delete m_scrollArea; + m_scrollArea = NULL; + } + + if( m_subWindow != NULL ) + { + m_subWindow->setAttribute( Qt::WA_DeleteOnClose ); + m_subWindow->close(); + + if( m_subWindow != NULL ) + { + delete m_subWindow; + } + m_subWindow = NULL; + } + + if( p_subWindow != NULL ) + { + p_subWindow = NULL; + } + m_pluginMutex.lock(); if( m_plugin ) { @@ -832,23 +882,24 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); const QMap & dump = m_vi->m_plugin->parameterDump(); - int paramCount = (dump).size(); + m_vi->paramCount = dump.size(); bool isVstKnobs = true; if (m_vi->vstKnobs == NULL) { - m_vi->vstKnobs = new knob *[paramCount]; + m_vi->vstKnobs = new knob *[ m_vi->paramCount ]; isVstKnobs = false; } if (m_vi->knobFModel == NULL) { - m_vi->knobFModel = new FloatModel *[paramCount]; + m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ]; } char paramStr[35]; QStringList list1; if (isVstKnobs == false) { - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < m_vi->paramCount; i++ ) + { sprintf( paramStr, "param%d", i); list1 = dump[paramStr].split(":"); @@ -865,15 +916,19 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume } int i = 0; - for (int lrow = 0+1; lrow < (int(paramCount / 10) + 1)+1; lrow++) { - for (int lcolumn = 0; lcolumn < 10; lcolumn++) { - if (i < paramCount) + for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) + { + for( int lcolumn = 0; lcolumn < 10; lcolumn++ ) + { + if( i < m_vi->paramCount ) + { l->addWidget( m_vi->vstKnobs[i], lrow, lcolumn, Qt::AlignCenter ); + } i++; } } - l->setRowStretch( (int(paramCount / 10) + 1), 1 ); + l->setRowStretch( ( int( m_vi->paramCount / 10) + 1), 1 ); l->setColumnStretch( 10, 1 ); widget->setLayout(l); @@ -911,10 +966,25 @@ void manageVestigeInstrumentView::syncPlugin( void ) manageVestigeInstrumentView::~manageVestigeInstrumentView() { + if( m_vi->knobFModel != NULL ) + { + for( int i = 0; i < m_vi->paramCount; i++ ) + { + delete m_vi->knobFModel[ i ]; + delete m_vi->vstKnobs[ i ]; + } + } + if (m_vi->vstKnobs != NULL) { delete []m_vi->vstKnobs; m_vi->vstKnobs = NULL; } + + if( m_vi->knobFModel != NULL ) + { + delete [] m_vi->knobFModel; + m_vi->knobFModel = NULL; + } if (m_vi->m_scrollArea != NULL) { delete m_vi->m_scrollArea; @@ -929,6 +999,8 @@ manageVestigeInstrumentView::~manageVestigeInstrumentView() delete m_vi->m_subWindow; m_vi->m_subWindow = NULL; } + + m_vi->p_subWindow = NULL; } diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 632b6dee6..0320fe4b6 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -92,6 +92,7 @@ private: knob ** vstKnobs; FloatModel ** knobFModel; QObject * p_subWindow; + int paramCount; friend class VestigeInstrumentView; diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index 866c8c4ed..abd22c5ee 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -69,13 +69,14 @@ void VstEffectControls::loadSettings( const QDomElement & _this ) m_effect->m_plugin->loadSettings( _this ); const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - vstKnobs = new knob *[paramCount]; - knobFModel = new FloatModel *[paramCount]; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; QStringList list1; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < paramCount; i++ ) + { sprintf( paramStr, "param%d", i); list1 = dump[paramStr].split(":"); @@ -120,13 +121,15 @@ void VstEffectControls::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_effect->m_plugin->saveSettings( _doc, _this ); if (knobFModel != NULL) { const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - for (int i = 0; i < paramCount; i++) + for( int i = 0; i < paramCount; i++ ) + { if (knobFModel[i]->isAutomated() || knobFModel[i]->getControllerConnection()) { sprintf( paramStr, "param%d", i); knobFModel[i]->saveSettings( _doc, _this, paramStr ); } + } } } m_effect->m_pluginMutex.unlock(); @@ -321,17 +324,17 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + m_vi->paramCount = dump.size(); bool isVstKnobs = true, isKnobFModel = true; if (m_vi->vstKnobs == NULL) { - m_vi->vstKnobs = new knob *[paramCount]; + m_vi->vstKnobs = new knob *[ m_vi->paramCount ]; isVstKnobs = false; } if (m_vi->knobFModel == NULL) { - m_vi->knobFModel = new FloatModel *[paramCount]; + m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ]; isKnobFModel = false; } @@ -339,7 +342,8 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * QStringList list1; if (isVstKnobs == false) { - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < m_vi->paramCount; i++ ) + { sprintf( paramStr, "param%d", i); list1 = dump[paramStr].split(":"); @@ -356,15 +360,19 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * } int i = 0; - for (int lrow = 0+1; lrow < (int(paramCount / 10) + 1)+1; lrow++) { - for (int lcolumn = 0; lcolumn < 10; lcolumn++) { - if (i < paramCount) + for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) + { + for( int lcolumn = 0; lcolumn < 10; lcolumn++ ) + { + if( i < m_vi->paramCount ) + { l->addWidget( m_vi->vstKnobs[i], lrow, lcolumn, Qt::AlignCenter ); + } i++; } } - l->setRowStretch( (int(paramCount / 10) + 1), 1 ); + l->setRowStretch( ( int( m_vi->paramCount / 10 ) + 1 ), 1 ); l->setColumnStretch( 10, 1 ); widget->setLayout(l); @@ -418,8 +426,46 @@ void manageVSTEffectView::setParameter( void ) manageVSTEffectView::~manageVSTEffectView() { - delete m_vi2->m_subWindow; - m_vi2->m_subWindow = NULL; + if( m_vi2->knobFModel != NULL ) + { + for( int i = 0; i < m_vi2->paramCount; i++ ) + { + delete m_vi2->knobFModel[ i ]; + delete m_vi2->vstKnobs[ i ]; + } + } + + if( m_vi2->vstKnobs != NULL ) + { + delete [] m_vi2->vstKnobs; + m_vi2->vstKnobs = NULL; + } + + if( m_vi2->knobFModel != NULL ) + { + delete [] m_vi2->knobFModel; + m_vi2->knobFModel = NULL; + } + + if( m_vi2->m_scrollArea != NULL ) + { + delete m_vi2->m_scrollArea; + m_vi2->m_scrollArea = NULL; + } + + if( m_vi2->m_subWindow != NULL ) + { + m_vi2->m_subWindow->setAttribute( Qt::WA_DeleteOnClose ); + m_vi2->m_subWindow->close(); + + if( m_vi2->m_subWindow != NULL ) + { + delete m_vi2->m_subWindow; + } + m_vi2->m_subWindow = NULL; + } + //delete m_vi2->m_subWindow; + //m_vi2->m_subWindow = NULL; } diff --git a/plugins/vst_effect/VstEffectControls.h b/plugins/vst_effect/VstEffectControls.h index a56c54fcc..1c92cfefe 100644 --- a/plugins/vst_effect/VstEffectControls.h +++ b/plugins/vst_effect/VstEffectControls.h @@ -88,6 +88,7 @@ private: QScrollArea * m_scrollArea; FloatModel ** knobFModel; knob ** vstKnobs; + int paramCount; QObject * ctrHandle; From e8bdc7b3c144fa3e3189396c3883dcadcf521eee Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 20:13:59 +0100 Subject: [PATCH 06/46] VST Automation: Sync button fix Only not automated values are synced from VST plugin, no need to sync already automated values. Synced values are now not trackable with undo / redo, before all changes were tracked individualy, but user could lose ability to undo easily changes before sync buton was pressed. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 23 +++++++++++++++-------- plugins/vst_effect/VstEffectControls.cpp | 23 +++++++++++++++-------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 514d93f1e..2f7914fd6 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -950,16 +950,23 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume void manageVestigeInstrumentView::syncPlugin( void ) { char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; const QMap & dump = m_vi->m_plugin->parameterDump(); - float f; + float f_value; - for (int i = 0; i<(dump).size(); i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); - f = (list1.at(2)).toFloat(); - m_vi->knobFModel[i]->setValue(f); - m_vi->knobFModel[i]->setInitValue(f); + for( int i = 0; i < m_vi->paramCount; i++ ) + { + // only not automated knobs are synced from VST + // those auto-setted values are not jurnaled, tracked for undo / redo + if( !( m_vi->knobFModel[ i ]->isAutomated() || + m_vi->knobFModel[ i ]->getControllerConnection() ) ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); + f_value = ( s_dumpValues.at( 2 ) ).toFloat(); + m_vi->knobFModel[ i ]->setAutomatedValue( f_value ); + m_vi->knobFModel[ i ]->setInitValue( f_value ); + } } } diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index abd22c5ee..968aed2a4 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -394,16 +394,23 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * void manageVSTEffectView::syncPlugin( void ) { char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; const QMap & dump = m_effect->m_plugin->parameterDump(); - float f; + float f_value; - for (int i = 0; i<(dump).size(); i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); - f = (list1.at(2)).toFloat(); - m_vi2->knobFModel[i]->setValue(f); - m_vi2->knobFModel[i]->setInitValue(f); + for( int i = 0; i < m_vi2->paramCount; i++ ) + { + // only not automated knobs are synced from VST + // those auto-setted values are not jurnaled, tracked for undo / redo + if( !( m_vi2->knobFModel[ i ]->isAutomated() || + m_vi2->knobFModel[ i ]->getControllerConnection() ) ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); + f_value = ( s_dumpValues.at( 2 ) ).toFloat(); + m_vi2->knobFModel[ i ]->setAutomatedValue( f_value ); + m_vi2->knobFModel[ i ]->setInitValue( f_value ); + } } } From ba03b7045737108722a061c11d03cd40e5c1e094 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 20:50:25 +0100 Subject: [PATCH 07/46] State of VST controls in LMMS control wrapper is reloaded after Save/Load, from plugins state After project save/load unsaved VST control parameters in LMMS VST control wrapper are not set to zero now, but reloaded according plugins saved state. No need to sync values manualy again, after project save, load. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 20 ++++++++++++++------ plugins/vst_effect/VstEffectControls.cpp | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 2f7914fd6..ee54629b3 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -121,19 +121,27 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) char paramStr[35]; vstKnobs = new knob *[ paramCount ]; knobFModel = new FloatModel *[ paramCount ]; - QStringList list1; + QStringList s_dumpValues; QWidget * widget = new QWidget(); for( int i = 0; i < paramCount; i++ ) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); vstKnobs[i] = new knob( knobBright_26, widget ); - vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - vstKnobs[i]->setLabel( list1.at(1).left(15) ); + vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); - knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, this, QString::number(i) ); + knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) ); knobFModel[i]->loadSettings( _this, paramStr ); + + if( !( knobFModel[ i ]->isAutomated() || + knobFModel[ i ]->getControllerConnection() ) ) + { + knobFModel[ i ]->setValue( ( s_dumpValues.at( 2 )).toFloat() ); + knobFModel[ i ]->setInitValue( ( s_dumpValues.at( 2 )).toFloat() ); + } + connect( knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); vstKnobs[i]->setModel( knobFModel[i] ); diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index 968aed2a4..ec74eea6a 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -73,19 +73,27 @@ void VstEffectControls::loadSettings( const QDomElement & _this ) char paramStr[35]; vstKnobs = new knob *[ paramCount ]; knobFModel = new FloatModel *[ paramCount ]; - QStringList list1; + QStringList s_dumpValues; QWidget * widget = new QWidget(); for( int i = 0; i < paramCount; i++ ) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); vstKnobs[i] = new knob( knobBright_26, widget ); - vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - vstKnobs[i]->setLabel( list1.at(1).left(15) ); + vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); - knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, this, QString::number(i) ); + knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) ); knobFModel[i]->loadSettings( _this, paramStr ); + + if( !( knobFModel[ i ]->isAutomated() || + knobFModel[ i ]->getControllerConnection() ) ) + { + knobFModel[ i ]->setValue( (s_dumpValues.at( 2 ) ).toFloat() ); + knobFModel[ i ]->setInitValue( (s_dumpValues.at( 2 ) ).toFloat() ); + } + connect( knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); vstKnobs[i]->setModel( knobFModel[i] ); From 5b6fa164e74a2b7e27f9e3f242d3c4157b04885e Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 22:22:30 +0100 Subject: [PATCH 08/46] VST Automation: Filter for automated parameters Filter to display only automated / all knobs (new button) on LMMS VST parametr control window. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 38 ++++++++++++++++++++++++ plugins/vestige/vestige.h | 2 ++ plugins/vst_effect/VstEffectControls.cpp | 37 +++++++++++++++++++++++ plugins/vst_effect/VstEffectControls.h | 2 ++ 4 files changed, 79 insertions(+) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index ee54629b3..192d58d33 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -889,6 +889,19 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); + m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), this ); + connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this, + SLOT( displayAutomatedOnly() ) ); + m_displayAutomatedOnly->setWhatsThis( + tr( "Click here if you want to display automated parameters only." ) ); + + l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + for( int i = 0; i < 10; i++ ) + { + l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); + } + const QMap & dump = m_vi->m_plugin->parameterDump(); m_vi->paramCount = dump.size(); @@ -979,6 +992,31 @@ void manageVestigeInstrumentView::syncPlugin( void ) } + + +void manageVestigeInstrumentView::displayAutomatedOnly( void ) +{ + bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0; + + for( int i = 0; i< m_vi->paramCount; i++ ) + { + + if( !( m_vi->knobFModel[ i ]->isAutomated() || + m_vi->knobFModel[ i ]->getControllerConnection() ) ) + { + if( m_vi->vstKnobs[ i ]->isVisible() == true && isAuto ) + { + m_vi->vstKnobs[ i ]->hide(); + m_displayAutomatedOnly->setText( "All" ); + } else { + m_vi->vstKnobs[ i ]->show(); + m_displayAutomatedOnly->setText( "Automated" ); + } + } + } +} + + manageVestigeInstrumentView::~manageVestigeInstrumentView() { if( m_vi->knobFModel != NULL ) diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 0320fe4b6..299cdda3e 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -111,6 +111,7 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); @@ -128,6 +129,7 @@ private: QWidget *widget; QGridLayout * l; QPushButton * m_syncButton; + QPushButton * m_displayAutomatedOnly; } ; diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index ec74eea6a..c8b0766cc 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -331,6 +331,19 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); + m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), widget ); + connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this, + SLOT( displayAutomatedOnly() ) ); + m_displayAutomatedOnly->setWhatsThis( + tr( "Click here if you want to display automated parameters only." ) ); + + l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + for( int i = 0; i < 10; i++ ) + { + l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); + } + const QMap & dump = m_effect->m_plugin->parameterDump(); m_vi->paramCount = dump.size(); @@ -424,6 +437,30 @@ void manageVSTEffectView::syncPlugin( void ) +void manageVSTEffectView::displayAutomatedOnly( void ) +{ + bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0; + + for( int i = 0; i< m_vi2->paramCount; i++ ) + { + + if( !( m_vi2->knobFModel[ i ]->isAutomated() || + m_vi2->knobFModel[ i ]->getControllerConnection() ) ) + { + if( m_vi2->vstKnobs[ i ]->isVisible() == true && isAuto ) + { + m_vi2->vstKnobs[ i ]->hide(); + m_displayAutomatedOnly->setText( "All" ); + } else { + m_vi2->vstKnobs[ i ]->show(); + m_displayAutomatedOnly->setText( "Automated" ); + } + } + } +} + + + void manageVSTEffectView::setParameter( void ) { diff --git a/plugins/vst_effect/VstEffectControls.h b/plugins/vst_effect/VstEffectControls.h index 1c92cfefe..67622196f 100644 --- a/plugins/vst_effect/VstEffectControls.h +++ b/plugins/vst_effect/VstEffectControls.h @@ -113,6 +113,7 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); private: @@ -129,6 +130,7 @@ private: QGridLayout * l; QPushButton * m_syncButton; + QPushButton * m_displayAutomatedOnly; } ; From f4cc0373210038b3534316cdf62a772817c5f2ad Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 21:57:56 +0100 Subject: [PATCH 09/46] VST Control: Right window title for parameter manual edit When you double-click on some knob to change its VST parameter value manually, new dialog window now has same title as what was that knobs name. (instead of "lmms" title string) Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 16 ++++++++-------- plugins/vst_effect/VstEffectControls.cpp | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 192d58d33..32e82c252 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -128,7 +128,7 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) sprintf( paramStr, "param%d", i ); s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[i] = new knob( knobBright_26, widget ); + vstKnobs[i] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); @@ -916,21 +916,21 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume } char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; if (isVstKnobs == false) { for( int i = 0; i < m_vi->paramCount; i++ ) { sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + s_dumpValues = dump[ paramStr ].split( ":" ); - m_vi->vstKnobs[i] = new knob( knobBright_26, this ); - m_vi->vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - m_vi->vstKnobs[i]->setLabel( list1.at(1).left(15) ); + m_vi->vstKnobs[ i ] = new knob( knobBright_26, this, s_dumpValues.at( 1 ) ); + m_vi->vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + m_vi->vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); sprintf( paramStr, "%d", i); - m_vi->knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, - castModel(), tr( paramStr ) ); + m_vi->knobFModel[ i ] = new FloatModel( (s_dumpValues.at( 2 )).toFloat(), + 0.0f, 1.0f, 0.01f, castModel(), tr( paramStr ) ); connect( m_vi->knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); m_vi->vstKnobs[i] ->setModel( m_vi->knobFModel[i] ); } diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index c8b0766cc..3b1fd16ce 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -80,7 +80,7 @@ void VstEffectControls::loadSettings( const QDomElement & _this ) sprintf( paramStr, "param%d", i ); s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[i] = new knob( knobBright_26, widget ); + vstKnobs[i] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); @@ -360,23 +360,24 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * } char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; if (isVstKnobs == false) { for( int i = 0; i < m_vi->paramCount; i++ ) { sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + s_dumpValues = dump[ paramStr ].split( ":" ); - m_vi->vstKnobs[i] = new knob( knobBright_26, widget); - m_vi->vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - m_vi->vstKnobs[i]->setLabel( list1.at(1).left(15) ); + m_vi->vstKnobs[ i ] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + m_vi->vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + m_vi->vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); sprintf( paramStr, "%d", i); - m_vi->knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, - _eff, tr( paramStr ) ); - connect( m_vi->knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); - m_vi->vstKnobs[i] ->setModel( m_vi->knobFModel[i] ); + m_vi->knobFModel[ i ] = new FloatModel( ( s_dumpValues.at( 2 ) ).toFloat(), + 0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) ); + connect( m_vi->knobFModel[ i ], SIGNAL( dataChanged() ), this, + SLOT( setParameter() ) ); + m_vi->vstKnobs[ i ] ->setModel( m_vi->knobFModel[ i ] ); } } From b9c926dabd887c0e90db265e6c3642d72bc4416e Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 3 Jan 2013 22:09:24 +0100 Subject: [PATCH 10/46] VST parametr control window: window title as trackname Window title for VSTi parameter controlling window should be set according to actual track name, not from VST plugin name as its now, it is hard to get to know which window belongs to where when same plugin is e.g. opened several times, so with the same name. Now this is handled in paint event, but TBD whenever is that track name changed. tbd, temporary solution. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 32e82c252..4a5a3d456 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -852,6 +852,11 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) m_vi->m_plugin->vendorString() ); p.drawText( 10, 225, m_vi->m_plugin->currentProgramName() ); } + + if( m_vi->m_subWindow != NULL ) + { + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() ); + } // m_pluginMutex.unlock(); } @@ -872,7 +877,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); m_vi->m_subWindow->setWidget(m_vi->m_scrollArea); - m_vi->m_subWindow->setWindowTitle(m_vi->m_plugin->name()); + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() ); m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); //m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose); @@ -1114,6 +1119,7 @@ void manageVestigeInstrumentView::dropEvent( QDropEvent * _de ) void manageVestigeInstrumentView::paintEvent( QPaintEvent * ) { + m_vi->m_subWindow->setWindowTitle(m_vi->instrumentTrack()->name()); } From 1612a253496b34ff8b8a784c0bfe82c83e713c85 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 6 Jan 2013 23:13:59 +0100 Subject: [PATCH 11/46] ExportProjectDialog: fixed broken cancel button The cancel button functionality was lost during introduction of the multi track export functionality. Closes #3598342. --- src/gui/export_project_dialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index 93527c657..eba9123ee 100644 --- a/src/gui/export_project_dialog.cpp +++ b/src/gui/export_project_dialog.cpp @@ -1,7 +1,7 @@ /* * export_project_dialog.cpp - implementation of dialog for exporting project * - * Copyright (c) 2004-2012 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -103,6 +103,8 @@ void exportProjectDialog::reject() { (*it)->abortProcessing(); } + + QDialog::reject(); } From 99091e9ce36cc2b3034951b4648d22cad43315b5 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 6 Jan 2013 23:40:48 +0100 Subject: [PATCH 12/46] AutomatableModelView: added context menu action for removing song-global automation Up to now there was no possibility to remove song-global automation from a control once created. Overcome this issue by adding an according action in the context menu of all AutomatableModelView instances. --- include/AutomatableModelView.h | 3 ++- src/gui/AutomatableModelView.cpp | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index b7c3579e7..6c59d8f61 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -1,7 +1,7 @@ /* * AutomatableModelView.h - class AutomatableModelView * - * Copyright (c) 2008-2009 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -101,6 +101,7 @@ public slots: void execConnectionDialog(); void removeConnection(); void editSongGlobalAutomation(); + void removeSongGlobalAutomation(); protected: diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 744ba26f5..3d1995636 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -1,7 +1,7 @@ /* * AutomatableModelView.cpp - implementation of AutomatableModelView * - * Copyright (c) 2011 Tobias Doerffel + * Copyright (c) 2011-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -88,6 +88,12 @@ void AutomatableModelView::addDefaultActions( QMenu * _menu ) AutomatableModel::tr( "Edit song-global automation" ), amvSlots, SLOT( editSongGlobalAutomation() ) ); + + _menu->addAction( QPixmap(), + AutomatableModel::tr( "Remove song-global automation" ), + amvSlots, + SLOT( removeSongGlobalAutomation() ) ); + _menu->addSeparator(); QString controllerTxt; @@ -241,4 +247,11 @@ void AutomatableModelViewSlots::editSongGlobalAutomation() +void AutomatableModelViewSlots::removeSongGlobalAutomation() +{ + delete AutomationPattern::globalAutomationPattern( amv->modelUntyped() ); +} + + + #include "moc_AutomatableModelView.cxx" From 80106138c8bad310d5cefd3ecc7980253a627f4a Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 7 Jan 2013 00:29:25 +0100 Subject: [PATCH 13/46] AutomationPattern: removed magic value at position zero In automation patterns there always had to be a value at position zero which also had a special semantic concerning the initial values of connected objects. However that logic was buggy and confusing. I therefore completely removed the neccessity for a value at position zero (automated value will be 0 until the first set point). --- include/AutomationPattern.h | 4 +- src/core/AutomationPattern.cpp | 145 ++++++++++++--------------------- 2 files changed, 54 insertions(+), 95 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 391283bf2..62bf92d06 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -2,7 +2,7 @@ * AutomationPattern.h - declaration of class AutomationPattern, which contains * all information about an automation pattern * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -109,6 +109,8 @@ public slots: private: + void cleanObjects(); + AutomationTrack * m_autoTrack; QVector m_idsToResolve; objectVector m_objects; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 003f75856..6ec1ce319 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -2,7 +2,7 @@ * AutomationPattern.cpp - implementation of class AutomationPattern which * holds dynamic values * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -45,7 +45,6 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : m_hasAutomation( false ) { changeLength( midiTime( 1, 0 ) ); - m_timeMap[0] = 0; } @@ -101,12 +100,6 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) if( addIt ) { m_objects += _obj; - // been empty before? - if( m_objects.size() == 1 && !hasAutomation() ) - { - // then initialize default-value - putValue( 0, _obj->value(), false ); - } connect( _obj, SIGNAL( destroyed( jo_id_t ) ), this, SLOT( objectDestroyed( jo_id_t ) ), Qt::DirectConnection ); @@ -152,6 +145,8 @@ midiTime AutomationPattern::putValue( const midiTime & _time, const float _value, const bool _quant_pos ) { + cleanObjects(); + midiTime newTime = _quant_pos && engine::automationEditor() ? note::quantized( _time, engine::automationEditor()->quantization() ) : @@ -159,43 +154,7 @@ midiTime AutomationPattern::putValue( const midiTime & _time, m_timeMap[newTime] = _value; - if( newTime == 0 ) - { - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ) - { - if( *it ) - { - ( *it )->setValue( _value ); - ++it; - } - else - { - it = m_objects.erase( it ); - } - } - } - - // just one automation value? - if( m_timeMap.size() == 1 ) - { - m_hasAutomation = m_objects.isEmpty(); // usually false - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) - { - // default value differs from current value? - if( *it && _value != ( *it )->initValue() ) - { - // then enable automating this object - m_hasAutomation = true; - } - } - } - else - { - // in all other cases assume we have automation - m_hasAutomation = true; - } + m_hasAutomation = true; // we need to maximize our length in case we're part of a hidden // automation track as the user can't resize this pattern @@ -214,41 +173,22 @@ midiTime AutomationPattern::putValue( const midiTime & _time, void AutomationPattern::removeValue( const midiTime & _time ) { - if( _time != 0 ) + cleanObjects(); + + m_timeMap.remove( _time ); + + if( getTrack() && + getTrack()->type() == track::HiddenAutomationTrack ) { - m_timeMap.remove( _time ); - - if( m_timeMap.size() == 1 ) - { - const float val = m_timeMap[0]; - m_hasAutomation = false; - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ) - { - if( *it ) - { - ( *it )->setValue( val ); - if( ( *it )->initValue() != val ) - { - m_hasAutomation = true; - } - ++it; - } - else - { - it = m_objects.erase( it ); - } - } - } - - if( getTrack() && - getTrack()->type() == track::HiddenAutomationTrack ) - { - changeLength( length() ); - } - - emit dataChanged(); + changeLength( length() ); } + + if( m_timeMap.isEmpty() ) + { + m_hasAutomation = false; + } + + emit dataChanged(); } @@ -260,10 +200,22 @@ float AutomationPattern::valueAt( const midiTime & _time ) const { return 0; } - timeMap::const_iterator v = m_timeMap.lowerBound( _time ); + + if( m_timeMap.contains( _time ) ) + { + return m_timeMap[_time]; + } + // lowerBound returns next value with greater key, therefore we take // the previous element to get the current value - return ( v != m_timeMap.begin() ) ? (v-1).value() : v.value(); + timeMap::ConstIterator v = m_timeMap.lowerBound( _time ); + + if( v == m_timeMap.begin() ) + { + return 0; + } + + return (v-1).value(); } @@ -326,17 +278,7 @@ void AutomationPattern::loadSettings( const QDomElement & _this ) } m_hasAutomation = m_timeMap.size() > 0; - if( m_hasAutomation == false ) - { - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) - { - if( *it ) - { - ( *it )->setValue( m_timeMap[0] ); - } - } - } + int len = _this.attribute( "len" ).toInt(); if( len <= 0 ) { @@ -366,7 +308,7 @@ const QString AutomationPattern::name() const void AutomationPattern::processMidiTime( const midiTime & _time ) { - if( _time >= 0 && m_hasAutomation ) + if( _time >= 0 && hasAutomation() ) { const float val = valueAt( _time ); for( objectVector::iterator it = m_objects.begin(); @@ -502,9 +444,9 @@ void AutomationPattern::resolveAllIDs() void AutomationPattern::clear() { - const float val = firstObject()->value(); m_timeMap.clear(); - putValue( 0, val ); + + emit dataChanged(); if( engine::automationEditor() && engine::automationEditor()->currentPattern() == this ) @@ -537,6 +479,21 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) +void AutomationPattern::cleanObjects() +{ + for( objectVector::iterator it = m_objects.begin(); it != m_objects.end(); ) + { + if( *it ) + { + ++it; + } + else + { + it = m_objects.erase( it ); + } + } +} + #include "moc_AutomationPattern.cxx" From 59300906d7ea4f1fc8f6856eca17a6305452384a Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 7 Jan 2013 00:36:43 +0100 Subject: [PATCH 14/46] AutomationEditor: fixed drawing of empty automation patterns Due to recent changes to AutomationPattern the inner draw loop needs to be adjusted. --- src/gui/AutomationEditor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/AutomationEditor.cpp b/src/gui/AutomationEditor.cpp index 1593d1c82..d87b7bf09 100644 --- a/src/gui/AutomationEditor.cpp +++ b/src/gui/AutomationEditor.cpp @@ -2,7 +2,7 @@ * AutomationEditor.cpp - implementation of AutomationEditor which is used for * actual setting of dynamic values * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -1424,7 +1424,8 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) timeMap & time_map = m_pattern->getTimeMap(); timeMap::iterator it = time_map.begin(); p.setPen( QColor( 0xFF, 0xDF, 0x20 ) ); - do + + while( it != time_map.end() ) { Sint32 len_ticks = 4; @@ -1516,7 +1517,7 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) } else printf("not in range\n"); ++it; - } while( it != time_map.end() ); + } } else { From 174037c31a1911d8d15cd834758a896d3872e016 Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Mon, 7 Jan 2013 20:21:57 +0100 Subject: [PATCH 15/46] ExportProjectDialog: fix static file extension on multitrack export When using the multitrack export feature the output files always had the extension ".wav", even if exported as OGG. This patch fixes this issue. Closes #3595157. Signed-off-by: Tobias Doerffel --- include/export_project_dialog.h | 1 + src/gui/export_project_dialog.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/export_project_dialog.h b/include/export_project_dialog.h index eb0ba35ec..7f068e4b2 100644 --- a/include/export_project_dialog.h +++ b/include/export_project_dialog.h @@ -59,6 +59,7 @@ private slots: private: QString m_fileName; QString m_dirName; + QString m_fileExtension; typedef QVector RenderVector; RenderVector m_renderers; bool m_multiExport; diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index eba9123ee..2a385093c 100644 --- a/src/gui/export_project_dialog.cpp +++ b/src/gui/export_project_dialog.cpp @@ -39,6 +39,7 @@ exportProjectDialog::exportProjectDialog( const QString & _file_name, QDialog( _parent ), Ui::ExportProjectDialog(), m_fileName( _file_name ), + m_fileExtension(), m_multiExport(multi_export) { setupUi( this ); @@ -200,7 +201,7 @@ void exportProjectDialog::multiRender() m_unmuted.push_back(tk); QString nextName = tk->name(); nextName = nextName.remove(QRegExp("[^a-zA-Z]")); - QString name = QString("%1_%2.wav").arg(x++).arg(nextName); + QString name = QString( "%1_%2%3" ).arg( x++ ).arg( nextName ).arg( m_fileExtension ); m_fileName = QDir(m_dirName).filePath(name); prepRender(); } @@ -221,7 +222,7 @@ void exportProjectDialog::multiRender() m_unmuted.push_back(tk); QString nextName = tk->name(); nextName = nextName.remove(QRegExp("[^a-zA-Z]")); - QString name = QString("%1_%2.wav").arg(x++).arg(nextName); + QString name = QString( "%1_%2%3" ).arg( x++ ).arg( nextName ).arg( m_fileExtension ); m_fileName = QDir(m_dirName).filePath(name); prepRender(); } @@ -292,6 +293,7 @@ void exportProjectDialog::startBtnClicked() __fileEncodeDevices[i].m_description ) ) { m_ft = __fileEncodeDevices[i].m_fileFormat; + m_fileExtension = QString( QLatin1String( __fileEncodeDevices[i].m_extension ) ); break; } } From 9ec76136781097d0c2346515b594d2eceb2460b9 Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Mon, 7 Jan 2013 21:14:04 +0100 Subject: [PATCH 16/46] InstrumentTrack: Add support for more MIDI commands MIDI commands All Notes Off, All Sound Off and Omni/Mono/Poly mode will now silence all playing notes, as they should. Signed-off-by: Tobias Doerffel --- include/midi.h | 10 ++++++++++ src/tracks/InstrumentTrack.cpp | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/midi.h b/include/midi.h index 61a9e8ebf..71a1d0f57 100644 --- a/include/midi.h +++ b/include/midi.h @@ -98,6 +98,16 @@ enum MidiStandardControllers MidiControllerSostenuto = 66, MidiControllerSoftPedal = 67, MidiControllerLegatoFootswitch = 68, + // Channel Mode Messages are controllers too... + MidiControllerAllSoundOff = 120, + MidiControllerResetAllControllers = 121, + MidiControllerLocalControl = 122, + MidiControllerAllNotesOff = 123, + MidiControllerOmniOn = 124, + MidiControllerOmniOff = 125, + MidiControllerMonoOn = 126, + MidiControllerPolyOn = 127, + }; const int MidiChannelCount = 16; diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 267af765b..016c54d3d 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -315,6 +315,17 @@ void InstrumentTrack::processInEvent( const midiEvent & _me, m_sustainPedalPressed = false; } } + if( _me.controllerNumber() == MidiControllerAllSoundOff || + _me.controllerNumber() == MidiControllerAllNotesOff || + _me.controllerNumber() == MidiControllerOmniOn || + _me.controllerNumber() == MidiControllerOmniOff || + _me.controllerNumber() == MidiControllerMonoOn || + _me.controllerNumber() == MidiControllerPolyOn ) + { + silenceAllNotes(); + } + m_instrument->handleMidiEvent( _me, _time ); + break; case MidiProgramChange: m_instrument->handleMidiEvent( _me, _time ); From e3e2e48b71a608bdbdb13fde50e1c826608a24f0 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 7 Jan 2013 22:06:37 +0100 Subject: [PATCH 17/46] MainWindow: pass optional parameter to toggleWindow() to force showing window There are use cases where we want to force to show a certain window. --- include/MainWindow.h | 6 +++--- src/gui/MainWindow.cpp | 23 ++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index a76fc320c..6335c6401 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -1,7 +1,7 @@ /* * main_window.h - declaration of class MainWindow, the main window of LMMS * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -106,7 +106,7 @@ public slots: void aboutLMMS( void ); void help( void ); void toggleAutomationEditorWin( void ); - void toggleBBEditorWin( void ); + void toggleBBEditorWin( bool forceShow = false ); void toggleSongEditorWin( void ); void toggleProjectNotesWin( void ); void toggleFxMixerWin( void ); @@ -132,7 +132,7 @@ private: void finalize( void ); - void toggleWindow( QWidget * _w ); + void toggleWindow( QWidget *window, bool forceShow = false ); QMdiArea * m_workspace; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 8a70355fd..98bf7e408 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1,9 +1,7 @@ -#ifndef SINGLE_SOURCE_COMPILE - /* * main_window.cpp - implementation of LMMS-main-window * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -810,16 +808,17 @@ void MainWindow::help( void ) -void MainWindow::toggleWindow( QWidget * _w ) +void MainWindow::toggleWindow( QWidget *window, bool forceShow ) { - QWidget * parent = _w->parentWidget(); + QWidget *parent = window->parentWidget(); - if( m_workspace->activeSubWindow() != parent - || parent->isHidden() ) + if( forceShow || + m_workspace->activeSubWindow() != parent || + parent->isHidden() ) { parent->show(); - _w->show(); - _w->setFocus(); + window->show(); + window->setFocus(); } else { @@ -836,9 +835,9 @@ void MainWindow::toggleWindow( QWidget * _w ) -void MainWindow::toggleBBEditorWin( void ) +void MainWindow::toggleBBEditorWin( bool forceShow ) { - toggleWindow( engine::getBBEditor() ); + toggleWindow( engine::getBBEditor(), forceShow ); } @@ -1072,5 +1071,3 @@ void MainWindow::autoSave() #include "moc_MainWindow.cxx" - -#endif From 27e2b5e4ea557b24762aa045271081231c698d32 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 7 Jan 2013 22:07:52 +0100 Subject: [PATCH 18/46] BbTrack: fixed openInBBEditor() to reliably show the BB Editor Use the function provided by MainWindow to reliably show the BB Editor. Thanks to Tres Finocchiaro for pointing out this issue. --- src/tracks/bb_track.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index db258a7f8..f4ba75dbc 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -1,7 +1,7 @@ /* * bb_track.cpp - implementation of class bbTrack and bbTCO * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -33,6 +33,7 @@ #include "embed.h" #include "engine.h" #include "gui_templates.h" +#include "MainWindow.h" #include "mixer.h" #include "rename_dialog.h" #include "song.h" @@ -223,10 +224,9 @@ void bbTCOView::paintEvent( QPaintEvent * ) void bbTCOView::openInBBEditor() { - engine::getBBTrackContainer()->setCurrentBB( bbTrack::numOfBBTrack( - m_bbTCO->getTrack() ) ); - engine::getBBEditor()->show(); - engine::getBBEditor()->setFocus(); + engine::getBBTrackContainer()->setCurrentBB( bbTrack::numOfBBTrack( m_bbTCO->getTrack() ) ); + + engine::mainWindow()->toggleBBEditorWin( true ); } From 128d94b261708f3f0859b3010eeb8b416440fde2 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 16:56:49 +0100 Subject: [PATCH 19/46] German localization: fixed wrong chord name translation The chord name 6add9 should stay 6add9. --- data/locale/de.qm | Bin 191705 -> 191705 bytes data/locale/de.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/data/locale/de.qm b/data/locale/de.qm index 57adb48786c123df98d171a13d94f9d3a9661295..0d300fb4e46ff3368201799955f0875081c24e15 100644 GIT binary patch delta 21 dcmcb4ll$gP?hXGN7|og*+Zq|SH8S1I1^|393TXfU delta 26 icmcb4ll$gP?hXGNCUZ^{ZWd~67iwhOF4V|$JR1P8nhTi# diff --git a/data/locale/de.ts b/data/locale/de.ts index 6c22a9ae8..cdeb4810d 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -539,7 +539,7 @@ If you're interested in translating LMMS in another language or want to imp 6add9 - madd9 + 6add9 m6 From 1a981f50c85e68987df1f77a841c2393edf608d7 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 16:57:35 +0100 Subject: [PATCH 20/46] InstrumentFunctions/ChordCreator: fixed wrong 6add9 chord The 6add9 played a normal major chord which is wrong and has been fixed. Thanks to Mike804 for reporting this bug. Closes #3600618. --- src/core/InstrumentFunctions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 30c3aef6b..5b663b401 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -1,7 +1,7 @@ /* * InstrumentFunctions.cpp - models for instrument-function-tab * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -48,7 +48,7 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "6" ), { 0, 4, 7, 9, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "6sus4" ), { 0, 5, 7, 9, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "6add9" ), { 0, 4, 7, 12, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "6add9" ), { 0, 4, 7, 9, 14, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "m6" ), { 0, 3, 7, 9, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "m6add9" ), { 0, 3, 7, 9, 14, -1 } }, From 4a1642abc7d3369fb0cab5a2f641287c873d1d20 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 17:45:51 +0100 Subject: [PATCH 21/46] InstrumentFunctions/ChordCreator: fixed wrong 6add9 chord Various seventh chords were wrong and have been fixed. Thanks to Mike804 for pointing out issues with some chords. Closes #3600618. --- src/core/InstrumentFunctions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 5b663b401..05804d4c6 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -56,11 +56,11 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "7sus4" ), { 0, 5, 7, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7#5" ), { 0, 4, 8, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7b5" ), { 0, 4, 6, 10, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#9" ), { 0, 4, 7, 10, 13, 18, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7b9" ), { 0, 4, 7, 10, 13, 16, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#5#9" ), { 0, 4, 8, 12, 14, 19, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#5b9" ), { 0, 4, 8, 12, 14, 17, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7b5b9" ), { 0, 4, 6, 10, 12, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#9" ), { 0, 4, 7, 10, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7b9" ), { 0, 4, 7, 10, 13, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#5#9" ), { 0, 4, 8, 10, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#5b9" ), { 0, 4, 8, 10, 13, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7b5b9" ), { 0, 4, 6, 10, 13, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7add11" ), { 0, 4, 7, 10, 17, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7add13" ), { 0, 4, 7, 10, 21, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7#11" ), { 0, 4, 7, 10, 18, -1 } }, From 6940ceca44e99975c09fb6a680d00974dd693755 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 18:13:18 +0100 Subject: [PATCH 22/46] InstrumentFunctions: added natural minor scale A natural minor scale was missing which lead to some irritations about the harmonic and melodic minor scales. Had to add the new scale at the end of the chord table in order to not break existing projects and presets in which simple chord table indices are saved. Closes #3594824. --- src/core/InstrumentFunctions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 05804d4c6..5868d19b4 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -130,6 +130,7 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "Mixolydian" ), { 0, 2, 4, 5, 7, 9, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Aeolian" ), { 0, 2, 3, 5, 7, 8, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Locrian" ), { 0, 1, 3, 5, 6, 8, 10, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "Minor" ), { 0, 2, 3, 5, 7, 8, 10, -1 } }, } ; From 99efe8aef32dc8033efb4e78110582ecf6d67da9 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 18:48:35 +0100 Subject: [PATCH 23/46] LocalZynAddSubFx: include/forward MIDI channel information When sending MIDI events to the ZynAddSubFX engine do not statically send them on channel 0 but on the MIDI channel which is set as output MIDI channel of the instrument track. This adds some flexibility when it comes to multipart ZynAddSubFX presets which are connected to different MIDI channels. --- plugins/zynaddsubfx/LocalZynAddSubFx.cpp | 12 ++++++------ plugins/zynaddsubfx/ZynAddSubFx.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp index bd270816f..24e4ff182 100644 --- a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp @@ -1,7 +1,7 @@ /* * LocalZynAddSubFx.cpp - local implementation of ZynAddSubFx plugin * - * Copyright (c) 2009-2010 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -206,10 +206,10 @@ void LocalZynAddSubFx::processMidiEvent( const midiEvent & _e ) } if( m_runningNotes[_e.key()] > 0 ) { - m_master->NoteOff( 0, _e.key() ); + m_master->NoteOff( _e.channel(), _e.key() ); } ++m_runningNotes[_e.key()]; - m_master->NoteOn( 0, _e.key(), _e.velocity() ); + m_master->NoteOn( _e.channel(), _e.key(), _e.velocity() ); break; } case MidiNoteOff: @@ -219,16 +219,16 @@ void LocalZynAddSubFx::processMidiEvent( const midiEvent & _e ) } if( --m_runningNotes[_e.key()] <= 0 ) { - m_master->NoteOff( 0, _e.key() ); + m_master->NoteOff( _e.channel(), _e.key() ); } break; case MidiPitchBend: - m_master->SetController( 0, C_pitchwheel, + m_master->SetController( _e.channel(), C_pitchwheel, _e.m_data.m_param[0] + _e.m_data.m_param[1]*128-8192 ); break; case MidiControlChange: - m_master->SetController( 0, + m_master->SetController( _e.channel(), midiIn.getcontroller( _e.m_data.m_param[0] ), _e.m_data.m_param[1] ); break; diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index f1483c19b..5ad842a00 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -1,7 +1,7 @@ /* * ZynAddSubFx.cpp - ZynAddSubxFX-embedding plugin * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -450,7 +450,7 @@ void ZynAddSubFxInstrument::initPlugin() void ZynAddSubFxInstrument::sendControlChange( MidiControllers midiCtl, float value ) { - handleMidiEvent( midiEvent( MidiControlChange, 0, midiCtl, (int) value, this ), + handleMidiEvent( midiEvent( MidiControlChange, instrumentTrack()->midiPort()->realOutputChannel(), midiCtl, (int) value, this ), midiTime() ); } From b784d3daf05a3b5943b960a28795c8f8d77dc730 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Tue, 8 Jan 2013 12:59:46 +0100 Subject: [PATCH 24/46] VST Effects: fix for uncontrollable plugin window Supposed fix for 3595560, see also http://sourceforge.net/apps/phpbb/lmms/viewtopic.php?f=7&t=778 http://sourceforge.net/tracker/?func=detail&aid=3595560&group_id=105168&atid=640434 VST effects pluginWidget is not deleted, when we close General Settings of instrument track, on which are directly attached VST effects, but only when we remove this VST effects or tracks itself. Signed-off-by: Tobias Doerffel --- plugins/vst_effect/VstEffect.cpp | 4 ++++ plugins/vst_effect/VstEffectControlDialog.cpp | 2 +- src/gui/widgets/pixmap_button.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/vst_effect/VstEffect.cpp b/plugins/vst_effect/VstEffect.cpp index 2ada8ca14..a6286d38e 100644 --- a/plugins/vst_effect/VstEffect.cpp +++ b/plugins/vst_effect/VstEffect.cpp @@ -162,6 +162,10 @@ void VstEffect::openPlugin( const QString & _plugin ) void VstEffect::closePlugin() { m_pluginMutex.lock(); + if( m_plugin->pluginWidget() != NULL ) + { + delete m_plugin->pluginWidget(); + } delete m_plugin; m_plugin = NULL; m_pluginMutex.unlock(); diff --git a/plugins/vst_effect/VstEffectControlDialog.cpp b/plugins/vst_effect/VstEffectControlDialog.cpp index bb012b93b..ce598fade 100644 --- a/plugins/vst_effect/VstEffectControlDialog.cpp +++ b/plugins/vst_effect/VstEffectControlDialog.cpp @@ -226,7 +226,7 @@ void VstEffectControlDialog::paintEvent( QPaintEvent * ) VstEffectControlDialog::~VstEffectControlDialog() { - delete m_pluginWidget; + //delete m_pluginWidget; } diff --git a/src/gui/widgets/pixmap_button.cpp b/src/gui/widgets/pixmap_button.cpp index 0320b4932..795915da5 100644 --- a/src/gui/widgets/pixmap_button.cpp +++ b/src/gui/widgets/pixmap_button.cpp @@ -57,7 +57,7 @@ void pixmapButton::paintEvent( QPaintEvent * ) { QPainter p( this ); - if( model()->value() || m_pressed ) + if( model() != NULL && model()->value() || m_pressed ) { if( !m_activePixmap.isNull() ) { From d943182a6c7af0cd0bb5b8dadf37276d94d4bf58 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 13 Jan 2013 19:07:12 +0100 Subject: [PATCH 25/46] ZynAddSubFX: increased polyphony to 128 It occured to me that ZASF reported polyphony overflows when using it in combination with LMMS' arpeggio and chord functionality. --- plugins/zynaddsubfx/src/globals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/zynaddsubfx/src/globals.h b/plugins/zynaddsubfx/src/globals.h index 7c0717168..9f52c3569 100644 --- a/plugins/zynaddsubfx/src/globals.h +++ b/plugins/zynaddsubfx/src/globals.h @@ -93,7 +93,7 @@ extern int OSCIL_SIZE; /* * The poliphony (notes) */ -#define POLIPHONY 60 +#define POLIPHONY 128 /* * Number of system effects From 071358bd98f7b039044d6f80c403fe4c66d0124b Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 14 Jan 2013 23:06:02 +0100 Subject: [PATCH 26/46] PixmapButton: added parentheses to fix ambiguity Fixes compiler warning. --- src/gui/widgets/pixmap_button.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/pixmap_button.cpp b/src/gui/widgets/pixmap_button.cpp index 795915da5..a7b5bcf64 100644 --- a/src/gui/widgets/pixmap_button.cpp +++ b/src/gui/widgets/pixmap_button.cpp @@ -2,7 +2,7 @@ * pixmap_button.cpp - implementation of pixmap-button (often used as "themed" * checkboxes/radiobuttons etc) * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -57,7 +57,7 @@ void pixmapButton::paintEvent( QPaintEvent * ) { QPainter p( this ); - if( model() != NULL && model()->value() || m_pressed ) + if( ( model() != NULL && model()->value() ) || m_pressed ) { if( !m_activePixmap.isNull() ) { From a184bc039e8ac3064491772fd4d1d512619ac903 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 14 Jan 2013 23:13:21 +0100 Subject: [PATCH 27/46] Sf2Player: update track name after loading file For consistent behaviour with other plugins such as AudioFileProcessor and ZynAddSubFX update the track name after loading a file. --- plugins/sf2_player/sf2_player.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 93ddb324e..453ac05e6 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -2,7 +2,7 @@ * sf2_player.cpp - a soundfont2 player using fluidSynth * * Copyright (c) 2008 Paul Giblock - * Copyright (c) 2009-2010 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -389,6 +389,8 @@ void sf2Instrument::openFile( const QString & _sf2File ) } delete[] sf2Ascii; + + instrumentTrack()->setName( QFileInfo( _sf2File ).baseName() ); } From 1c9c76f3994a2054b5f4cb2f6b24c7f9b7fd1a10 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Tue, 15 Jan 2013 00:13:21 +0100 Subject: [PATCH 28/46] VST GUI fixes, improvements Various fixes improvements: + Added support for VST parameters control for windows. + New `Close` button for VST parameter controls. + Faster GUI for all instruments, effects-loading, not only VSTs, and both one-instrument track window mode and normal window mode should be supported. + Better integration for VST GUIs on Linux, e.g. plugin window should not stay always on top of other windows. + VST GUI overlook should remain same with different wine setups ( except for whole virtual desktops emulations ). + VST effect control window merged with VST effect editor window should be more easier to control. + Little corections at effectviews model updates of instrument tracks effect chains. Signed-off-by: Tobias Doerffel --- plugins/vestige/vestige.cpp | 34 ++++-- plugins/vestige/vestige.h | 2 + plugins/vst_base/RemoteVstPlugin.cpp | 8 +- plugins/vst_base/VstPlugin.cpp | 45 ++++++-- plugins/vst_base/VstPlugin.h | 2 +- plugins/vst_effect/VstEffectControlDialog.cpp | 100 +++++++++++------- plugins/vst_effect/VstEffectControlDialog.h | 2 + plugins/vst_effect/VstEffectControls.cpp | 20 +++- plugins/vst_effect/VstEffectControls.h | 2 + src/gui/widgets/EffectRackView.cpp | 23 ++-- src/gui/widgets/EffectView.cpp | 16 ++- src/tracks/InstrumentTrack.cpp | 6 +- 12 files changed, 190 insertions(+), 70 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 4a5a3d456..eeb536f58 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -263,7 +263,7 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } - m_plugin->showEditor(); + m_plugin->showEditor( NULL, false ); if( set_ch_name ) { @@ -743,8 +743,7 @@ void VestigeInstrumentView::selPreset( void ) void VestigeInstrumentView::toggleGUI( void ) { - QMutexLocker ml( &m_vi->m_pluginMutex ); - if( m_vi->m_plugin == NULL ) + if( m_vi == NULL || m_vi->m_plugin == NULL ) { return; } @@ -848,14 +847,15 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) p.setPen( QColor( 251, 41, 8 ) ); f.setBold( false ); p.setFont( pointSize<8>( f ) ); - p.drawText( 10, 114, tr( "by" ) + " " + + p.drawText( 10, 114, tr( "by " ) + m_vi->m_plugin->vendorString() ); p.drawText( 10, 225, m_vi->m_plugin->currentProgramName() ); } if( m_vi->m_subWindow != NULL ) { - m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() ); + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); } // m_pluginMutex.unlock(); } @@ -877,7 +877,8 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); m_vi->m_subWindow->setWidget(m_vi->m_scrollArea); - m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() ); + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); //m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose); @@ -902,6 +903,16 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + m_closeButton = new QPushButton( tr( " Close " ), widget ); + connect( m_closeButton, SIGNAL( clicked() ), this, + SLOT( closeWindow() ) ); + m_closeButton->setWhatsThis( + tr( "Close VST plugin knob-controller window." ) ); + + l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); + + for( int i = 0; i < 10; i++ ) { l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); @@ -973,6 +984,14 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume +void manageVestigeInstrumentView::closeWindow() +{ + m_vi->m_subWindow->hide(); +} + + + + void manageVestigeInstrumentView::syncPlugin( void ) { char paramStr[35]; @@ -1119,7 +1138,8 @@ void manageVestigeInstrumentView::dropEvent( QDropEvent * _de ) void manageVestigeInstrumentView::paintEvent( QPaintEvent * ) { - m_vi->m_subWindow->setWindowTitle(m_vi->instrumentTrack()->name()); + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); } diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 299cdda3e..600e87e22 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -113,6 +113,7 @@ protected slots: void syncPlugin( void ); void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); protected: @@ -130,6 +131,7 @@ private: QGridLayout * l; QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; + QPushButton * m_closeButton; } ; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 40d02cacb..2344d1060 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -558,10 +558,12 @@ void RemoteVstPlugin::initEditor() } #ifdef LMMS_BUILD_LINUX - m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), - ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, - 0, 0, 10, 10, NULL, NULL, hInst, NULL ); + //m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), + // ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, + // 0, 0, 10, 10, NULL, NULL, hInst, NULL ); + m_window = CreateWindowEx( 0 , "LVSL", m_shortName.c_str(), + WS_POPUP | WS_SYSMENU | WS_BORDER , 0, 0, 10, 10, NULL, NULL, hInst, NULL); #else m_windowID = 1; // arbitrary value on win32 to signal // vstPlugin-class that we have an editor diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index c986de2d8..e548f3a9b 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -49,6 +49,7 @@ #include "MainWindow.h" #include "song.h" #include "templates.h" +#include class vstSubWin : public QMdiSubWindow @@ -200,11 +201,24 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) -void VstPlugin::showEditor( QWidget * _parent ) +void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) { QWidget * w = pluginWidget(); if( w ) { +#ifdef LMMS_BUILD_WIN32 + // hide sw, plugin window wrapper on win32 + // this is obtained from pluginWidget() + if( isEffect ) + { + w->setWindowFlags( Qt::FramelessWindowHint ); + w->setAttribute( Qt::WA_TranslucentBackground ); + } + else + { + w->setWindowFlags( Qt::WindowCloseButtonHint ); + } +#endif w->show(); return; } @@ -222,13 +236,30 @@ void VstPlugin::showEditor( QWidget * _parent ) { vstSubWin * sw = new vstSubWin( engine::mainWindow()->workspace() ); - sw->setWidget( m_pluginWidget ); + if( isEffect ) + { + sw->setAttribute( Qt::WA_TranslucentBackground ); + sw->setWindowFlags( Qt::FramelessWindowHint ); + sw->setWidget( m_pluginWidget ); + + QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); + xe->show(); + } + else + { + sw->setWindowFlags( Qt::WindowCloseButtonHint ); + sw->setWidget( m_pluginWidget ); + + QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); + xe->move( 4, 24 ); + xe->show(); + } } - QX11EmbedContainer * xe = new QX11EmbedContainer( m_pluginWidget ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->show(); #endif if( m_pluginWidget ) @@ -258,7 +289,7 @@ void VstPlugin::loadSettings( const QDomElement & _this ) { if( _this.attribute( "guivisible" ).toInt() ) { - showEditor(); + showEditor( NULL, false ); } else { diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index c932d50aa..992e59ff0 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -53,7 +53,7 @@ public: return m_pluginWindowID != 0; } - void showEditor( QWidget * _parent = NULL ); + void showEditor( QWidget * _parent = NULL, bool isEffect = false ); void hideEditor(); inline const QString & name() const diff --git a/plugins/vst_effect/VstEffectControlDialog.cpp b/plugins/vst_effect/VstEffectControlDialog.cpp index ce598fade..a77fcdf73 100644 --- a/plugins/vst_effect/VstEffectControlDialog.cpp +++ b/plugins/vst_effect/VstEffectControlDialog.cpp @@ -36,34 +36,50 @@ #include #include - +#include "gui_templates.h" + VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), - m_pluginWidget( NULL ) + m_pluginWidget( NULL ), + m_plugin( NULL ) { QGridLayout * l = new QGridLayout( this ); - l->setContentsMargins( 20, 10, 10, 10 ); + l->setContentsMargins( 10, 10, 10, 10 ); l->setVerticalSpacing( 2 ); l->setHorizontalSpacing( 2 ); -#ifdef LMMS_BUILD_LINUX - _ctl->m_effect->m_plugin->showEditor(); - m_pluginWidget = _ctl->m_effect->m_plugin->pluginWidget(); + if( _ctl != NULL && _ctl->m_effect != NULL && + _ctl->m_effect->m_plugin != NULL ) + { + m_plugin = _ctl->m_effect->m_plugin; + m_plugin->showEditor( NULL, true ); + m_pluginWidget = m_plugin->pluginWidget(); + +#ifdef LMMS_BUILD_WIN32 + + if( !m_pluginWidget ) + { + m_pluginWidget = m_plugin->pluginWidget( false ); + } +#endif + } + if( m_pluginWidget ) { setWindowTitle( m_pluginWidget->windowTitle() ); QPushButton * btn = new QPushButton( tr( "Show/hide VST FX GUI" ) ); btn->setCheckable( true ); - l->addWidget( btn, 0, 0, 1, 13, Qt::AlignCenter ); + l->addWidget( btn, 0, 0, 1, 80, Qt::AlignLeft ); connect( btn, SIGNAL( toggled( bool ) ), m_pluginWidget, SLOT( setVisible( bool ) ) ); + emit btn->click(); + btn->setMinimumWidth( 200 ); btn->setMaximumWidth( 200 ); btn->setMinimumHeight( 24 ); btn->setMaximumHeight( 24 ); - m_managePluginButton = new pixmapButton( this, "" ); m_managePluginButton->setCheckable( false ); m_managePluginButton->setCursor( Qt::PointingHandCursor ); @@ -78,13 +94,11 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_managePluginButton->setWhatsThis( tr( "Click here, if you want to control VST-plugin from host." ) ); - m_managePluginButton->setMinimumWidth( 21 ); m_managePluginButton->setMaximumWidth( 21 ); m_managePluginButton->setMinimumHeight( 21 ); m_managePluginButton->setMaximumHeight( 21 ); - m_openPresetButton = new pixmapButton( this, "" ); m_openPresetButton->setCheckable( false ); m_openPresetButton->setCursor( Qt::PointingHandCursor ); @@ -104,7 +118,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_openPresetButton->setMinimumHeight( 16 ); m_openPresetButton->setMaximumHeight( 16 ); - m_rolLPresetButton = new pixmapButton( this, "" ); m_rolLPresetButton->setCheckable( false ); m_rolLPresetButton->setCursor( Qt::PointingHandCursor ); @@ -114,6 +127,10 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : "stepper-left" ) ); connect( m_rolLPresetButton, SIGNAL( clicked() ), _ctl, SLOT( rolrPreset() ) ); + + connect( m_rolLPresetButton, SIGNAL( clicked() ), this, + SLOT( update() ) ); + toolTip::add( m_rolLPresetButton, tr( "Previous (-)" ) ); m_rolLPresetButton->setShortcut( Qt::Key_Minus ); @@ -126,7 +143,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_rolLPresetButton->setMinimumHeight( 16 ); m_rolLPresetButton->setMaximumHeight( 16 ); - m_rolRPresetButton = new pixmapButton( this, "" ); m_rolRPresetButton->setCheckable( false ); m_rolRPresetButton->setCursor( Qt::PointingHandCursor ); @@ -136,6 +152,10 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : "stepper-right" ) ); connect( m_rolRPresetButton, SIGNAL( clicked() ), _ctl, SLOT( rollPreset() ) ); + + connect( m_rolRPresetButton, SIGNAL( clicked() ), this, + SLOT( update() ) ); + toolTip::add( m_rolRPresetButton, tr( "Next (+)" ) ); m_rolRPresetButton->setShortcut( Qt::Key_Plus ); @@ -148,8 +168,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_rolRPresetButton->setMinimumHeight( 16 ); m_rolRPresetButton->setMaximumHeight( 16 ); - - _ctl->m_selPresetButton = new QPushButton( tr( "" ), this ); _ctl->m_selPresetButton->setCheckable( false ); @@ -160,13 +178,11 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : _ctl->m_selPresetButton->setMenu(_ctl->menu); - _ctl->m_selPresetButton->setMinimumWidth( 16 ); _ctl->m_selPresetButton->setMaximumWidth( 16 ); _ctl->m_selPresetButton->setMinimumHeight( 16 ); _ctl->m_selPresetButton->setMaximumHeight( 16 ); - m_savePresetButton = new pixmapButton( this, "" ); m_savePresetButton->setCheckable( false ); m_savePresetButton->setCursor( Qt::PointingHandCursor ); @@ -181,36 +197,28 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_savePresetButton->setWhatsThis( tr( "Click here, if you want to save current VST-plugin preset program." ) ); - m_savePresetButton->setMinimumWidth( 21 ); m_savePresetButton->setMaximumWidth( 21 ); m_savePresetButton->setMinimumHeight( 21 ); m_savePresetButton->setMaximumHeight( 21 ); + for( int i = 0; i < 13; i++ ) + { + l->addItem( new QSpacerItem( 15, 30, QSizePolicy::Fixed, + QSizePolicy::Fixed ), 1, i ); + } + l->addWidget( m_openPresetButton, 1, 6, 1, 1, Qt::AlignLeft ); + l->addWidget( m_rolLPresetButton, 1, 3, 1, 1, Qt::AlignLeft ); + l->addWidget( m_rolRPresetButton, 1, 4, 1, 1, Qt::AlignLeft ); + l->addWidget( _ctl->m_selPresetButton, 1, 5, 1, 1, Qt::AlignLeft ); - l->addWidget( m_openPresetButton, 1, 7, Qt::AlignCenter ); - l->addWidget( m_rolLPresetButton, 1, 4, Qt::AlignCenter ); - l->addWidget( m_rolRPresetButton, 1, 5, Qt::AlignCenter ); - l->addWidget(_ctl->m_selPresetButton, 1, 6, Qt::AlignLeft ); - - l->addWidget( m_managePluginButton, 1, 10, 2, 2, Qt::AlignLeft ); - - l->addWidget( m_savePresetButton, 1, 8, 2, 2, Qt::AlignCenter ); - - l->setRowStretch( 3, 1 ); - l->setColumnStretch( 13, 1 ); + l->addWidget( m_savePresetButton, 1, 7, 2, 2, Qt::AlignLeft ); + l->addWidget( m_managePluginButton, 1, 8, 2, 2, Qt::AlignCenter ); + l->addWidget( m_pluginWidget, 3, 0, 1, 80, Qt::AlignCenter ); + l->setRowStretch( 5, 1 ); + l->setColumnStretch( 80, 1 ); } -#endif -#ifdef LMMS_BUILD_WIN32 - _ctl->m_effect->m_plugin->showEditor( this ); - QWidget * w = _ctl->m_effect->m_plugin->pluginWidget( false ); - if( w ) - { - setWindowTitle( w->windowTitle() ); - l->addWidget( w ); - } -#endif } @@ -218,7 +226,23 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : void VstEffectControlDialog::paintEvent( QPaintEvent * ) { + if( m_plugin != NULL ) + { + QString plugin_name = m_plugin->name(); + QPainter p( this ); + QFont f = p.font(); + f.setBold( true ); + p.setFont( pointSize<10>( f ) ); + p.setPen( QColor( 32, 160, 54 ) ); + p.drawText( 225, 20, plugin_name ); + p.setPen( QColor( 251, 41, 8 ) ); + f.setBold( false ); + p.setFont( pointSize<8>( f ) ); + p.drawText( 225, 34, tr( "Effect by: " ) + m_plugin->vendorString() ); + p.drawText( 225, 47, m_plugin->currentProgramName() ); + + } } diff --git a/plugins/vst_effect/VstEffectControlDialog.h b/plugins/vst_effect/VstEffectControlDialog.h index bd4687511..9ca82408e 100644 --- a/plugins/vst_effect/VstEffectControlDialog.h +++ b/plugins/vst_effect/VstEffectControlDialog.h @@ -26,6 +26,7 @@ #define _VST_EFFECT_CONTROL_DIALOG_H #include "EffectControlDialog.h" +#include "VstPlugin.h" #include #include @@ -57,6 +58,7 @@ private: pixmapButton * m_managePluginButton; pixmapButton * m_savePresetButton; + VstPlugin * m_plugin; } ; #endif diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index 3b1fd16ce..c8aeaf075 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -314,7 +314,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); m_vi->m_subWindow->setWidget(m_vi->m_scrollArea); - m_vi->m_subWindow->setWindowTitle(_eff->m_plugin->name()); + m_vi->m_subWindow->setWindowTitle( _eff->m_plugin->name() + tr( " - VST parameter control" ) ); m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); //m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose); @@ -339,6 +339,16 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + m_closeButton = new QPushButton( tr( " Close " ), widget ); + connect( m_closeButton, SIGNAL( clicked() ), this, + SLOT( closeWindow() ) ); + m_closeButton->setWhatsThis( + tr( "Close VST effect knob-controller window." ) ); + + l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); + + for( int i = 0; i < 10; i++ ) { l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); @@ -413,6 +423,14 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * +void manageVSTEffectView::closeWindow() +{ + m_vi2->m_subWindow->hide(); +} + + + + void manageVSTEffectView::syncPlugin( void ) { char paramStr[35]; diff --git a/plugins/vst_effect/VstEffectControls.h b/plugins/vst_effect/VstEffectControls.h index 67622196f..bc33a2088 100644 --- a/plugins/vst_effect/VstEffectControls.h +++ b/plugins/vst_effect/VstEffectControls.h @@ -115,6 +115,7 @@ protected slots: void syncPlugin( void ); void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); private: @@ -131,6 +132,7 @@ private: QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; + QPushButton * m_closeButton; } ; diff --git a/src/gui/widgets/EffectRackView.cpp b/src/gui/widgets/EffectRackView.cpp index ad38e17f9..31ab4411e 100644 --- a/src/gui/widgets/EffectRackView.cpp +++ b/src/gui/widgets/EffectRackView.cpp @@ -175,29 +175,36 @@ void EffectRackView::update() Qt::QueuedConnection ); view->show(); m_effectViews.append( view ); - view_map[i] = true; + if( i < view_map.size() ) + { + view_map[i] = true; + } + else + { + view_map.append( true ); + } } } - int i = m_lastY = 0; + int i = m_lastY = 0, nView = 0; for( QVector::Iterator it = m_effectViews.begin(); - it != m_effectViews.end(); ) + it != m_effectViews.end(); i++ ) { - if( i < view_map.size() && i < m_effectViews.size() && - view_map[i] == false ) + if( i < view_map.size() && view_map[i] == false ) { - delete m_effectViews[i]; + delete m_effectViews[nView]; it = m_effectViews.erase( it ); } else { ( *it )->move( 0, m_lastY ); m_lastY += ( *it )->height(); + ++nView; ++it; - ++i; } } + w->setFixedSize( 210, m_lastY ); QWidget::update(); @@ -242,7 +249,7 @@ void EffectRackView::addEffect() void EffectRackView::modelChanged() { - clearViews(); + //clearViews(); m_effectsGroupBox->setModel( &fxChain()->m_enabledModel ); connect( fxChain(), SIGNAL( aboutToClear() ), this, SLOT( clearViews() ) ); diff --git a/src/gui/widgets/EffectView.cpp b/src/gui/widgets/EffectView.cpp index a62ab6f73..0eb94b20f 100644 --- a/src/gui/widgets/EffectView.cpp +++ b/src/gui/widgets/EffectView.cpp @@ -85,6 +85,8 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : "while deciding when to stop processing signals." ) ); + setModel( _model ); + if( effect()->controls()->controlCount() > 0 ) { QPushButton * ctls_btn = new QPushButton( tr( "Controls" ), @@ -94,6 +96,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : ctls_btn->setGeometry( 140, 14, 50, 20 ); connect( ctls_btn, SIGNAL( clicked() ), this, SLOT( editControls() ) ); + m_controlView = effect()->controls()->createView(); if( m_controlView ) { @@ -141,7 +144,8 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : "Right clicking will bring up a context menu where you can change the order " "in which the effects are processed or delete an effect altogether." ) ); - setModel( _model ); + //move above vst effect view creation + //setModel( _model ); } @@ -149,7 +153,15 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : EffectView::~EffectView() { + +#ifdef LMMS_BUILD_LINUX + delete m_subWindow; +#else + // otherwise on win32 build VST GUI can get lost + m_subWindow->hide(); +#endif + } @@ -159,7 +171,7 @@ void EffectView::editControls() { if( m_subWindow ) { - if( !effect()->controls()->isViewVisible() ) + if( !m_subWindow->isVisible() ) { m_subWindow->show(); m_subWindow->raise(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 016c54d3d..4ffe3c22d 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1047,9 +1047,9 @@ void InstrumentTrackView::freeInstrumentTrackWindow() model()->setHook( NULL ); m_window->setInstrumentTrackView( NULL ); m_window->parentWidget()->hide(); - m_window->setModel( - engine::dummyTrackContainer()-> - dummyInstrumentTrack() ); + //m_window->setModel( + // engine::dummyTrackContainer()-> + // dummyInstrumentTrack() ); m_window->updateInstrumentView(); s_windowCache << m_window; } From 32e9ddc6e4cd727cadcce258f9e10b786905935e Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Fri, 18 Jan 2013 09:49:28 +0100 Subject: [PATCH 29/46] VST effect control window re-design New outlook for VST effect control window Signed-off-by: Tobias Doerffel --- plugins/vst_effect/VstEffectControlDialog.cpp | 79 +++++++++++-------- plugins/vst_effect/VstEffectControlDialog.h | 3 + 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/plugins/vst_effect/VstEffectControlDialog.cpp b/plugins/vst_effect/VstEffectControlDialog.cpp index a77fcdf73..2261f2466 100644 --- a/plugins/vst_effect/VstEffectControlDialog.cpp +++ b/plugins/vst_effect/VstEffectControlDialog.cpp @@ -37,12 +37,15 @@ #include #include #include "gui_templates.h" +#include +#include VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), m_pluginWidget( NULL ), - m_plugin( NULL ) + m_plugin( NULL ), + tbLabel( NULL ) { QGridLayout * l = new QGridLayout( this ); l->setContentsMargins( 10, 10, 10, 10 ); @@ -68,15 +71,16 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : if( m_pluginWidget ) { setWindowTitle( m_pluginWidget->windowTitle() ); - QPushButton * btn = new QPushButton( tr( "Show/hide VST FX GUI" ) ); + setMinimumWidth( 250 ); + + QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); btn->setCheckable( true ); - l->addWidget( btn, 0, 0, 1, 80, Qt::AlignLeft ); connect( btn, SIGNAL( toggled( bool ) ), m_pluginWidget, SLOT( setVisible( bool ) ) ); emit btn->click(); - btn->setMinimumWidth( 200 ); - btn->setMaximumWidth( 200 ); + btn->setMinimumWidth( 78 ); + btn->setMaximumWidth( 78 ); btn->setMinimumHeight( 24 ); btn->setMaximumHeight( 24 ); @@ -202,22 +206,40 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_savePresetButton->setMinimumHeight( 21 ); m_savePresetButton->setMaximumHeight( 21 ); - for( int i = 0; i < 13; i++ ) - { - l->addItem( new QSpacerItem( 15, 30, QSizePolicy::Fixed, - QSizePolicy::Fixed ), 1, i ); - } - l->addWidget( m_openPresetButton, 1, 6, 1, 1, Qt::AlignLeft ); - l->addWidget( m_rolLPresetButton, 1, 3, 1, 1, Qt::AlignLeft ); - l->addWidget( m_rolRPresetButton, 1, 4, 1, 1, Qt::AlignLeft ); - l->addWidget( _ctl->m_selPresetButton, 1, 5, 1, 1, Qt::AlignLeft ); - - l->addWidget( m_savePresetButton, 1, 7, 2, 2, Qt::AlignLeft ); - l->addWidget( m_managePluginButton, 1, 8, 2, 2, Qt::AlignCenter ); - l->addWidget( m_pluginWidget, 3, 0, 1, 80, Qt::AlignCenter ); + int newSize = m_pluginWidget->width() + 20; + newSize = (newSize < 250) ? 250 : newSize; + QWidget* resize = new QWidget(this); + resize->resize( newSize, 10 ); + QWidget* space0 = new QWidget(this); + space0->resize(8, 10); + QWidget* space1 = new QWidget(this); + space1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + QFont f( "Arial", 10 ); + l->addItem( new QSpacerItem( newSize - 20, 30, QSizePolicy::Fixed, + QSizePolicy::Fixed ), 1, 0 ); + l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter ); + l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); l->setRowStretch( 5, 1 ); - l->setColumnStretch( 80, 1 ); + l->setColumnStretch( 1, 1 ); + + QToolBar * tb = new QToolBar( this ); + tb->resize( newSize , 32 ); + tb->addWidget(space0); + tb->addWidget( m_rolLPresetButton ); + tb->addWidget( m_rolRPresetButton ); + tb->addWidget( _ctl->m_selPresetButton ); + tb->addWidget( m_openPresetButton ); + tb->addWidget( m_savePresetButton ); + tb->addWidget( m_managePluginButton ); + tb->addWidget( btn ); + tb->addWidget(space1); + + tbLabel = new QLabel( tr( "Effect by: " ), this ); + tbLabel->setFont( pointSize<7>( f ) ); + tbLabel->setTextFormat(Qt::RichText); + tbLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft ); + tb->addWidget( tbLabel ); } } @@ -226,22 +248,11 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : void VstEffectControlDialog::paintEvent( QPaintEvent * ) { - if( m_plugin != NULL ) + if( m_plugin != NULL && tbLabel != NULL ) { - QString plugin_name = m_plugin->name(); - QPainter p( this ); - QFont f = p.font(); - - f.setBold( true ); - p.setFont( pointSize<10>( f ) ); - p.setPen( QColor( 32, 160, 54 ) ); - p.drawText( 225, 20, plugin_name ); - p.setPen( QColor( 251, 41, 8 ) ); - f.setBold( false ); - p.setFont( pointSize<8>( f ) ); - p.drawText( 225, 34, tr( "Effect by: " ) + m_plugin->vendorString() ); - p.drawText( 225, 47, m_plugin->currentProgramName() ); - + tbLabel->setText( tr( "Effect by: " ) + m_plugin->vendorString() + + tr( "       
" ) + + m_plugin->currentProgramName() ); } } diff --git a/plugins/vst_effect/VstEffectControlDialog.h b/plugins/vst_effect/VstEffectControlDialog.h index 9ca82408e..de8b748dd 100644 --- a/plugins/vst_effect/VstEffectControlDialog.h +++ b/plugins/vst_effect/VstEffectControlDialog.h @@ -30,6 +30,7 @@ #include #include +#include class VstEffectControls; @@ -59,6 +60,8 @@ private: pixmapButton * m_savePresetButton; VstPlugin * m_plugin; + + QLabel * tbLabel; } ; #endif From c1a2c6b1fabca7b3a6af8339038f40c700ff1e9b Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Fri, 18 Jan 2013 09:57:51 +0100 Subject: [PATCH 30/46] VST effects: secure master channel freezing Master channel keeps freezing, when VST effects are placed directly into Master channels FxChain, on slower computers. Provided solution prolongs VST initialisation phase, by waiting on two extra messages, which usually follows initialisation and are possibly not yet implemented well. IdSampleRateInformation, IdBufferSizeInformation This seems to prevent Master channel freezes e.g. on VST effects like Tiny-Q. Signed-off-by: Tobias Doerffel --- plugins/vst_base/RemoteVstPlugin.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 2344d1060..1231cb1c4 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -335,7 +335,12 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : { message m = receiveMessage(); processMessage( m ); - if( m.id == IdVstLoadPlugin || m.id == IdQuit ) + //if( m.id == IdVstLoadPlugin || m.id == IdQuit ) + + // IdBufferSizeInformation is sent right after plugin load + // otherwise causes deadlocks to FxMixer/EffectChain + + if( m.id == IdBufferSizeInformation || m.id == IdQuit ) { break; } From edf93d04cbc452c81aba6549f65586bc8b02978a Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 23 Jan 2013 23:08:03 +0100 Subject: [PATCH 31/46] Midi: added new property fromMidiPort This new property is going to signal whether a specific event comes from a MIDI port is was generated internally. Based on patch by nuio , 2013-01-20 --- include/midi.h | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/include/midi.h b/include/midi.h index 71a1d0f57..4452d8762 100644 --- a/include/midi.h +++ b/include/midi.h @@ -1,7 +1,7 @@ /* * midi.h - constants, structs etc. concerning MIDI * - * Copyright (c) 2005-2012 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -132,7 +132,8 @@ struct midiEvent m_metaEvent( MidiMetaInvalid ), m_channel( _channel ), m_sysExData( NULL ), - m_sourcePort( _sourcePort ) + m_sourcePort( _sourcePort ), + m_fromMidiPort( false ) { m_data.m_param[0] = _param1; m_data.m_param[1] = _param2; @@ -144,7 +145,8 @@ struct midiEvent m_metaEvent( MidiMetaInvalid ), m_channel( 0 ), m_sysExData( _sysex_data ), - m_sourcePort( NULL ) + m_sourcePort( NULL ), + m_fromMidiPort( false ) { m_data.m_sysExDataLen = _data_len; } @@ -155,7 +157,8 @@ struct midiEvent m_channel( _copy.m_channel ), m_data( _copy.m_data ), m_sysExData( _copy.m_sysExData ), - m_sourcePort( _copy.m_sourcePort ) + m_sourcePort( _copy.m_sourcePort ), + m_fromMidiPort( _copy.m_fromMidiPort ) { } @@ -222,6 +225,15 @@ struct midiEvent ( (float)( PanningRight - PanningLeft ) ) ); } + void setFromMidiPort( bool enabled ) + { + m_fromMidiPort = enabled; + } + + bool isFromMidiPort() const + { + return m_fromMidiPort; + } MidiEventTypes m_type; // MIDI event type MidiMetaEvents m_metaEvent; // Meta event (mostly unused) @@ -236,6 +248,10 @@ struct midiEvent const char * m_sysExData; const void * m_sourcePort; + +private: + bool m_fromMidiPort; + } ; From 8a6aecfbdcdd3f83069c4f889bfa058e81d954e8 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 23 Jan 2013 23:10:13 +0100 Subject: [PATCH 32/46] MidiPort: set fromMidiPort-flag of MidiEvent instances This completes the functionality introduced with commit edf93d04cbc452c81aba6549f65586bc8b02978a. Based on patch by nuio , 2013-01-20 --- src/core/midi/MidiPort.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 49c61e674..bdf9d42fd 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -2,7 +2,7 @@ * MidiPort.cpp - abstraction of MIDI-ports which are part of LMMS's MIDI- * sequencing system * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -129,20 +129,24 @@ void MidiPort::processInEvent( const midiEvent & _me, const midiTime & _time ) if( inputEnabled() && ( inputChannel()-1 == _me.m_channel || inputChannel() == 0 ) ) { + midiEvent ev = _me; if( _me.m_type == MidiNoteOn || _me.m_type == MidiNoteOff || _me.m_type == MidiKeyPressure ) { - if( _me.key() < 0 || _me.key() >= NumKeys ) + ev.key() = ev.key() + KeysPerOctave; + if( ev.key() < 0 || ev.key() >= NumKeys ) { return; } } - midiEvent ev = _me; + if( fixedInputVelocity() >= 0 && _me.velocity() > 0 ) { ev.velocity() = fixedInputVelocity(); } + + ev.setFromMidiPort( true ); m_midiEventProcessor->processInEvent( ev, _time ); } } From 2327581da7bdd53c22353acb6bcbd0b487d50733 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 23 Jan 2013 23:11:47 +0100 Subject: [PATCH 33/46] MidiPort: coding style fixes --- src/core/midi/MidiPort.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index bdf9d42fd..eb6949356 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -45,17 +45,17 @@ MidiPort::MidiPort( const QString & _name, MidiClient * _mc, m_outputChannelModel( 1, 1, MidiChannelCount, this, tr( "Output channel" ) ), m_inputControllerModel( 0, 0, MidiControllerCount, this, - tr( "Input controller" ) ), + tr( "Input controller" ) ), m_outputControllerModel( 0, 0, MidiControllerCount, this, - tr( "Output controller" ) ), + tr( "Output controller" ) ), m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, - tr( "Fixed input velocity" ) ), + tr( "Fixed input velocity" ) ), m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ), m_fixedOutputNoteModel( -1, -1, MidiMaxNote, this, tr( "Fixed output note" ) ), m_outputProgramModel( 1, 1, MidiProgramCount, this, - tr( "Output MIDI program" ) ), + tr( "Output MIDI program" ) ), m_readableModel( false, this, tr( "Receive MIDI-events" ) ), m_writableModel( false, this, tr( "Send MIDI-events" ) ) { @@ -168,10 +168,11 @@ void MidiPort::processOutEvent( const midiEvent & _me, const midiTime & _time ) --ev.m_channel; } */ if( ( _me.m_type == MidiNoteOn || _me.m_type == MidiNoteOff ) && - fixedOutputNote() >=0 ) { - // Convert MIDI note number (from spinbox) -> LMMS note number - // that will be converted back when outputted. - ev.key() = fixedOutputNote() - KeysPerOctave; + fixedOutputNote() >= 0 ) + { + // Convert MIDI note number (from spinbox) -> LMMS note number + // that will be converted back when outputted. + ev.key() = fixedOutputNote() - KeysPerOctave; } if( fixedOutputVelocity() >= 0 && _me.velocity() > 0 && ( _me.m_type == MidiNoteOn || From a3abcdb2e0ac91887c5ef7b4299ab8113994625c Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 23 Jan 2013 23:12:04 +0100 Subject: [PATCH 34/46] InstrumentTrack: directly forward MIDI events under special circumstances In the special case that a MIDI event comes from a MIDI port, the instrument is MIDI based (VST plugin, Sf2Player etc.) and the user did not set dedicated MIDI output channel, directly forward the MIDI event to the instrument plugin so properties such as MIDI channels are kept. Based on patch by nuio , 2013-01-20 --- src/tracks/InstrumentTrack.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 4ffe3c22d..e60610c6f 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -221,6 +221,18 @@ void InstrumentTrack::processInEvent( const midiEvent & _me, const midiTime & _time ) { engine::getMixer()->lock(); + + // in the special case this event comes from a MIDI port, the instrument + // is MIDI based (VST plugin, Sf2Player etc.) and the user did not set + // a dedicated MIDI output channel, directly pass the MIDI event to the + // instrument plugin + if( _me.isFromMidiPort() && m_instrument->isMidiBased() && + midiPort()->realOutputChannel() < 0 ) + { + m_instrument->handleMidiEvent( _me, _time ); + return; + } + switch( _me.m_type ) { // we don't send MidiNoteOn, MidiNoteOff and MidiKeyPressure From 26625b137f5c6fd9c500d89473fbc58a853ea35d Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 23 Jan 2013 23:26:23 +0100 Subject: [PATCH 35/46] InstrumentTrack: do not evaluate realOutputChannel() for the time being When deciding whether to forward a MIDI event directly to the instrument plugin do not evaluate realOutputChannel() for the time being as it is ranged from 0 to 15 and has no don't-care-state like the input channel. This is a workaround - we need a better solution here. --- src/tracks/InstrumentTrack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index e60610c6f..5dcac3f11 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -226,8 +226,8 @@ void InstrumentTrack::processInEvent( const midiEvent & _me, // is MIDI based (VST plugin, Sf2Player etc.) and the user did not set // a dedicated MIDI output channel, directly pass the MIDI event to the // instrument plugin - if( _me.isFromMidiPort() && m_instrument->isMidiBased() && - midiPort()->realOutputChannel() < 0 ) + if( _me.isFromMidiPort() && m_instrument->isMidiBased()/* && + midiPort()->realOutputChannel() < 0 */ ) { m_instrument->handleMidiEvent( _me, _time ); return; From 0acfff036f36ce813ea1145ed29db7d36d20669a Mon Sep 17 00:00:00 2001 From: Frank Mather Date: Sat, 9 Feb 2013 12:27:17 +0100 Subject: [PATCH 36/46] Added HydrogenImport plugin This is a new plugin that imports hydrogen drum machine songs. Signed-off-by: Tobias Doerffel --- plugins/CMakeLists.txt | 1 + plugins/HydrogenImport/CMakeLists.txt | 4 + plugins/HydrogenImport/HydrogenImport.cpp | 404 ++++++++++++++++++++++ plugins/HydrogenImport/HydrogenImport.h | 27 ++ plugins/HydrogenImport/LocalFileMng.h | 29 ++ plugins/HydrogenImport/local_file_mgr.cpp | 234 +++++++++++++ 6 files changed, 699 insertions(+) create mode 100644 plugins/HydrogenImport/CMakeLists.txt create mode 100644 plugins/HydrogenImport/HydrogenImport.cpp create mode 100644 plugins/HydrogenImport/HydrogenImport.h create mode 100644 plugins/HydrogenImport/LocalFileMng.h create mode 100644 plugins/HydrogenImport/local_file_mgr.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 132524acf..db590e379 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,6 +2,7 @@ ADD_SUBDIRECTORY(audio_file_processor) ADD_SUBDIRECTORY(bass_booster) ADD_SUBDIRECTORY(bit_invader) ADD_SUBDIRECTORY(flp_import) +ADD_SUBDIRECTORY(HydrogenImport) ADD_SUBDIRECTORY(kicker) ADD_SUBDIRECTORY(ladspa_browser) ADD_SUBDIRECTORY(ladspa_effect) diff --git a/plugins/HydrogenImport/CMakeLists.txt b/plugins/HydrogenImport/CMakeLists.txt new file mode 100644 index 000000000..27e8d6a4d --- /dev/null +++ b/plugins/HydrogenImport/CMakeLists.txt @@ -0,0 +1,4 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(hydrogenimport HydrogenImport.cpp HydrogenImport.h local_file_mgr.cpp LocalFileMng.h) + diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp new file mode 100644 index 000000000..6390e4834 --- /dev/null +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "LocalFileMng.h" +#include "HydrogenImport.h" +#include "song.h" +#include "engine.h" +#include "Instrument.h" +#include "InstrumentTrack.h" +#include "note.h" +#include "pattern.h" +#include "track.h" +#include "bb_track.h" +#include "bb_track_container.h" +#include "Instrument.h" + +#define MAX_LAYERS 4 +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Hydrogen Import", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Filter for importing Hydrogen files into LMMS" ), + "frank mather", + 0x0100, + Plugin::ImportFilter, + NULL, + NULL, + NULL +} ; + +} + +QString filename; +class NoteKey +{ +public: + enum Key { + C = 0, + Cs, + D, + Ef, + E, + F, + Fs, + G, + Af, + A, + Bf, + B, + }; + + static int stringToNoteKey( const QString& str ) + { + int m_key; + + + QString sKey = str.left( str.length() - 1 ); + QString sOct = str.mid( str.length() - 1, str.length() ); + + if ( sKey.endsWith( "-" ) ) + { + sKey.replace( "-", "" ); + sOct.insert( 0, "-" ); + } + int nOctave = sOct.toInt(); + + if ( sKey == "C" ) + { + m_key = NoteKey::C; + } + else if ( sKey == "Cs" ) + { + m_key = NoteKey::Cs; + } + else if ( sKey == "D" ) + { + m_key = NoteKey::D; + } + else if ( sKey == "Ef" ) + { + m_key = NoteKey::Ef; + } + else if ( sKey == "E" ) + { + m_key = NoteKey::E; + } + else if ( sKey == "F" ) + { + m_key = NoteKey::F; + } + else if ( sKey == "Fs" ) + { + m_key = NoteKey::Fs; + } + else if ( sKey == "G" ) + { + m_key = NoteKey::G; + } + else if ( sKey == "Af" ) + { + m_key = NoteKey::Af; + } + else if ( sKey == "A" ) + { + m_key = NoteKey::A; + } + else if ( sKey == "Bf" ) + { + m_key = NoteKey::Bf; + } + else if ( sKey == "B" ) { + m_key = NoteKey::B; + } + return m_key + (nOctave*12)+57; + } + +}; +HydrogenImport::HydrogenImport( const QString & _file ) : + ImportFilter( _file, &hydrogenimport_plugin_descriptor ) +{ + filename = _file; +} + + + + +HydrogenImport::~HydrogenImport() +{ +} +Instrument * ins; +bool HydrogenImport::readSong() +{ + QHash drum_track; + QHash pattern_length; + QHash pattern_id; + + song *s = engine::getSong(); + int song_num_tracks = s->tracks().size(); + if ( QFile( filename ).exists() == false ) + { + printf( "Song file not found \n" ); + return false; + } + QDomDocument doc = LocalFileMng::openXmlDocument( filename ); + QDomNodeList nodeList = doc.elementsByTagName( "song" ); + + if( nodeList.isEmpty() ) + { + printf( "Error reading song: song node not found\n" ); + return NULL; + } + QDomNode songNode = nodeList.at( 0 ); + + QString m_sSongVersion = LocalFileMng::readXmlString( songNode , "version", "Unknown version" ); + + + + + float fBpm = LocalFileMng::readXmlFloat( songNode, "bpm", 120 ); + float fVolume = LocalFileMng::readXmlFloat( songNode, "volume", 0.5 ); + float fMetronomeVolume = LocalFileMng::readXmlFloat( songNode, "metronomeVolume", 0.5 ); + QString sName( LocalFileMng::readXmlString( songNode, "name", "Untitled Song" ) ); + QString sAuthor( LocalFileMng::readXmlString( songNode, "author", "Unknown Author" ) ); + QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) ); + QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) ); + bool bLoopEnabled = LocalFileMng::readXmlBool( songNode, "loopEnabled", false ); + QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" ); + + + float fHumanizeTimeValue = LocalFileMng::readXmlFloat( songNode, "humanize_time", 0.0 ); + float fHumanizeVelocityValue = LocalFileMng::readXmlFloat( songNode, "humanize_velocity", 0.0 ); + float fSwingFactor = LocalFileMng::readXmlFloat( songNode, "swing_factor", 0.0 ); + + + QDomNode instrumentListNode = songNode.firstChildElement( "instrumentList" ); + if ( ( ! instrumentListNode.isNull() ) ) + { + + int instrumentList_count = 0; + QDomNode instrumentNode; + instrumentNode = instrumentListNode.firstChildElement( "instrument" ); + while ( ! instrumentNode.isNull() ) + { + instrumentList_count++; + QString sId = LocalFileMng::readXmlString( instrumentNode, "id", "" ); // instrument id + QString sDrumkit = LocalFileMng::readXmlString( instrumentNode, "drumkit", "" ); // drumkit + QString sName = LocalFileMng::readXmlString( instrumentNode, "name", "" ); // name + float fVolume = LocalFileMng::readXmlFloat( instrumentNode, "volume", 1.0 ); // volume + bool bIsMuted = LocalFileMng::readXmlBool( instrumentNode, "isMuted", false ); // is muted + float fPan_L = LocalFileMng::readXmlFloat( instrumentNode, "pan_L", 0.5 ); // pan L + float fPan_R = LocalFileMng::readXmlFloat( instrumentNode, "pan_R", 0.5 ); // pan R + float fFX1Level = LocalFileMng::readXmlFloat( instrumentNode, "FX1Level", 0.0 ); // FX level + float fFX2Level = LocalFileMng::readXmlFloat( instrumentNode, "FX2Level", 0.0 ); // FX level + float fFX3Level = LocalFileMng::readXmlFloat( instrumentNode, "FX3Level", 0.0 ); // FX level + float fFX4Level = LocalFileMng::readXmlFloat( instrumentNode, "FX4Level", 0.0 ); // FX level + float fGain = LocalFileMng::readXmlFloat( instrumentNode, "gain", 1.0, false, false ); // instrument gain + + + int fAttack = LocalFileMng::readXmlInt( instrumentNode, "Attack", 0, false, false ); // Attack + int fDecay = LocalFileMng::readXmlInt( instrumentNode, "Decay", 0, false, false ); // Decay + float fSustain = LocalFileMng::readXmlFloat( instrumentNode, "Sustain", 1.0, false, false ); // Sustain + int fRelease = LocalFileMng::readXmlInt( instrumentNode, "Release", 1000, false, false ); // Release + + float fRandomPitchFactor = LocalFileMng::readXmlFloat( instrumentNode, "randomPitchFactor", 0.0f, false, false ); + + bool bFilterActive = LocalFileMng::readXmlBool( instrumentNode, "filterActive", false ); + float fFilterCutoff = LocalFileMng::readXmlFloat( instrumentNode, "filterCutoff", 1.0f, false ); + float fFilterResonance = LocalFileMng::readXmlFloat( instrumentNode, "filterResonance", 0.0f, false ); + QString sMuteGroup = LocalFileMng::readXmlString( instrumentNode, "muteGroup", "-1", false ); + QString sMidiOutChannel = LocalFileMng::readXmlString( instrumentNode, "midiOutChannel", "-1", false, false ); + QString sMidiOutNote = LocalFileMng::readXmlString( instrumentNode, "midiOutNote", "60", false, false ); + int nMuteGroup = sMuteGroup.toInt(); + bool isStopNote = LocalFileMng::readXmlBool( instrumentNode, "isStopNote", false ); + int nMidiOutChannel = sMidiOutChannel.toInt(); + int nMidiOutNote = sMidiOutNote.toInt(); + + if ( sId.isEmpty() ) { + printf( "Empty ID for instrument. skipping \n" ); + instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" ); + continue; + } + QDomNode filenameNode = instrumentNode.firstChildElement( "filename" ); + + if ( ! filenameNode.isNull() ) + { + return false; + } + else + { + unsigned nLayer = 0; + QDomNode layerNode = instrumentNode.firstChildElement( "layer" ); + while ( ! layerNode.isNull() ) + { + if ( nLayer >= MAX_LAYERS ) + { + printf( "nLayer >= MAX_LAYERS" ); + continue; + } + QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" ); + bool sIsModified = LocalFileMng::readXmlBool( layerNode, "ismodified", false ); + QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" ); + unsigned sStartframe = LocalFileMng::readXmlInt( layerNode, "startframe", 0 ); + unsigned sLoopFrame = LocalFileMng::readXmlInt( layerNode, "loopframe", 0 ); + int sLoops = LocalFileMng::readXmlInt( layerNode, "loops", 0 ); + unsigned sEndframe = LocalFileMng::readXmlInt( layerNode, "endframe", 0 ); + bool sUseRubber = LocalFileMng::readXmlInt( layerNode, "userubber", 0, false ); + float sRubberDivider = LocalFileMng::readXmlFloat( layerNode, "rubberdivider", 0.0 ); + int sRubberCsettings = LocalFileMng::readXmlInt( layerNode, "rubberCsettings", 1 ); + int sRubberPitch = LocalFileMng::readXmlFloat( layerNode, "rubberPitch", 0.0 ); + + float fMin = LocalFileMng::readXmlFloat( layerNode, "min", 0.0 ); + float fMax = LocalFileMng::readXmlFloat( layerNode, "max", 1.0 ); + float fGain = LocalFileMng::readXmlFloat( layerNode, "gain", 1.0 ); + float fPitch = LocalFileMng::readXmlFloat( layerNode, "pitch", 0.0, false, false ); + if ( nLayer == 0 ) + { + drum_track[sId] = ( InstrumentTrack * ) track::create( track::InstrumentTrack,engine::getBBTrackContainer() ); + drum_track[sId]->volumeModel()->setValue( fVolume * 100 ); + drum_track[sId]->panningModel()->setValue( ( fPan_R - fPan_L ) * 100 ); + ins = drum_track[sId]->loadInstrument( "audiofileprocessor" ); + ins->loadFile( sFilename ); + } + nLayer++; + layerNode = ( QDomNode ) layerNode.nextSiblingElement( "layer" ); + } + } + + instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" ); + } + if ( instrumentList_count == 0 ) + { + return false; + } + } + else + { + return false; + } + QDomNode patterns = songNode.firstChildElement( "patternList" ); + int pattern_count = 0; + int nbb = engine::getBBTrackContainer()->numOfBBs(); + QDomNode patternNode = patterns.firstChildElement( "pattern" ); + int pn = 1; + while ( !patternNode.isNull() ) + { + if ( pn > 0 ) + { + pattern_count++; + s->addBBTrack(); + pn = 0; + } + QString sName; // name + sName = LocalFileMng::readXmlString( patternNode, "name", sName ); + + QString sCategory = ""; // category + sCategory = LocalFileMng::readXmlString( patternNode, "category", sCategory ,false ,false ); + int nSize = -1; + nSize = LocalFileMng::readXmlInt( patternNode, "size", nSize, false, false ); + pattern_length[sName] = nSize; + QDomNode pNoteListNode = patternNode.firstChildElement( "noteList" ); + if ( ! pNoteListNode.isNull() ) { + QDomNode noteNode = pNoteListNode.firstChildElement( "note" ); + while ( ! noteNode.isNull() ) { + unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 ); + float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0 , false , false ); + float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f ); + float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 ); + float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 ); + int nLength = LocalFileMng::readXmlInt( noteNode, "length", -1, true ); + float nPitch = LocalFileMng::readXmlFloat( noteNode, "pitch", 0.0, false, false ); + QString sKey = LocalFileMng::readXmlString( noteNode, "key", "C0", false, false ); + QString nNoteOff = LocalFileMng::readXmlString( noteNode, "note_off", "false", false, false ); + + QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", 0,false, false ); + int i = pattern_count - 1 + nbb; + pattern_id[sName] = pattern_count - 1; + pattern *p = dynamic_cast( drum_track[instrId]->getTCO( i ) ); + note n; + n.setPos( nPosition ); + if ( (nPosition + 48) <= nSize ) + { + n.setLength( 48 ); + } + else + { + n.setLength( nSize - nPosition ); + } + n.setVolume( fVelocity * 100 ); + n.setPanning( ( fPan_R - fPan_L ) * 100 ); + n.setKey( NoteKey::stringToNoteKey( sKey ) ); + p->addNote( n,false ); + pn = pn + 1; + noteNode = ( QDomNode ) noteNode.nextSiblingElement( "note" ); + } + } + patternNode = ( QDomNode ) patternNode.nextSiblingElement( "pattern" ); + } + // Pattern sequence + QDomNode patternSequenceNode = songNode.firstChildElement( "patternSequence" ); + QDomNode groupNode = patternSequenceNode.firstChildElement( "group" ); + int pos = 0; + while ( !groupNode.isNull() ) + { + int best_length = 0; + QDomNode patternId = groupNode.firstChildElement( "patternID" ); + while ( !patternId.isNull() ) + { + QString patId = patternId.firstChild().nodeValue(); + patternId = ( QDomNode ) patternId.nextSiblingElement( "patternID" ); + + int i = pattern_id[patId]+song_num_tracks; + track *t = ( bbTrack * ) s->tracks().at( i ); + trackContentObject *tco = t->createTCO( pos ); + tco->movePosition( pos ); + + + if ( pattern_length[patId] > best_length ) + { + best_length = pattern_length[patId]; + } + } + pos = pos + best_length; + groupNode = groupNode.nextSiblingElement( "group" ); + } + + if ( pattern_count == 0 ) + { + return false; + } + return true; +} +bool HydrogenImport::tryImport( trackContainer * _tc ) +{ + if( openFile() == false ) + { + return false; + } + return readSong(); +} + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return new HydrogenImport( QString::fromUtf8( + static_cast( _data ) ) ); +} + + +} + diff --git a/plugins/HydrogenImport/HydrogenImport.h b/plugins/HydrogenImport/HydrogenImport.h new file mode 100644 index 000000000..10a1cba7c --- /dev/null +++ b/plugins/HydrogenImport/HydrogenImport.h @@ -0,0 +1,27 @@ +#ifndef _HYDROGEN_IMPORT_H +#define _HYDROGEN_IMPORT_H + +#include +#include +#include + +#include "ImportFilter.h" + + +class HydrogenImport : public ImportFilter +{ +public: + HydrogenImport( const QString & _file ); + bool readSong(); + + virtual ~HydrogenImport(); + + virtual PluginView * instantiateView( QWidget * ) + { + return( NULL ); + } +private: + virtual bool tryImport( trackContainer * _tc ); +}; +#endif + diff --git a/plugins/HydrogenImport/LocalFileMng.h b/plugins/HydrogenImport/LocalFileMng.h new file mode 100644 index 000000000..217f95f34 --- /dev/null +++ b/plugins/HydrogenImport/LocalFileMng.h @@ -0,0 +1,29 @@ +#ifndef LFILEMNG_H +#define LFILEMNG_H + +#include +#include +#include +#include +#include + +class LocalFileMng +{ +public: + LocalFileMng(); + ~LocalFileMng(); + std::vector getallPatternList(){ + return m_allPatternList; + } + + static QString readXmlString( QDomNode , const QString& nodeName, const QString& defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static float readXmlFloat( QDomNode , const QString& nodeName, float defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static int readXmlInt( QDomNode , const QString& nodeName, int defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static bool readXmlBool( QDomNode , const QString& nodeName, bool defaultValue, bool bShouldExists = true , bool tinyXmlCompatMode = false ); + static void convertFromTinyXMLString( QByteArray* str ); + static bool checkTinyXMLCompatMode( const QString& filename ); + static QDomDocument openXmlDocument( const QString& filename ); + std::vector m_allPatternList; +}; +#endif //LFILEMNG_H + diff --git a/plugins/HydrogenImport/local_file_mgr.cpp b/plugins/HydrogenImport/local_file_mgr.cpp new file mode 100644 index 000000000..78560fef9 --- /dev/null +++ b/plugins/HydrogenImport/local_file_mgr.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "LocalFileMng.h" + + +/* New QtXml based methods */ + +QString LocalFileMng::readXmlString( QDomNode node , const QString& nodeName, const QString& defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return element.text(); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + + } + return defaultValue; + } +} + +float LocalFileMng::readXmlFloat( QDomNode node , const QString& nodeName, float defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QLocale c_locale = QLocale::c(); + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return c_locale.toFloat(element.text()); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + +int LocalFileMng::readXmlInt( QDomNode node , const QString& nodeName, int defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QLocale c_locale = QLocale::c(); + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return c_locale.toInt( element.text() ); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + +bool LocalFileMng::readXmlBool( QDomNode node , const QString& nodeName, bool defaultValue, bool bShouldExists, bool tinyXmlCompatMode) +{ + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + if( element.text() == "true"){ + return true; + } else { + return false; + } + } else { + //_WARNINGLOG( "Using default value in " + nodeName ); + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + + +/* Convert (in-place) an XML escape sequence into a literal byte, + * rather than the character it actually refers to. + */ +void LocalFileMng::convertFromTinyXMLString( QByteArray* str ) +{ + /* When TinyXML encountered a non-ASCII character, it would + * simply write the character as "&#xx;" -- where "xx" is + * the hex character code. However, this doesn't respect + * any encodings (e.g. UTF-8, UTF-16). In XML, &#xx; literally + * means "the Unicode character # xx." However, in a UTF-8 + * sequence, this could be an escape character that tells + * whether we have a 2, 3, or 4-byte UTF-8 sequence. + * + * For example, the UTF-8 sequence 0xD184 was being written + * by TinyXML as "Ñ„". However, this is the UTF-8 + * sequence for the cyrillic small letter EF (which looks + * kind of like a thorn or a greek phi). This letter, in + * XML, should be saved as ф, or even literally + * (no escaping). As a consequence, when Ñ is read + * by an XML parser, it will be interpreted as capital N + * with a tilde (~). Then „ will be interpreted as + * an unknown or control character. + * + * So, when we know that TinyXML wrote the file, we can + * simply exchange these hex sequences to literal bytes. + */ + int pos = 0; + + pos = str->indexOf("&#x"); + while( pos != -1 ) { + if( isxdigit(str->at(pos+3)) + && isxdigit(str->at(pos+4)) + && (str->at(pos+5) == ';') ) { + char w1 = str->at(pos+3); + char w2 = str->at(pos+4); + + w1 = tolower(w1) - 0x30; // '0' = 0x30 + if( w1 > 9 ) w1 -= 0x27; // '9' = 0x39, 'a' = 0x61 + w1 = (w1 & 0xF); + + w2 = tolower(w2) - 0x30; // '0' = 0x30 + if( w2 > 9 ) w2 -= 0x27; // '9' = 0x39, 'a' = 0x61 + w2 = (w2 & 0xF); + + char ch = (w1 << 4) | w2; + (*str)[pos] = ch; + ++pos; + str->remove(pos, 5); + } + pos = str->indexOf("&#x"); + } +} + +bool LocalFileMng::checkTinyXMLCompatMode( const QString& filename ) +{ + /* + Check if filename was created with TinyXml or QtXml + TinyXML: return true + QtXml: return false + */ + + QFile file( filename ); + + if ( !file.open(QIODevice::ReadOnly) ) + return false; + + QString line = file.readLine(); + file.close(); + if ( line.startsWith( "name(); + if( enc == QString("System") ) { + enc = "UTF-8"; + } + QByteArray line; + QByteArray buf = QString("\n") + .arg( enc ) + .toLocal8Bit(); + + //_INFOLOG( QString("Using '%1' encoding for TinyXML file").arg(enc) ); + + while( !file.atEnd() ) { + line = file.readLine(); + LocalFileMng::convertFromTinyXMLString( &line ); + buf += line; + } + + if( ! doc.setContent( buf ) ) { + file.close(); + return QDomDocument(); + } + + } else { + if( ! doc.setContent( &file ) ) { + file.close(); + return QDomDocument(); + } + } + file.close(); + + return doc; +} + From 7682bc6ef9fac0fa4698a4d5ce9184b6e4d01f73 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sat, 9 Feb 2013 12:27:48 +0100 Subject: [PATCH 37/46] Song: added h2song file extension to file import dialog After we added the HydrogenImport plugin we should also change the file import dialog such that the user can chose h2song files. --- src/core/song.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/song.cpp b/src/core/song.cpp index 58a1e31a7..d233e5946 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -1,7 +1,7 @@ /* * song.cpp - root of the model tree * - * Copyright (c) 2004-2012 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -1096,8 +1096,9 @@ void song::importProject() tr("MIDI sequences") + " (*.mid *.midi *.rmi);;" + tr("FL Studio projects") + - " (*.flp" - ");;" + + " (*.flp);;" + + tr("Hydrogen projects") + + " (*.h2song);;" + tr("All file types") + " (*.*)"); From 0ef2997ecef5277dd3c4fff3cbe61100ae8a7e25 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 7 Feb 2013 21:54:06 +0100 Subject: [PATCH 38/46] VST to host sync This patch should bring VST to host synchronization for LMMS. (e.g. for plugins like dBlue Glitch, TAL Filters). Synchronization is done via shared memory, missing song time positions are reccalculated and added to PPQ position sync values (SHM - common input interface for sync of all VST plugins) --- include/RemotePlugin.h | 65 ++++++++++- include/VST_sync_shm.h | 58 ++++++++++ include/aeffectx.h | 20 +++- include/setup_dialog.h | 2 + include/song.h | 8 +- plugins/vst_base/RemoteVstPlugin.cpp | 162 ++++++++++++++++++++++++--- src/core/song.cpp | 159 +++++++++++++++++++++++++- src/gui/setup_dialog.cpp | 26 ++++- 8 files changed, 475 insertions(+), 25 deletions(-) create mode 100644 include/VST_sync_shm.h diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 5ac33e766..46ffc5e15 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -27,6 +27,7 @@ #include "export.h" #include "midi.h" +#include "VST_sync_shm.h" #include #include @@ -811,7 +812,9 @@ class RemotePluginClient : public RemotePluginBase public: RemotePluginClient( key_t _shm_in, key_t _shm_out ); virtual ~RemotePluginClient(); - +#ifdef USE_QT_SHMEM + sncVST * getQtVSTshm(); +#endif virtual bool processMessage( const message & _m ); virtual void process( const sampleFrame * _in_buf, @@ -879,7 +882,9 @@ private: #ifdef USE_QT_SHMEM QSharedMemory m_shmObj; + QSharedMemory m_shmQtID; #endif + sncVST * m_SncVSTplug; float * m_shm; int m_inputCount; @@ -1007,13 +1012,60 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ), #ifdef USE_QT_SHMEM m_shmObj(), + m_shmQtID( "/usr/bin/lmms" ), #endif + m_SncVSTplug( NULL ), m_shm( NULL ), m_inputCount( 0 ), m_outputCount( 0 ), m_sampleRate( 44100 ), m_bufferSize( 0 ) { +#ifdef USE_QT_SHMEM + if( m_shmQtID.attach( QSharedMemory::ReadOnly ) ) + { + m_SncVSTplug = (sncVST *) m_shmQtID.data(); + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + return; + } +#else + key_t key; + int m_shmID; + + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemotePluginClient::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemotePluginClient::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemotePluginClient::shmat" ); + } + else + { + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + + // detach segment + if( shmdt(m_SncVSTplug) == -1 ) + { + perror("RemotePluginClient::shmdt"); + } + return; + } + } + } +#endif + // if attaching shared memory fails sendMessage( IdSampleRateInformation ); sendMessage( IdBufferSizeInformation ); } @@ -1023,6 +1075,9 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginClient::~RemotePluginClient() { +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#endif sendMessage( IdQuit ); #ifndef USE_QT_SHMEM @@ -1032,6 +1087,14 @@ RemotePluginClient::~RemotePluginClient() +#ifdef USE_QT_SHMEM +sncVST * RemotePluginClient::getQtVSTshm() +{ + return m_SncVSTplug; +} +#endif + + bool RemotePluginClient::processMessage( const message & _m ) { diff --git a/include/VST_sync_shm.h b/include/VST_sync_shm.h new file mode 100644 index 000000000..595b21cd9 --- /dev/null +++ b/include/VST_sync_shm.h @@ -0,0 +1,58 @@ +/* + * VST_sync_shm.h - type declarations needed for VST to lmms host sync + * + * Copyright (c) 2004-2013 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _VST_SYNC_SHM_H +#define _VST_SYNC_SHM_H + +// VST sync frequency (in ms), how often will be VST plugin synced +// keep it power of two if possible (not used by now) +//#define VST_SNC_TIMER 1 + +// When defined, latency should be subtracted from song PPQ position +//#define VST_SNC_LATENCY + +// define file for ftok as shared memory shmget key +#define VST_SNC_SHM_KEY_FILE "/dev/null" +//#define VST_SNC_SHM_RND_KEY 3561653564469 + +struct sncVST +{ + bool isPlayin; + double ppqPos; + int timeSigNumer; + int timeSigDenom; + bool isCycle; + bool hasSHM; + double cycleStart; + double cycleEnd; + int m_bufferSize; + int m_sampleRate; + int m_bpm; + +#ifdef VST_SNC_LATENCY + double m_latency; +#endif +} ; + +#endif diff --git a/include/aeffectx.h b/include/aeffectx.h index 1e8dea3f6..393249b03 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -114,8 +114,14 @@ 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; class RemoteVstPlugin; @@ -267,12 +273,18 @@ public: double samplePos; // 08 double sampleRate; - // unconfirmed 10 18 - char empty1[8 + 8]; + // unconfirmed 10 + char empty1[8]; + // 18 + double ppqPos; // 20? double tempo; - // unconfirmed 28 30 38 - char empty2[8 + 8 + 8]; + // 28 + double barStartPos; + // 30? + double cycleStartPos; + // 38? + double cycleEndPos; // 40? int timeSigNumerator; // 44? diff --git a/include/setup_dialog.h b/include/setup_dialog.h index e899cd6e5..d22fd9328 100644 --- a/include/setup_dialog.h +++ b/include/setup_dialog.h @@ -108,6 +108,7 @@ private slots: void toggleAutoSave( bool _enabled ); void toggleOneInstrumentTrackWindow( bool _enabled ); void toggleCompactTrackButtons( bool _enabled ); + void toggleSyncVSTPlugins( bool _enabled ); private: @@ -156,6 +157,7 @@ private: bool m_enableAutoSave; bool m_oneInstrumentTrackWindow; bool m_compactTrackButtons; + bool m_syncVSTPlugins; typedef QMap AswMap; typedef QMap MswMap; diff --git a/include/song.h b/include/song.h index 45c0a6f69..eddfe2d88 100644 --- a/include/song.h +++ b/include/song.h @@ -25,13 +25,14 @@ #ifndef _SONG_H #define _SONG_H +#include #include #include "track_container.h" #include "AutomatableModel.h" #include "Controller.h" #include "MeterModel.h" - +#include "VST_sync_shm.h" class AutomationTrack; class pattern; @@ -249,6 +250,8 @@ private slots: void updateFramesPerTick(); + void updateSampleRateSHM(); + private: @@ -323,6 +326,9 @@ private: } ; QVector m_actions; + int m_shmID; + sncVST * m_SncVSTplug; + QSharedMemory m_shmQtID; friend class engine; friend class songEditor; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 1231cb1c4..254d70071 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -92,7 +92,20 @@ struct ERect #include "midi.h" #include "communication.h" +#include "VST_sync_shm.h" +#ifdef LMMS_BUILD_WIN32 +#define USE_QT_SHMEM +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif static VstHostLanguages hlang = LanguageEnglish; @@ -303,6 +316,18 @@ private: double m_currentSamplePos; int m_currentProgram; + // host to plugin synchronisation data structure + struct in + { + double lastppqPos; + double m_Timestamp; + } ; + + in * m_in; + + int m_shmID; + sncVST * m_SncVSTplug; + } ; @@ -324,23 +349,65 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : m_midiEvents(), m_bpm( 0 ), m_currentSamplePos( 0 ), - m_currentProgram( -1 ) + m_currentProgram( -1 ), + m_in( NULL ), + m_shmID( -1 ), + m_SncVSTplug( NULL ) + { pthread_mutex_init( &m_pluginLock, NULL ); __plugin = this; +#ifndef USE_QT_SHMEM + key_t key; + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemoteVstPlugin.cpp::shmat" ); + } + } + } +#else + m_SncVSTplug = RemotePluginClient::getQtVSTshm(); +#endif + if( m_SncVSTplug == NULL ) + { + fprintf(stderr, "RemoteVstPlugin.cpp: " + "Failed to initialize shared memory for VST synchronization.\n" + " (VST-host synchronization will be disabled)\n"); + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + m_SncVSTplug->isPlayin = true; + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + m_SncVSTplug->ppqPos = 0; + m_SncVSTplug->isCycle = false; + m_SncVSTplug->hasSHM = false; + m_SncVSTplug->m_sampleRate = sampleRate(); + } + + m_in = ( in* ) new char[ sizeof( in ) ]; + m_in->lastppqPos = 0; + m_in->m_Timestamp = -1; + // process until we have loaded the plugin while( 1 ) { message m = receiveMessage(); processMessage( m ); - //if( m.id == IdVstLoadPlugin || m.id == IdQuit ) - - // IdBufferSizeInformation is sent right after plugin load - // otherwise causes deadlocks to FxMixer/EffectChain - - if( m.id == IdBufferSizeInformation || m.id == IdQuit ) + if( m.id == IdVstLoadPlugin || m.id == IdQuit ) { break; } @@ -352,6 +419,21 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : RemoteVstPlugin::~RemoteVstPlugin() { +#ifndef USE_QT_SHMEM + // detach shared memory segment + if( shmdt( m_SncVSTplug ) == -1) + { + if( __plugin->m_SncVSTplug->hasSHM ) + { + perror( "~RemoteVstPlugin::shmdt" ); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } +#endif if( m_window != NULL ) { pluginDispatch( effEditClose ); @@ -491,6 +573,12 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) updateInOutCount(); + // some plugins have to set samplerate during init + if( m_SncVSTplug->hasSHM ) + { + updateSampleRate(); + } + /* set program to zero */ /* i comment this out because it breaks dfx Geometer * looks like we cant set programs for it @@ -1356,16 +1444,62 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, // fields are required (see valid masks above), as some // items may require extensive conversions - memset( &_timeInfo, 0, sizeof( _timeInfo ) ); + // Shared memory was initialised? - see song.cpp + //assert( __plugin->m_SncVSTplug != NULL ); + memset( &_timeInfo, 0, sizeof( _timeInfo ) ); _timeInfo.samplePos = __plugin->m_currentSamplePos; - _timeInfo.sampleRate = __plugin->sampleRate(); + _timeInfo.sampleRate = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_sampleRate : + __plugin->sampleRate(); _timeInfo.flags = 0; - _timeInfo.tempo = __plugin->m_bpm; - _timeInfo.timeSigNumerator = 4; - _timeInfo.timeSigDenominator = 4; - _timeInfo.flags |= (/* kVstBarsValid|*/kVstTempoValid ); - _timeInfo.flags |= kVstTransportPlaying; + _timeInfo.tempo = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm; + _timeInfo.timeSigNumerator = __plugin->m_SncVSTplug->timeSigNumer; + _timeInfo.timeSigDenominator = __plugin->m_SncVSTplug->timeSigDenom; + _timeInfo.flags |= kVstTempoValid; + _timeInfo.flags |= kVstTimeSigValid; + + if( __plugin->m_SncVSTplug->isCycle ) + { + _timeInfo.cycleStartPos = __plugin->m_SncVSTplug->cycleStart; + _timeInfo.cycleEndPos = __plugin->m_SncVSTplug->cycleEnd; + _timeInfo.flags |= kVstCyclePosValid; + _timeInfo.flags |= kVstTransportCycleActive; + } + + if( __plugin->m_SncVSTplug->ppqPos != + __plugin->m_in->m_Timestamp ) + { + _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstTransportChanged; + __plugin->m_in->lastppqPos = __plugin->m_SncVSTplug->ppqPos; + __plugin->m_in->m_Timestamp = __plugin->m_SncVSTplug->ppqPos; + } + else if( __plugin->m_SncVSTplug->isPlayin ) + { + __plugin->m_in->lastppqPos += ( + __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm ) / (double)10340; + _timeInfo.ppqPos = __plugin->m_in->lastppqPos; + } +// _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstPpqPosValid; + + if( __plugin->m_SncVSTplug->isPlayin ) + { + _timeInfo.flags |= kVstTransportPlaying; + } + _timeInfo.barStartPos = ( (int) ( _timeInfo.ppqPos / + ( 4 *__plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ) ) ) * + ( 4 * __plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ); + + _timeInfo.flags |= kVstBarsValid; + #ifdef LMMS_BUILD_WIN64 return (long long) &_timeInfo; #else diff --git a/src/core/song.cpp b/src/core/song.cpp index d233e5946..e1ce8ae86 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -61,6 +61,21 @@ #include "text_float.h" #include "timeline.h" +#ifdef LMMS_BUILD_WIN32 +#ifndef USE_QT_SHMEM +#define USE_QT_SHMEM +#endif +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif + tick_t midiTime::s_ticksPerTact = DefaultTicksPerTact; @@ -88,7 +103,10 @@ song::song() : m_length( 0 ), m_trackToPlay( NULL ), m_patternToPlay( NULL ), - m_loopPattern( false ) + m_loopPattern( false ), + m_shmID( -1 ), + m_SncVSTplug( NULL ), + m_shmQtID( "/usr/bin/lmms" ) { connect( &m_tempoModel, SIGNAL( dataChanged() ), this, SLOT( setTempo() ) ); @@ -101,6 +119,59 @@ song::song() : connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFramesPerTick() ) ); + // handle VST plugins sync + if( configManager::inst()->value( "ui", "syncvstplugins" ).toInt() ) + { + connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, + SLOT( updateSampleRateSHM() ) ); +#ifdef USE_QT_SHMEM + if ( !m_shmQtID.create( sizeof( sncVST ) ) ) + { + fprintf(stderr, "song.cpp::m_shmQtID create SHM error: %s\n", + m_shmQtID.errorString().toStdString().c_str() ); + } + m_SncVSTplug = (sncVST *) m_shmQtID.data(); +#else + key_t key; // make the key: + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "song.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, sizeof( sncVST ), + 0644 | IPC_CREAT ) ) == -1 ) + { + perror( "song.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "song.cpp::shmat" ); + } + } + } +#endif + // if we are connected into shared memory + if( m_SncVSTplug != NULL ) + { + m_SncVSTplug->isPlayin = m_playing | m_exporting; + m_SncVSTplug->hasSHM = true; + m_SncVSTplug->m_sampleRate = + engine::getMixer()->processingSampleRate(); + m_SncVSTplug->m_bufferSize = + engine::getMixer()->framesPerPeriod(); + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + } + } // end of VST plugin sync section + + if( m_SncVSTplug == NULL ) + { + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + } connect( &m_masterVolumeModel, SIGNAL( dataChanged() ), this, SLOT( masterVolumeChanged() ) ); @@ -116,6 +187,24 @@ song::song() : song::~song() { + // detach shared memory, delete it: +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#else + if( shmdt( m_SncVSTplug ) == -1) + { + if( m_SncVSTplug->hasSHM ) + { + perror("~song::shmdt"); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } + shmctl(m_shmID, IPC_RMID, NULL); +#endif m_playing = false; delete m_globalAutomationTrack; } @@ -150,6 +239,13 @@ void song::setTempo() engine::updateFramesPerTick(); + m_SncVSTplug->m_bpm = tempo; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * tempo / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif + emit tempoChanged( tempo ); } @@ -162,6 +258,8 @@ void song::setTimeSignature() emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() ); emit dataChanged(); m_oldTicksPerTact = ticksPerTact(); + m_SncVSTplug->timeSigNumer = getTimeSigModel().getNumerator(); + m_SncVSTplug->timeSigDenom = getTimeSigModel().getDenominator(); } @@ -178,6 +276,7 @@ void song::doActions() timeLine * tl = m_playPos[m_playMode].m_timeLine; m_playing = false; + m_SncVSTplug->isPlayin = m_exporting; m_recording = true; if( tl != NULL ) { @@ -219,31 +318,37 @@ void song::doActions() case ActionPlaySong: m_playMode = Mode_PlaySong; m_playing = true; + m_SncVSTplug->isPlayin = true; Controller::resetFrameCounter(); break; case ActionPlayTrack: m_playMode = Mode_PlayTrack; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPlayBB: m_playMode = Mode_PlayBB; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPlayPattern: m_playMode = Mode_PlayPattern; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPause: m_playing = false;// just set the play-flag + m_SncVSTplug->isPlayin = m_exporting; m_paused = true; break; case ActionResumeFromPause: m_playing = true;// just set the play-flag + m_SncVSTplug->isPlayin = true; m_paused = false; break; } @@ -360,8 +465,14 @@ void song::processNextBuffer() while( total_frames_played < engine::getMixer()->framesPerPeriod() ) { - f_cnt_t played_frames = engine::getMixer() - ->framesPerPeriod() - total_frames_played; + f_cnt_t played_frames = ( m_SncVSTplug->m_bufferSize = engine::getMixer() + ->framesPerPeriod() ) - total_frames_played; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * + m_SncVSTplug->m_bpm / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif float current_frame = m_playPos[m_playMode].currentFrame(); // did we play a tick? @@ -369,6 +480,14 @@ void song::processNextBuffer() { int ticks = m_playPos[m_playMode].getTicks() + (int)( current_frame / frames_per_tick ); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ) - + m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ); +#endif + // did we play a whole tact? if( ticks >= midiTime::ticksPerTact() ) { @@ -402,18 +521,37 @@ void song::processNextBuffer() // offset ticks = ticks % ( max_tact * midiTime::ticksPerTact() ); +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (double)48 ) + - m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (double)48 ); +#endif } } m_playPos[m_playMode].setTicks( ticks ); if( check_loop ) { + m_SncVSTplug->isCycle = true; + m_SncVSTplug->cycleStart = + ( tl->loopBegin().getTicks() ) + / (double)48; + m_SncVSTplug->cycleEnd = + ( tl->loopEnd().getTicks() ) + / (double)48; if( m_playPos[m_playMode] >= tl->loopEnd() ) { m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); } } + else + { + m_SncVSTplug->isCycle = false; + } current_frame = fmodf( current_frame, frames_per_tick ); m_playPos[m_playMode].setCurrentFrame( current_frame ); @@ -637,6 +775,7 @@ void song::startExport() doActions(); m_exporting = true; + m_SncVSTplug->isPlayin = true; } @@ -647,6 +786,7 @@ void song::stopExport() stop(); m_exporting = false; m_exportLoop = false; + m_SncVSTplug->isPlayin = m_playing; } @@ -1216,6 +1356,19 @@ void song::updateFramesPerTick() +void song::updateSampleRateSHM() +{ + m_SncVSTplug->m_sampleRate = engine::getMixer()->processingSampleRate(); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif +} + + + + void song::setModified() { if( !m_loadingProject ) diff --git a/src/gui/setup_dialog.cpp b/src/gui/setup_dialog.cpp index 26566d19b..b8f38f9bf 100644 --- a/src/gui/setup_dialog.cpp +++ b/src/gui/setup_dialog.cpp @@ -117,7 +117,9 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : m_oneInstrumentTrackWindow( configManager::inst()->value( "ui", "oneinstrumenttrackwindow" ).toInt() ), m_compactTrackButtons( configManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) + "compacttrackbuttons" ).toInt() ), + m_syncVSTPlugins( configManager::inst()->value( "ui", + "syncvstplugins" ).toInt() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); @@ -186,7 +188,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : tabWidget * misc_tw = new tabWidget( tr( "MISC" ), general ); - misc_tw->setFixedHeight( 156 ); + misc_tw->setFixedHeight( 174 ); ledCheckBox * enable_tooltips = new ledCheckBox( tr( "Enable tooltips" ), @@ -247,6 +249,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : this, SLOT( toggleCompactTrackButtons( bool ) ) ); + ledCheckBox * syncVST = new ledCheckBox( + tr( "Sync VST plugins to host playback" ), + misc_tw ); + syncVST->move( 10, 144 ); + syncVST->setChecked( m_syncVSTPlugins ); + connect( syncVST, SIGNAL( toggled( bool ) ), + this, SLOT( toggleSyncVSTPlugins( bool ) ) ); + + gen_layout->addWidget( bufsize_tw ); gen_layout->addSpacing( 10 ); @@ -774,6 +785,8 @@ void setupDialog::accept() QString::number( m_oneInstrumentTrackWindow ) ); configManager::inst()->setValue( "ui", "compacttrackbuttons", QString::number( m_compactTrackButtons ) ); + configManager::inst()->setValue( "ui", "syncvstplugins", + QString::number( m_syncVSTPlugins ) ); configManager::inst()->setWorkingDir( m_workingDir ); configManager::inst()->setVSTDir( m_vstDir ); @@ -956,6 +969,15 @@ void setupDialog::toggleCompactTrackButtons( bool _enabled ) +void setupDialog::toggleSyncVSTPlugins( bool _enabled ) +{ + m_syncVSTPlugins = _enabled; +} + + + + + void setupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) { m_oneInstrumentTrackWindow = _enabled; From 7084ec0be6d4c47de27498c4c4ad7909c5748add Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 7 Feb 2013 21:58:55 +0100 Subject: [PATCH 39/46] VST Effects: preserve effect name after LMMS project reload This patch will set plugin name (information which is not stored with lmms project file) according plugin file, without *.dll, when is LMMS project loaded from the file. Future verion could maybe use PluginBrowser or EffectSellectDialog for the same. Signed-off-by: Tobias Doerffel --- plugins/vst_effect/VstEffect.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/vst_effect/VstEffect.cpp b/plugins/vst_effect/VstEffect.cpp index a6286d38e..a4215cc18 100644 --- a/plugins/vst_effect/VstEffect.cpp +++ b/plugins/vst_effect/VstEffect.cpp @@ -65,7 +65,8 @@ VstEffect::VstEffect( Model * _parent, { openPlugin( m_key.attributes["file"] ); } - setDisplayName( m_key.name ); + setDisplayName( m_key.attributes["file"].section( ".dll", 0, 0 ).isEmpty() + ? m_key.name : m_key.attributes["file"].section( ".dll", 0, 0 ) ); } From 0b394269537c2d444b2ee03a4e0ea73182ea4b09 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 7 Feb 2013 22:03:05 +0100 Subject: [PATCH 40/46] VST Effects: Open each effect only once, when loaded from project file Folowing change should ensure VST effect is opened just once, once loaded from project file. Double opening seems to trace back to commit 184ddc4d1c2947e459c96158cc77f0ff4699846f from 2006, when this VST effect save / load feature was introduced as new. Anyway VST effect parameters seems to load corectly in VstEffectControls::loadSettings, from project file even without double VST effect opening. Signed-off-by: Tobias Doerffel --- plugins/vst_effect/VstEffectControls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index c8aeaf075..0dd31ed10 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -61,8 +61,8 @@ VstEffectControls::~VstEffectControls() void VstEffectControls::loadSettings( const QDomElement & _this ) { - m_effect->closePlugin(); - m_effect->openPlugin( _this.attribute( "plugin" ) ); + //m_effect->closePlugin(); + //m_effect->openPlugin( _this.attribute( "plugin" ) ); m_effect->m_pluginMutex.lock(); if( m_effect->m_plugin != NULL ) { From 50c2242caad44c5ccf684e6bc6c287e31189ba0b Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Mon, 11 Feb 2013 00:19:08 +0100 Subject: [PATCH 41/46] VST sync patch: compatibility fix for 64bit builds It seems 64bit builds for some reason have problems with VST Sync feature on, workaround seems to be converting VST sync patch from double to floats, which does work both with 32 and 64bit builds. Double precision seems to produce odd numbers with 64bit build. (tested on VirtualBox Linux Mint 14.1 64 bit OS) (cherry picked from commit 011f87e6e60cccd16f3783e9c4885e03d95c1e56) Signed-off-by: Tobias Doerffel --- include/VST_sync_shm.h | 8 ++++---- plugins/vst_base/RemoteVstPlugin.cpp | 6 +++--- src/core/song.cpp | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/VST_sync_shm.h b/include/VST_sync_shm.h index 595b21cd9..7b6a813d5 100644 --- a/include/VST_sync_shm.h +++ b/include/VST_sync_shm.h @@ -39,19 +39,19 @@ struct sncVST { bool isPlayin; - double ppqPos; + float ppqPos; int timeSigNumer; int timeSigDenom; bool isCycle; bool hasSHM; - double cycleStart; - double cycleEnd; + float cycleStart; + float cycleEnd; int m_bufferSize; int m_sampleRate; int m_bpm; #ifdef VST_SNC_LATENCY - double m_latency; + float m_latency; #endif } ; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 254d70071..122dda3bc 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -319,8 +319,8 @@ private: // host to plugin synchronisation data structure struct in { - double lastppqPos; - double m_Timestamp; + float lastppqPos; + float m_Timestamp; } ; in * m_in; @@ -1482,7 +1482,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, __plugin->m_in->lastppqPos += ( __plugin->m_SncVSTplug->hasSHM ? __plugin->m_SncVSTplug->m_bpm : - __plugin->m_bpm ) / (double)10340; + __plugin->m_bpm ) / (float)10340; _timeInfo.ppqPos = __plugin->m_in->lastppqPos; } // _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; diff --git a/src/core/song.cpp b/src/core/song.cpp index e1ce8ae86..6fdba2b21 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -243,7 +243,7 @@ void song::setTempo() #ifdef VST_SNC_LATENCY m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * tempo / - ( (double) m_SncVSTplug->m_sampleRate * 60 ); + ( (float) m_SncVSTplug->m_sampleRate * 60 ); #endif emit tempoChanged( tempo ); @@ -471,7 +471,7 @@ void song::processNextBuffer() #ifdef VST_SNC_LATENCY m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm / - ( (double) m_SncVSTplug->m_sampleRate * 60 ); + ( (float) m_SncVSTplug->m_sampleRate * 60 ); #endif float current_frame = m_playPos[m_playMode].currentFrame(); @@ -482,10 +482,10 @@ void song::processNextBuffer() + (int)( current_frame / frames_per_tick ); #ifdef VST_SNC_LATENCY - m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ) - + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 ) - m_SncVSTplug->m_latency; #else - m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ); + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 ); #endif // did we play a whole tact? @@ -523,11 +523,11 @@ void song::processNextBuffer() midiTime::ticksPerTact() ); #ifdef VST_SNC_LATENCY m_SncVSTplug->ppqPos = ( ( ticks + 0 ) - / (double)48 ) + / (float)48 ) - m_SncVSTplug->m_latency; #else m_SncVSTplug->ppqPos = ( ( ticks + 0 ) - / (double)48 ); + / (float)48 ); #endif } } @@ -538,10 +538,10 @@ void song::processNextBuffer() m_SncVSTplug->isCycle = true; m_SncVSTplug->cycleStart = ( tl->loopBegin().getTicks() ) - / (double)48; + / (float)48; m_SncVSTplug->cycleEnd = ( tl->loopEnd().getTicks() ) - / (double)48; + / (float)48; if( m_playPos[m_playMode] >= tl->loopEnd() ) { m_playPos[m_playMode].setTicks( @@ -1362,7 +1362,7 @@ void song::updateSampleRateSHM() #ifdef VST_SNC_LATENCY m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm / - ( (double) m_SncVSTplug->m_sampleRate * 60 ); + ( (float) m_SncVSTplug->m_sampleRate * 60 ); #endif } From 2375b7f0a0c29b047594433c4c7689d718df209e Mon Sep 17 00:00:00 2001 From: NoiseByNorthwest Date: Mon, 18 Feb 2013 22:21:27 +0100 Subject: [PATCH 42/46] Fixes #3604316: LMMS crashes using AudioFileProcessor Signed-off-by: Tobias Doerffel --- plugins/audio_file_processor/audio_file_processor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 208a03d9d..729e9e429 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -927,6 +927,10 @@ void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cn void AudioFileProcessorWaveView::slideSampleByFrames( f_cnt_t _frames ) { + if( m_sampleBuffer.frames() <= 1 ) + { + return; + } const double v = double( _frames ) / m_sampleBuffer.frames(); m_startKnob->slideBy( v, false ); m_endKnob->slideBy( v, false ); From 9268398626e35844cea7fcc48a08622c6559182d Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 27 Feb 2013 21:48:49 +0100 Subject: [PATCH 43/46] AboutDialog: updated copyright year --- src/gui/dialogs/about_dialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/dialogs/about_dialog.ui b/src/gui/dialogs/about_dialog.ui index 40a6d4c99..b795fd0f8 100644 --- a/src/gui/dialogs/about_dialog.ui +++ b/src/gui/dialogs/about_dialog.ui @@ -125,7 +125,7 @@ - Copyright (c) 2004-2012, LMMS developers + Copyright (c) 2004-2013, LMMS developers true From fe35743ef088671eecda2802908a7bb0fd331e52 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 27 Feb 2013 22:13:52 +0100 Subject: [PATCH 44/46] Default theme: new graphics for note icons Thanks to John Serafino for the creative work. --- data/themes/default/note.png | Bin 1006 -> 974 bytes data/themes/default/note_double_whole.png | Bin 1159 -> 1219 bytes data/themes/default/note_eighth.png | Bin 985 -> 974 bytes data/themes/default/note_half.png | Bin 860 -> 556 bytes data/themes/default/note_quarter.png | Bin 852 -> 547 bytes data/themes/default/note_sixteenth.png | Bin 894 -> 1110 bytes data/themes/default/note_thirtysecond.png | Bin 1090 -> 1191 bytes data/themes/default/note_tripleteighth.png | Bin 1240 -> 1088 bytes data/themes/default/note_triplethalf.png | Bin 1037 -> 1069 bytes data/themes/default/note_tripletquarter.png | Bin 1012 -> 726 bytes data/themes/default/note_tripletsixteenth.png | Bin 1122 -> 1291 bytes .../default/note_tripletthirtysecond.png | Bin 1399 -> 1453 bytes data/themes/default/note_whole.png | Bin 3718 -> 709 bytes 13 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/default/note.png b/data/themes/default/note.png index 3f475b0ea497624eca88f9553b2ad24ad16105a5..f81425daed4eb550016c7efff9a545f72db8ad77 100644 GIT binary patch delta 952 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 diff --git a/data/themes/default/note_half.png b/data/themes/default/note_half.png index d73ccb4f576f6eb618fa15edcb0adddaff005d43..aff73e593196300dd5ed6a865c956e3a5991e5eb 100644 GIT binary patch delta 525 zcmV+o0`mRb2CM{-Du4d~{{a7>y{D4^000SaNLh0L00l7s00l7tx9uoW00007bV*G` z2i*Y=6cZ^w1y1Dv00Fs4L_t(Y$L*ERYZE~jhM#X|XLo}pJ*W-XC2Amu6-0#k4-^mH z1+Ri&d#?vUPf|qCqy7idt9q#-6!9YTs+VH%ASOr*B(2rPO@A^oUUsF}nwStg<%5BN zVcuun`SA`6R|15yu18p?g?-N(COH|@!sq+;-OMl z`dFE+%x#_9%5T;;y2isF#X+{BKwVX5r7XA2j(J~Qt=?Z>S$_@a`8)I1qD$eu*3@o1 z2;lW`;Hxw0qKN43YWG%crS<~Y27at9uf1vPG&+sm_J1zW16B3_$1Dk-q%Adf*i}SJ(Y4&GWGPuB zp#ij0$UYaUp92z%*)T8FX{AKObCEkPU8&o*_GV|f^B6-bbzuIsMZW;11A?-5%^aTq P00004}!IWj&Yt20YQ`;biN*Qa+8UV(D z^1{3TN}n=c(tldJ##hZw0#TsKOTSlwvNKqCSP0JIShF?|1F9EJ15iRMJs0TVN zz@$q+Uti{*UZ8k8^}$~XjL8rZMXeXB{uu!j^DnGNVzq1l)!@!0Z9ah|!0FSMLmpkA zRDbDyvA!U}EwN6Z&efq}uLe+<$vZz4sb8wLh8hXkhcM*|a*j`Zk*ak^+lD7^2AhEX zpLM7>F@KL9JvsM(?hWp3x!gIi{mTBr)@!?`^-EF3orsE_jVr^bIs& zO3G+m;}Ify@L5maqHDm&dTsMhbySirQ)L<^8)3@2# zad7O;*h8Gwx&*m^gI^{vCrdJf2wIvL>D!LfdDW8dPk8_U002ov JPDHLkV1f*}iBJFl diff --git a/data/themes/default/note_quarter.png b/data/themes/default/note_quarter.png index 01746885530cfe34f0f14c8accd3e9809437caf3..6c3acf2cd6e01763d5ba450fe43ffa39ae7c8c97 100644 GIT binary patch delta 515 zcmV+e0{s2d2BQR!Du4d~{{a7>y{D4^000SaNLh0L00l7s00l7tx9uoW00007bV*G` z2i*Y=6ciJ-NCA)l00FQ`L_t(Y$L*E9i_}0E#($F!HutgTez@ocRBnZ9+{V`Cu&`IK zvlIUX{|~|X8cQ4N1I1nh?c@qUL^vbP?Z=r-LNfE(BzPQpB!9tjUzlc?d4BV}!#v|s zuM`jfm!G_P@cz>6VEpCv_m4mU$VD5S1^^e>a6M-NHUv6#_SXee5FbE{xFt~iPr#oN zR2}g)0M+y??HsHDYrq=NsJt4lwu%L(%}e$!5O>Nzt42_TU6a`G{vxc(`2g5DBisRc z03xE=I;bLfn}2Pt4DSygjrKAo_>ms1;ML(C+8iwz?Qr3J{?@`Py0Iq#-lMe!t2DV7;7!FZ&_d1Y-))Eam4+lVk~T5|d^rztYSArWBUC4t%PG2E%f`oQFSx4U9EJIwCJ} zP8KsF9dZ0FIsg*DHf@msR_3#Lw&*OLXL;u9`3b(~Q<$97_*7P3lNb4${A~#^O}@aY zMFG(B@A~`B#CfbX)hz@oi5!`4=4EkM90D3xooUlQ>o;;bpf#qlRk;8F002ovPDHLk FV1jTx@6P}L delta 823 zcmV-71IYZN1k?tQDt`a~0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2i5@<76>CC0sl7u00QAjL_t(Y$E}pjOH^SL#((d7@65fr9W|C1zy0JyL zNe3cf2r6h%{RsuFTm&sz1T6#C^#?>z(5`4o1szJlG$Qk}Mt>)@8LuWeGqUY6xZ6qQb>`fmV`O~11SMXRg_Rd3%VeWwLWfd2l0a)&HXYBKjC zTayv~a6>y#W$&S6tPYS_PZzg1slU9s%Nr@rU%(@SEEP5NO{(VJmd=rhg(hHf>mEv8 z%*&T=Y=1w`K3`vWpnbJ}=C-xu*h(X}{t@rRwfG^AhN`^&lQUj=8rT(bmxjArwGYS44iS`fw@H zobf&TJOZ)^1r3z>s}wWG*YrXKP-C+xefDhEhJRxg#bQtC2y`sV2?4WJ@TD4ND`CzL zGk$7DyqK%*gk7XoYSB9hO?Wb8^M%ApO1Z@8yd_{2m|Lf1(pRXCdB@^|Bd>umUe{N@8)*^jSV@==sq5HEV4Ohy$a*9FQ%QWH%w2K%HS2;cPbB z1TeVx)ys_imP%ERLvaZ^sn33zoiJtpg;L1@egetL{Rd<^EOP(=002ovPDHLkV1faC BbO!(c diff --git a/data/themes/default/note_sixteenth.png b/data/themes/default/note_sixteenth.png index 33cb9ad7a692b24f615b836502ad2656697f0824..661bcb7be0043519a80ee0e6b452bd71aac3ad78 100644 GIT binary patch delta 1083 zcmV-B1jPIP2G$6WDu4d~{{a7>y{D4^000SaNLh0L00l7s00l7tx9uoW00007bV*G` z2i*Y=6cq(qhlPLu00ZMmL_t(Y$DLJgXj@ej|K0oEYtopeNwY3Z29-3e18sEvD>!8g znX*k~W3Ug}4XNl9-SEQ@{ZJ9gL_`WxCMZ~tf#_sT>j&9}V}H8MVaM88?B-f+X{$|_ zw8=}FyuA1N-n-Wiy%}*$WzB`dy>Rb6zkAN_cg~e?dxHRQ0a*LuiJgOi4)+s_syUTU z7#09tX3GX$1%L#=BPmi;lF0h|UWpyQ>)G%l02%t)`=o z^~SaV2maJ z*Q^WO>&;y_bZyW4Z~0Dr6k2guf>*A8{h`-dcLVSNsDG&jU@2^w1X`bt{BY09k>@YI zfBn1Jq3kx!xJd{hP4_qSggY8rgtBN=001PVp8DGlG=22UH}Qi*pZs|xf5Z3?00c}B27|N8~`9p17K4nx;1p@Oyb+_f!?trR>7JAfLeb|hmbU_j%0(SjEs|5oT8YU zRLRl0o_{y@oPG4&Lel78ijPRu%9I0uW7yND4h(iqe4p-K_?66Qmt-s~=JtAGbQ1t* zH;kVF*Z>$^rE@MwUIdUD?i>H=+}k5N)FJB^eMEu_cWhubGp>Cxd0YVSs$@ziVE{A$ znS8>SIQP!g{aVU6zXVR*(XHVJZYM=V7%iKZHh%~>00)2xAa50|x$DQKJ}B+`n!OK~ z*7E;2r=sK~v38X;yOLa)Fk^01OK`>=LWz{0DO?Z_;PMKumPMh|idhkrSufaF)|O-2 zL(PN|$=2<^M!%dsDWYSQ0Av7Oc(FFPEBIVvTjP_yI$zMXY&|oUxy(5S=bYY>LfRA$ zY=2}n%Z&B^voD`sOjiOZ9rnmkITqX<>}zR@#v_dp1blutj3FIO$Nxyqa>5{&al_X% zdt^_@y(KY_=^Q_u91#MrD{-*&Yi*X^Xb1)30bc{cjbYpwX@*wRz!-zNppq=~=5qPA z?~C0%b2-~J+&4Z6zyNUbE3Fhk4e^qVjDK@Djt#>!Ft4Ven>sXIgJBxbOw$o0G62p` z-M^m~_gRVDQq9N}3WLWyUd30E zsRN+sOgM*guG|sRGHcS=3=~;`?O4cbd0zZm|J?e~ItxG-j(LSzy?;9$JSN7)QJW}UQ70Z;)H%hIvp_!ni_tN8;xg%tn*002ovPDHLkV1ml* B{tW;C delta 865 zcmV-n1D^cW2>u3;Dt`a~0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2i5@<76v&(;48xb00Rq2L_t(Y$E}rHNK{c6g};4fbVkRSQPUc9tdZQPn3*A%u*WSt2MQyxmKRDX6K7FK371IvNLnP{1nP3i4>URDSmyFa}qTh7Z_A6)&fb>RT9gF zIakYlmBJ3Uj#6GSot;a41%g?-1!M#9W4xQB58!sZa1T5ldL%gyi-b&9J!_Ib*^0At zP1E)OY2&pjT3I^K7xISntyPDms3@O{?MGIbbeqfWw0|mH*F8Y;biJ^!!!_V|I`0RX z_MFn$;r((@a65fKHINHfrm6ryRH8Xdya*YVKJNEit65U}+H}t0Qc9Tti7qN8AdtGg z-72hv!sHD=CoSJDhoAjmReqrzh&6`q52GhDjbT3YHK=bXR_gt4ff%48=k45*ckkaM z0E06z)qnAnyS}TcGy#qSIe-iB81WH1Zi}u|*O;RY!1lqrvMa>$Wd(VF4Hy8r06#D^ zE`dK3&?XoXjbg_^_E|frOz6R`6_aS9q+-iKiP$k zNkUtaa5`Rq8^#-9F$f%!xkW0;G5d1ht4^aYVC+jA<;W09sTdI%se0SC!r&+Z`QIF< zjk3CgCJS^pp~GSPozP;Xw0|=qGHk>MqYjnPUQCo7Y9Oe~@V%}lKWFunGo;{45Y^Pp zv0C8+p*xpuKLA>QCgTQ$|MxVGhZrEGtgLLaNtxHRwzk?{zN+sPk$&Ja&|nbsj!_ad rL4+h3m}i)v10kd89w7Ly>H&TM=JE@h$Jz=a00000NkvXXu0mjfo@t3v diff --git a/data/themes/default/note_thirtysecond.png b/data/themes/default/note_thirtysecond.png index 80f970a7ceaf2965ebb74098a7b41c294b2b8b9c..a3daee57927420c2b03de184fe65194e8346a90d 100644 GIT binary patch delta 1165 zcmV;81akYr2&V~y{D4^000SaNLh0L00l7s00l7tx9uoW00007bV*G` z2i*Y~6D>N5LkfTZ00cBiL_t(Y$DNg1Y!p=($Ny($XK!t{Y_}I$2(?{WOlc{QY6=Np zzzXrf5Dd|_yhs2g(ZrC@m^7G>=tEz?2;oJHu^O#fsJ?hXM1Q0)q$RcV2DX|)OE2zr zXJ>D-b3fzDOtTb9=$_;}opZk5_y2B(#nUYUzy+ZA$mRV5{^#9Is-R3I=VZ+~H?951 zVpaff0PwOLTfwqqL;YL9?&{{!Z2!M0h71&$odN(U+QRG4w4l>myD=5t&B>OL=MkIxRJd*9~{2kbmDQ4 ztN;!VE!!$UCa~S-t*bB?*b^u%n7@K_&WjQH?-6o7xE*MgP=(OOG-13jN~6t(SdWtlJ!-1{LOq_)!1`0Fzx|5BM5dYK|vT zaf9I)qkp%3=%h4PmP7Knj4i(nccyKnR%pYFA6s_ZvGE zS-pPy+|>TmxG`zQo#nyZrAM84PN#i8s}Pw0Ynp(s(%pFQ;?8S_np@S$pZ6 zyBh0S%NwmSMRrLB(Tx~^7G|*Enc7hHhNHJ(TYr6llF+^c09~Qd({!>O62`6w8z2et zVE;!G&C}hnGl^S>+*-I%N?)Jv(TN(AW7^HT-46!sBs-p9oUvru+hvUaFdUgGt##}eIDh|`np8F08D<&79Dp2a6c+1=9HgPaym9(Y+n$b1U0jZ%x^h>^mdJ2?61LS|qig{D0`raa4=EI%Az-sR9;&mwkafSy1Ay^ZN== zT2zYivUL#CA}FO05(0@qT9+~NDF78f%AN0EQz5Bi?5q2?0ccB;Y%6e)T(X^-CJaM| ztjLH6JS0VeD2b328KR;Xmgh7BA`O2#_hGm%EL*@w0Ct@lFp!8PVr#-~4-9G`#D9mN z=^CPO0U delta 1063 zcmV+?1laqh3Bm}FDt`a~0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2i5@<76=0yZ{Pm_00YlSL_t(Y$E{XvXj@ene$LI;O?s22N!NC@>5|wkTT_|KI-~}n zox=KouAmm(n9xy_;RoUe{*e9<6cqd;BEtSmK?L#76#SfsZGWkDU{+>Vw5|^u+}tEL#~;05m#kSDeBp55-k0}zpZ9&`3 z?dRWm{gu{P5`P#@2D4j8i9rB40L4l`)ln71oZwQOvU{7GpB^06=kJqG2BMpX=&JY! z3Pqt3o2qNc8yUC0ThsI5o1chnNB3)b$GbcIOR>KEjnpgvUSUH~qp9H8ce?NLqjcY> zD*=(+)^FU zds-q5%jt9~2tWWJ*Hj`zvSR)L-7{;(SpDcDLu%(^2W&SdL$bV-S>)210MBoQ0VDww zY6AdB(O~G$xJ1VfFZ`TLNJ*yWWp_jLmv}yTEjmpp4FcFK1=!0}%1{%(UaQ$M*(|Ei zdyCVT&wowcV4h^1MON)5glM%|w&oseTY-sKx*EqHI&;jdbDG;%r(=H>V%g08MteIS ziciUMVI4qrr<(S{fMSUcWke})eZ?;|b(`_{mqYz}HK)x6V&5z0lokM~odE!;MSU#Y zoW9@x-2C)$l5S@(F@JHvSc<$G3%Zz_%C$(uXg23IJ`H|7 zH|F~SRy7BJWf-Q6ff@j^V!F8FNBK0KFt);2cnSu_feosnF-i{(AL{ab5W9pG;U@^v zB|{x2WQ4T5s|3%UeNBJIo{VrK9Tz;QiSXO>)x;ar5^SaT%|e^rbyAkqzXQPQIOsZj zz<*(P05DbRD*-?#rT;k$uxAHWm=Sp`sbI~6vMwe)H|0IQ;&t&0~w zlJ0(^DK<0nEsk4`-(nUCu+jj8tiXyHh<})XunCR6I$3m= diff --git a/data/themes/default/note_tripleteighth.png b/data/themes/default/note_tripleteighth.png index b33cd4fb628b19078367412a9298b44c9963c45d..4e4d214dea14a86c7bed29c03761f743d7fc0a4f 100644 GIT binary patch delta 1045 zcmV+w1nT?P3BU-DB#|*0e+4lB00l7tx9uoW00007bV*G`2i*c17A-TNL8b8k00YfQ zL_t(Y$L*EPYg|H&LbB(KGjLxS6YQiDG?DJd19t!_Z%)hke^@tY3bWu)4ilEJe@|Ag(w~A zz>VyOpE|tIb$i*}0MUq*YkA_j=T0qt1vCH`kgadD@cw~Uf3vBP`R*+2;pApmzcUh?GL&=MK;Y`r80eGM#h*jGb`f`UVhg0~n=Lq!ePfw*tVA zL*npNLPY>d6UD|s912JP;ntA<29R9>rPOeT!+b|=#~RK5towlbfd3Alw&j01=(cJ~ zMASVz@9;1&e`M<}umB@+7f^3IX)Ot5IM{8_)*qbM>9J*+0&E})z2r94YEou$^Sak+kjD2NP@wT@fQJa}~f$+`V=rSe3X zAP87lU6HW0+0u!J67M-hXQtp3s7zOwEKZWKGpg2Kw*V_OpIUIr&awQcL%B3Xr94Y6 zJ4!F~e_5wL4%qgb{f?7&N(HCDzH)_$LWy)Lt?KS(%7~KUW_OcXvql(&c%DbKUgi2q z^&;R&N{Zcnhi1FU;>uqbW2iUl)LQju>FllVfzZd#Z*(@OuC3zv9s_T{t$LN^$vrzF6zgXt@cVsE)32LKBvPPFaT2K zNpmPsNgT&!9~y#l!Kv`~@N#_P0p!dp=A}d~@ra~lTH5ln|DAstkH+8g0j0K0%}ufZ P3jhEBNkvXXu0mjfZoCD9 delta 1196 zcmV;d1XKIK2-pdbBnkm@Qb$4nuFf3kks%y^3ljhU3ljkVnw%H_000McNliru*a#Z} zAsY9nmr4Kt1XxK#K~zY`rPWJJ9CaMP@$bye?#%AaE|5}L1Xe({+h^L6iq^)qcqvF? zv6$9YV`8FBjP+m>>j@thjaCmPdTA4z25mg*Nux~~4OM8N(7H@a#YC#KgThYp90)Z)0bp4OS{5s?DHdj98WSNMFz*3&7M>6)bH@nIwgXup zw`7li=qNn;<;(Bh3{URL3?+i(^Rb10J>sJ(0AL_>@vRdrbp zl3FI|x9qRnRZ?=evB{p>V(c57h^{w7+BM7n1<>tse{fu`de2+EIP17e+_V~C z>QbVVsdO)E#A#z4uNtBJtT~iCuir@gdN-sMEG95rvP<3VY*{g|#r{mmzeTqb!&5hm zI(xI_DXGf1p#6z%c6&wd&4qJ+UrpIu;6}wz@o7iQ;glpyz-e?EeO9kz;&=Lh{KU|$_QeF|rP!zz%}%Wp z@b)PU;c)0Cki;Sk3MMfMq-wye>w0!E0g%-%8G)4o!9bu_*K<8UNVu+leW#BYR_j(< z1(L89ur0kR3?yM-r2r5ECV**kC_ZG(*``GoA|{1^eQAO`!PJ?R0`uMH00YP*uBAGJ z4Wb8358x01T3b)ejpnR1uF9!h9;*|lWIgtA!Q=AsuHKnyc(Lx8V`Tw#& zG!ix!koy5Z;W@R8{r2O39Cm(%JHO4mW17B3e;rUR0i?A={vP0ne#BiO~C_<=}BV=O$X+KQDe*+ce!qrMi8qswS#G&4?u zG?eK~mPdKMVRyYmv)Wwq!n(7M0=kWhjAqA)qs;+jQ8w<~w7<_Ea{Myf4PqV)3z*8> zER-DKM5sVwpWNna@u({Xvbq{z%@vKzP-56M zn(WLrSz9#K3W5NC$u$1yBE#O{2wb_`)hmhp!m0q}Z+oDpyQ|yfatG>EZ;xxU>(J!X zl%l58ROfGoP_v=BLH5aStOfug^QYJesPpf68i)fI>Hh%3wGDLutcP&`0000< KMNUMnLSTXgyg$_d diff --git a/data/themes/default/note_triplethalf.png b/data/themes/default/note_triplethalf.png index 58343a8fcd5f0141a886324f09a9dec8abc53fe4..5c8a7e90c88eef94dc05180fde462abe3c09f7d0 100644 GIT binary patch delta 1025 zcmV+c1pfPt2(1W^B#|*0e+4lB00l7tx9uoW00007bV*G`2i*c17BU-Ht^<_-00X*7 zL_t(Y$L*DEh!s^B$A8Z`bLY(5S?`_esztsnX0EpA24O~9Mx-DFQNdC~5Yh)RmZIzh z!7sh&ml8~Z2>g(tUo^}TOe6%uNc4jt(F^LW2z9Tzt-HE=-)7F~e}i-5w%j6!zWEG) zW;oAt{{M5HGxHzeuc3e(kk8;}W1T|y=SYE?H)VnXf_sJ*ubI^sZ>mn#$HX}WA}GM! zAQd0+3RZLx$(0;2x~sKnqdDTn84)Xj z!?q}#MAM?_^=3^^6>xyN&F}fik2QPt?b~+wv&KPS0!XqJ`oQ$Jk8U{d`H}tKop|Hi ztH4#Dk#YDyyyfug+rIhvXph%n*H&p|0hD0^7zW0&Ljwo_e~X9{EeUWrLp{qIAkm&q z;LmPN>63^9$OvjcHQRw?sAYw=EnjOb5U7dZ{#Kwv+iV8O?yf`LwF$WP_x^v4n*uil z{(E?BwkvJuWX&zN?Nrk~rrYx^0i8V?z$DFNs*UT6TmwPoH|<@}nG@@b!*`i6wn#%D z?|1uAW2BLt&VOM3 zYkhP2`e*7HmGjk+YBZVR914h7%&d4_ysp2m|Cyesr`nuoUb>QwdRP>Yr-W}_F;Dm3 zwQSv@(xSkt=Ps6$3qFA7Z}JD#61ClEbJDALL$?%ef4zMozP|QM`a^0HG$FCsXbwa{ zjuH<)pz zdNw}){iWS@Wxw#V<=XWLuIId=^1!#IDlsg&b2^Jf461C2>UK~zY`t=3yiTXh`3@$Wf3?P<&DDbynE2Dr85A_KP&!ye2g3&sRA zvIH@@nE0kIKIt<)_}~j(-Za^Z8sANfiOFK3St1VVxVR~o29YtOz)E4IIlY{l4{M=+ zc*|P+y_|>t@BjJze*ff`9H=&)>~;Rji2#VD@Ba*pM28p&y^kQC0KBC<033esq z0$uVOENZvGHUSf`5AI3z=ekECAEk2HpuMWy1~kCnpwfd5#u3C2;Hm5Y5J*^c-Tm!v zi~WK3fYwsZ;UqiRc&M&PZ)pxkfDXWaUpBD;uE`5*F15lbr?Kp3c_BB5rWJf$G%KkuyXXpYlz z0FVKhPSt&E0N_&pux!Wo)^h3MJ;$irjoWbo@U**Q2L);d0I{$4{LiUz6DLgKQzzzZ4$^tgO z?cb>u0Hh;LB=qZrU|vvT;a;GBK?E2ZyXq??R1E-bE+{7%_PoU_bswNw=j<)pZX4|A z1DYj(d@gI15j&w@FI+LMsIhQwW-0k?2?!7{U&1S%CS^J7Ezxcp?C6_bN;Y%Rb1L>~ z`*_3^kqu|joXAXi3rz-hLu-oL?RiWB82{;=ddINuP19qxuw#k;1^k$QneyhFbnb@M z6j#vQQ+(0$x}wP5*{hRZYP)-Dx+8Bh;CXrbY&twJ`1D1I%gW{0Q2XZ(3lz)16X|f* zr86(g2b12pujXf2+258j(z?|uq}g}&V2a^QZPP39jQoY7$V%0KA%y*+Du}zASZxgv z(z0$i#VB4ehvIxHxG3O%&A2xt0J4B@W4+A{11p2DBUXsBzD=_|)W#s;bN9)l)NSk8gB4 zm2&v=YG&e_iP^<$b{kj_8lMl1W&7Ok=3EXAfnZUc_Iw}zZgN^mRn_I*^Sw=f1+ou- zMPLCiWLa)06bdH+7jPf=16UIxk!WYEEBdUvU4E?=0N4TY6+Zz-;!j>2Xa_$PF}DB! N002ovPDHLkV1k;F*eL)2 diff --git a/data/themes/default/note_tripletquarter.png b/data/themes/default/note_tripletquarter.png index 032549b9c768affcf476a7f7d1826ce44290ec4e..39c037b2a238f70ac269b19cdee434c1c0c53ac7 100644 GIT binary patch delta 702 zcmV;v0zv)s2i66UB!2{RLP=Bz2nYy#2xN!=000SaNLh0L00l7s00l7tx9uoW0000P zbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$SQ%OWYR7i>Kkv~imaTLeD zzw1GJcU)TzTj-HOfm(>7#*h#P6J4BK9oS58Fgl=7ObmmDM1LGi#F*${G!7VJ;$WOz zATc2>EIOePspaUOD_2^2*K&8g=K_ORHy4wB*Y79qllLAYA{@-@(zVfaF&%t);r;R- zC4h%3(}&ix+GswX{`~gRveAsWvv_q(D{cr}v$6rY2hh7_9RS$2?=%rxbbY?@zcC&# z=K(L83jSqUf`68vC1?p+g8xFm0|1+4{E#>_*(rA(;~0HC5le*+g7LZdcqAN|^tZKT zx+NvlEB}tLDdtc1C9fz!Wytfqwc|YlVF2KzX=!XEHuCy#e{#4ts=^}=nfwO9v?>6= zF0(dORTokNsfmQz4@rj}9JLNPqSv<7!le>$=F~GX&Gha^Ub- z?vRI-kb*>S0^MER5CbC1o7;W>5M}XVhtx3J#{i(+*FG;x za$E^17*G?4DiH{NffdZM$dgAsJLV1wMga}40o%5b({s>@`A-1g!Rs+=RT$d_zGv3J z^E`Azhkw3ZY-m~S0{}R#6RlJ$$QARj9UHs$F0y(KKlRNeo)LSL-b_ESY9>TMg5x?U zl}g|0jrz^K^S(05rR%P{+LhbN0br<@m0G!4p0VaF4FHJI(;wRU{i4@$YUn z>?YX+f?$&pP(nfqjU&T2cxs1w5$FLlRzDbpeNUiR=l@8@}*clMcGIP7|9*m}T@0Eo5c z{|rR>CWv@GM-Yz#_F5bOmVLmzl<`DhP=1%cv|C{6fDV{D=cKlwaK!)B{X)LOT+wa; z8c<~?({=^Z2!Emnur*cy2qX+gc(ngRakTwYpr;nI+es!r>v0^bbo;!1AOy754e_V| zU~+Hse&X)(pH{DYs-v$n1hmy}M?5M}Ta~rg`ZXymk(H_wz_az65RVGf_H`pwyjoPt z7+SdscmW3u0Zjw-E(NJ_GqaY_EWL6&AOkWdT*{FFfPXWild?JcFj>f!H!W|-VHC@8 zz}Bw>b`mrV0OE!@QX0oyRNIO3MS$iHJ!XU~5cZ za9g`{Sbt#8yg(`chjv{U5Itae0DEHslfQHSJPEjX@jGWNW*-1Zdx}U@el2wr1y{g3 z47f#rrvcea+N>l01ptLpY8R8X4><4m0+%sluA63XJTwCMB!FTeZ|ngsR4=#!-r>hf zshxIu8@|63%l3eO0Yl!B$Xa>haBhjUlkAKs)=L2u`PoE!u-KE)cT*_S`n<}!J zPO6GxdrktFzVT6;Jmr4>U?4R8=Gbd4&7R|O{s%It0;}ny+R~xCP(EjSCm8YA=k%*2 zOKapQLRXayUzS(KPM?vOQl|nF{gr&=Xmm9GRN9`(?GT*f6ht>RJ|qszVf`Ki569nm7nqm~VcpsY3nJTqUQ zbT_~3Om5AT&srk|)e3?DTSY<-+`8?$JwAV9Zb1^4gr)%HcUwFbor^l1uK0-B6LW^0 z=QcJs6;)HUnV)8EZ)saAKuqX7d+g)<%U8Bf_5))4=5MiIoK9DKcx1?P0I&co0##X- zdrGB}53mA{fK^~s=+Ks~T%>V!Z M07*qoM6N<$f{rP@(f|Me diff --git a/data/themes/default/note_tripletsixteenth.png b/data/themes/default/note_tripletsixteenth.png index 6314c398ae29efb500c4187134db20f91bc01694..a307b542a9edfe5aa61e91226995603f2a5793ea 100644 GIT binary patch delta 1249 zcmV<71Rne12#X4kB#|*0e+4lB00l7tx9uoW00007bV*G`2i*c17X%WitUz-B00fvx zL_t(Y$K91nXk29!$A8~<@11*RCiBQ7nY7bj6Rl~81q-!Z_*nFTl%g)gR*@EUC5SF{ zBf3&hsJIhEMJo!Gx{>Na5CsKA2@P88Thla6o6KY;Gnw~&9T&a9f7DK?;LZbw%jKN! z{LlG+=bTURwL zY0ihga?YPpK||wL(L>qc5w9I|)EWQ{*uc>KBRi)%K`X~i#2gUJfe8maA7>R{DRiimlE4evC?$xjO$4u~&9Gv{+5}xA9R{tGPc?oD zWrNZn0VrSDDpaA_E5r5i+s-X}#mSGK`DzQ$jevt~Q!k(R;QUcw z1u!DJ4{Uwm=%K@v?$V&<*5U&aX%kS*YAXq#6aqL=Z~+?8e>Yr9$=W-J1BEA#?ES7S z_2EXPc}{69D6VlND4i3X7&bG>b#|c>TbZ9sY^!gN?GaI-{NjJ3&a_S_ucWo(_Kmn@ zrFv1MJh}-#^i3ygPxh+Eorhq>#pt&;oX(!)Z2C8hxPS$*q=l&!LA{IiZ@gpy!jzR= zMB!YTzVfNIe{@G_6`&9V6?%a=v_3zP?o02U{o&Hr?G^WeVQW_>Bs5}zXmrpG?eQ(+ zkF-0jc&`eAw3RLUc*8%xefB+|99^_TT&MQGy6MrBlis~@sr~KrhZnw#xc4K1ua)TB{I18!A|1U#&m`{%Q>K7A-0_(VO}3PoIA?2meX_KLD!7WYc32s$2Nn5W|c> zR^NgcV2NA@sMh#cfE9+KdjK1eUKhiR4%yKjN&p!NLSs#ZW@KCS$#@;l1_uezYu*tZ z%|H^!e~5?{L770BK86Yh3y1pi{S#ifTUu&WGe%5SGfo0__Mz;3g>8k$XiDvhEM*3y zkMLR|HeiuuVC3M))BOYeW1VuRT&!2JTBA&BkF|4b=h$1D$HzxDflWK_czt}+_+TMl!1sOT7Uv{b zt}knI!h9t&kQvEka*S>mWq4qigp*LkcFh7Td!PMywvgSGO=Tzyt!K2bk#sUeEAXgF zdkBa-o1GUk{h7gBCdXJ|lzeW8xE)uePTe*|ORnCmQ>+vT!T{HGStu=Vd2Zn};EE#y zf6aD-O0~itbAMu*CZ$S=;&Lgx_~X^@0pG*xUumqcu(XKlxpdtwS4#`b&R(82r6ixs zTC*>;+D#JiB%be4S*%=XOgG*DmV;BlvGQQ$fbaY1h4KQRsMTw3^+NTX;B;^XkZ?Kt zY{tC!T%*+_?!@stm!)EPra9GoQvoB^S6=HuJ*e+dw%RC7`MrC*dxR!SKnJkR9p+ue zn6Vd!+tG+W<-Z%849-T|79edtZ0<8s#@!N^4N{dU@3?mim!-b|(SizxToL0q00000 LNkvXXu0mjfx-ndm delta 1077 zcmV-51j_r13gQTmBnkm@Qb$4nuFf3kks%y^3ljhU3ljkVnw%H_000McNliru*a#Z} zAP<9XLf`-Z1L8?UK~zY`rPgmu)m0qF@%Q=tF3A1QAAyTpM24D@nkd53nW+nc2ZEK- zh|uvQx3D?4oaVyHC=^y(Pn_j!&G97G$~gpCZM9jN%D+Iv5L_|5%=jlh0Fz67=(oJ#FFZp(kiT9Gy*&W!~%wy0ihx~R^#&Wq2tmj&0;>fRB*<=0VD!O zKx3YW35Z&9Dob*2?v#3|lhXdzg!H6;o4{%yayno>NIcnzJ-e>&6_!|s5^L7VyK+p( za0~+*gAdpqBC>@ov7Z#XN`(Sv0HrRFV()v>l_eOo)4}Hu5fY-SPk|iU6{q*oqXt=5 z|E!SWxW?1bcg@c)4Fm^-vpxRgToF@FYCtJfqK-H`VF!`}2fc@+LMlYhO7c2?3Rg*R zfDSlAM2?UjKVkbW*@vcGCO^*qij)XWyK82l*|fJ3#zR0pU{9jQc4hGd$;OL7B`ac@ z^)pE_vmO>u;P8fkoWNl$vtLg3Qk3We_5-_lHBPh{0iq%eeoP`HEg0qe{i>RkbuIck zHV2?{Y3PvhD`8XgcA$^W!Ak#s4?Z?zZnDtt?N)<&1lLCIOm%|Zb+;Dh{Ubnr9DQl7 zNawHZ-vh%VUyhHdoz}FeHD7bnjh2}Ke!51RgArqd`QCpZtz%=OO@NE;iF5Lzo7Jnb zG5|B+)c{T$ubFO60l+;{>lvL!bVJyq=B68MvmjQsTJkAe@Drt$TiEq~=ZMtF+nc)s zNCGSdfFbv=7Sxy&JriF@Sz!O_`mBuhj-RjU+w2AY*okt?VOIL|J|czOlAImwS39;H z{`_F{X5$80eQg}ok1gYYnX@f5;Xo?T@prq{bE8XnIl@J$Wt^Po-{_2zLnq69ykH(Y z{GeRw9$t`&vG)a!F5I(!igouQQQh&fwttPJ-b<12yH?>&l+Ya|yloQTBOucNXzaH9 z{^n<8&c}8#V&c?|GvTVr65ZhahT8FS^!f(CZvzoDqmB&dw7Q0;N+>eu*?NRhDojK) z1Np`h^`Y50CnO-K1V40`z!dTLHSze!_vCP1zM2FAhA3NHrK~2M8pI90We^YutMtHiUru0`vh%azOWdY)`H0(mM0g*@+C_(~(1W^(KiZv*f<2V>Q&Wz)k?&-($f4jP>?qShwd2COz z<48xks&mhO&VTQJ>Iyea02jy}KXv#-5DoV=-rKoiWEM4w@ewnpCcEV4GG)J-8qK|b zkMlq;<7D-sJUCJ=rGie+p6CVCzog-j-_sFCnR6C}FRDhl``znmDSYMpc4 zi`LL~=hSYeuj8bMt+h#wF@OXz$=jVS9MAHTkzyk8rZytZSJ!zJ1KO!Hfo_Owaf3J?>-h3R{4kC)GfC_*! zha~1D;Z?+!d^nmbX2MUJ2c_sNaM_-dbCZ8+v@K#go^R_W-ira2*i{hYn&Qp-pPG&# z*qFML_57d^_o^RO0B%pikk=j_3YVCL`f5iBPxMt6vR%@ z2>tuNc<)pDe?GhLKD46N{q*RdW^0Wu~PixO!dI=cJq$;yvE#CLy zr|%2HfBNE{3}klZKo@8M<5|oF;$%880`PoyG@Fwy5YKXwfFmMSguPX?YaIb0FqwU~ zz(kyB17fhd_S|4#fJkJD_S1Z+pDp3@% zzO}A#yVo|(LFb40a(<DdIng>#;)zs7ZLcUVUm$Re^2_-826H{ik5qW9`#n8*v3pI6E+*0Y^-ly z04AE$@?g}b)oJnH^~<=fOQY4G-fq};U%UP?5Ql_?ZokXs_7;0IiZdXC?U z-v-pS?eDI*?|!d89N?#X!f?WNy?JGDcJN~Xmc3)%7fjiFN>Z{<`}$V!Qt)f~Y|nmj zN%sNw9;fCUBgNeqh|Wg8irBh3`U$eMPO&r2dm}BAuc@tZ}()ZHRyWX{b z*Y@@YmHC!weUi&Pd7j+o_q*rrd49l?&DrqO5b9S0&Ib82##>NsvI3w1ka%v(4l*d#Ci57Jo- zZIm~)udEuM^f!wmu|5Ftg{9#56jwuSdkN$QlycG7T(TwHAB_)wadTwst{di)73Scd ztPw%s*5X>nwWLF}GEbyGZbVe*i`&F4$KKz^2xh8y>caGJ=yrIHs#i6XcnYQ$14vq> z*0{m2BOioZrD`Y|W=7`hNrz8=7tro{W3$Y%wX%Hl+EgYpnHbZs@`;B52%=n(iI?$* z@*`EJ?M~}dZYn2ClTE8w|1Qv$T zHmNG*(+m|j=eptQ_ofv4wbjWh@##l*9+733mP8G2&KE&Qyi(9mylumOey2`p(N}nT z{CCrSAz>^uRXi2|c1^>QS81Dkijlo2Qc_r1!H#(^Q_V}7IXa;#D=j8gA9Pr!x_mL* ziC%pC>`0TIrL~3a8}>EoUlSCO_KI?MZ-Aiyprhj~ zy1Rb{=2E<@MR$Nza!gr&X))2bf0fnM?THaXvHwNj=et2siR^aiP3`aMnP`{is_$xO zk|?LmR7b~I?U^%277wUoiL8+|u&<0Cy)zVv#nY9C%F126{%0dSp;?%SI#^+ZjMAct ztgF*H^xi)AFPImv5z%CqOyJop0OH|xkj7HoslDW0tzMtrxMFnk=qg^BAyOV*!8gP9+BYYZ-3nP+*Wd+3$#zM%J6+BW}7QSNPfB-tC2mp`)AZ4h3Qfl%-G7^%mc~lP4_kAsb zh1bXdf&e%HHFE_SRUb3-_szcs0R#aEGIET_3#pFiGxQDB@c7{9fFn_R*|5g&dMF&0 zYZ977|K?m0tlsJa^9kkp2ZQyK44JYOK)! O0000mzLe*g(^ zNLh0L00l7s00l7tx9uoW00007bV*G`2i*Y~6E7~Jue^T%00L4;L_t(Y$L&>3XcJKo z{$_vk)9oga)L5Dn5sG*a#Giu^EJ(2z6?^H)Uc73B9=z$*gSQIOo(mqNMbIL65DXq~x>xpTH<+fgm*uM}2ZFN`kC z18@LTCD1P*)C52W(1ugUnnUKDY*)4~XJxfa`~YmjhR*_(Z(g^9wP45Id&?diM<*C4VTBtGXQFV``8iS zC->R~t(%h6&&*y3PJKEqjaJ^-jU(i5g4lI14Md**#g(Etmsa)~E+kzc}xKsSyme_3UaB6N^_cEuBM z-h1fHNXd%eMTY?>0Ae_aoO#t8CyVksmCr7h{Wt!wJLWzQlWDU)&;UdcLBhCc3?hSm ziqc^K0(F%AFa9$;#-#h!Ey>K?!1!pX(Y8gP%WgIUq7ahvO}ToFng7YK+ipB1@a;Ev l=&+-|Fw%MN)%(Bg6$dix-3&BC`4|8I002ovPDHLkV1hvkK^_1A literal 3718 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 F{{W0Z{v!YY From 8bc7f8ce54fe2a0d6a1e6a9aceeb63795cfdc350 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 27 Feb 2013 22:29:06 +0100 Subject: [PATCH 45/46] Updated chord and scale icon based on new note icons --- data/themes/default/chord.png | Bin 1006 -> 1044 bytes data/themes/default/scale.png | Bin 1006 -> 1056 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/default/chord.png b/data/themes/default/chord.png index 3f475b0ea497624eca88f9553b2ad24ad16105a5..8aa83cdbeb43597f5ccf458d1b2219f836692b3e 100644 GIT binary patch delta 1022 zcmVnJebcI4y6c9onfs>t6ohO%?0KnR8Hba)@V4Ptb`pBj^ z3Q@oheMCuwgfrwkXG;O3lz&Dn0u3n$A>bKDO|L-+fqytn;D=Rf zd%2pwJn}~yX_jJ4O=y}1Aq05F5wYktQOK*ZA}I?~nCx$1`ZMm?m z1~m}k>(sx z5}~Hoz&Jx83ItJrAPy0y37BC1QUC(-bCM?K$A1$KkP?iu2R%r50zaJKC^-5t`!G9L z3Xt0Ar$%JFI1c@%SeC-122}Na4{{Ek@!z50mC8ddN)fq0blRZXM3PMarvVfIt^>Hn zOa8IkmAp#7um|!JIcvYu{-xLIJ<+tAkC@b;AmntGWlslj@E3sZ{?`P~+s?&f?y=p~ z?tiMiUL9ih(g%POgS~rqdKO9 zG_q+1#uCw>mL-xjfv)KgLV$4w zKbpXsc>DY+zqJ&A|IPP}HRG8u4mVkvz@R2nMMYWO>%p{`eZQiwyjFP&|K;0aZksOv s*aYyHgv|ZvX%Q07*qoM6N<$f(QZHUjP6A delta 984 zcmV;}11J2H2<``vB!3BTNLh0L01FcU01FcV0GgZ_00007bV*G`2i5`>4I>z$9CFnF z00WIlL_t(Y$E}r3ZyQw}2n&eo+AfU~+kYu`a60a|8P5z0rVxUX z3?E0jtGPPQdCz&z9dHtH+IW)>06*XbBpimz(XD6cjh~Hwy6j(mO8-Xx2@4Hq02cp4 z2+lElW$B7FyF6?4zSo=J0H=TepzNi_U8%29>#UIjHh%yEKz2B0Ni|i3EMSSBQx{S* zsbI>BL<=68Im*7kL?FzI#7OcbP_$K;&Hy@ErBOj@l3*Vet9(z9jQv$1OLKX;{3Idv z1LoGwI0IB!yRY9zaVrqQ$G50J2KW^afQ*TkG#;RCpML--acAYh3Y|(P9HPVCX3HL^ zVM8i*<$uCL@JbBo95#OYuvuhW%k` zR*f1pR7<61)u>V>XXXet1z9u~TeQ!$n?N3Tu+tp=+M){_9gU5?=9b+Q^&-M0z=E<> zW`Fk4ED1A#)}(_Kj&}tWI>;;hI1M*~FpidO_BEk|_rp3HE&-KmgG?d2giDf(8 zG%HU2Xj=io+vf` zfF&RaWNihG0K$I6`hf@#vIp1zYIdasV8uRV;Nfn`0e%C+&sPW89Z{A50000JNR7i=Xl*@}8RTRd* zQ}3#&s(w~?wQ=HNz<(g1q9CYi9}F=IaT6jz42DRYxRDGEf`6D%5JAO_M3I2_0Jn-{ zkwp*}ZU!aHCI-iZba%b$R&~9u3&$X0#-TjxbH3j>_ni9?=N$hhCYzJTI-SmQmSH_0 zi?Y=4=GT;^Uv3}T{)9ivYibLiDQRPjKX3=`@o|5IuHA*Es3^)3K^)+=H~aqj?E1RY zwsSq!->}E_@qfPE!)P#qJ9J?g7NRtQD2T|H`4j!Le@1B4{V6gfQF>XHB*>BsMOL7z z8cbsak|=>_FOj3&1e&4^tGZH_#S$!M2;&d}37Go}qzx@PBTP19`xl|_+% zJ7?afH_qMoolcQy>6R8}an9f8$=_@L6JVdS9~litU$}$uWY8VJG)w?M!V)Zk#eC+? z*RO3{yMJ=G`hAydoHbUvcIUgE-P4_(gNZZ2us1;6G?;la)J;81v*a_H(i-37)xT-% z{|^Wok|gVjBtuaY=$Z!G>_As_h@uE00zikI(s{Vm1OQgcvP53wNLY$R;9*%TQC1cF z$VZe!NV61ao@T881lkUh@EJ{2K_nnsW>94XRexO}VJW=O!`z?$%q;fPT{ZSwvUS>W zYNh@Wz7H=<&I`6_Kcp!tIByD;WtYR)oBs0Z&Q+R{rbg;v z;(t`-RldvLPBLJ|>QfwbeD;J354(-GgbYD5|PnpeePob^#nGi3AW4i2gK_vMNzj zC90}IRadB+`u=Xvwc7wB09Lll_RcN52X#8mxoh0?0V=t7!0f_D*)WtvCSu0fsodP?GsK5&!@I07*qoL4I>z$9CFnF z00WIlL_t(Y$E}r3ZyQw}2n&eo+AfU~+kYu`a60a|8P5z0rVxUX z3?E0jtGPPQdCz&z9dHtH+IW)>06*XbBpimz(XD6cjh~Hwy6j(mO8-Xx2@4Hq02cp4 z2+lElW$B7FyF6?4zSo=J0H=TepzNi_U8%29>#UIjHh%yEKz2B0Ni|i3EMSSBQx{S* zsbI>BL<=68Im*7kL?FzI#7OcbP_$K;&Hy@ErBOj@l3*Vet9(z9jQv$1OLKX;{3Idv z1LoGwI0IB!yRY9zaVrqQ$G50J2KW^afQ*TkG#;RCpML--acAYh3Y|(P9HPVCX3HL^ zVM8i*<$uCL@JbBo95#OYuvuhW%k` zR*f1pR7<61)u>V>XXXet1z9u~TeQ!$n?N3Tu+tp=+M){_9gU5?=9b+Q^&-M0z=E<> zW`Fk4ED1A#)}(_Kj&}tWI>;;hI1M*~FpidO_BEk|_rp3HE&-KmgG?d2giDf(8 zG%HU2Xj=io+vf` zfF&RaWNihG0K$I6`hf@#vIp1zYIdasV8uRV;Nfn`0e%C+&sPW89Z{A50000 Date: Wed, 27 Feb 2013 21:57:05 +0100 Subject: [PATCH 46/46] Made 0.4.14 release Bumped version number to 0.4.14 in CMakeLists.txt, README and lmms.rc.in. --- CMakeLists.txt | 2 +- README | 4 ++-- lmms.rc.in | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f37ef069..ba56de4ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "0") SET(VERSION_MINOR "4") SET(VERSION_PATCH "14") -SET(VERSION_SUFFIX "rc1") +#SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) SET(VERSION "${VERSION}-${VERSION_SUFFIX}") diff --git a/README b/README index b6a49776c..76087fd25 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ -Linux MultiMedia Studio 0.4.13 +Linux MultiMedia Studio 0.4.14 =============================== -Copyright (c) 2004-2012 by LMMS-developers +Copyright (c) 2004-2013 by LMMS-developers This program is free software; you can redistribute it and/or modify diff --git a/lmms.rc.in b/lmms.rc.in index 11d548768..311e9a5ba 100644 --- a/lmms.rc.in +++ b/lmms.rc.in @@ -2,7 +2,7 @@ lmmsicon ICON data/lmms.ico #include VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,13,0 + FILEVERSION 0,4,14,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP @@ -17,7 +17,7 @@ BEGIN VALUE "CompanyName", "LMMS Developers\0" VALUE "FileDescription", "Linux MultiMedia Studio\0" VALUE "FileVersion", "@VERSION@\0" - VALUE "LegalCopyright", "Copyright (c) 2004-2012 LMMS Developers\0" + VALUE "LegalCopyright", "Copyright (c) 2004-2013 LMMS Developers\0" VALUE "OriginalFilename", "lmms.exe\0" VALUE "ProductName", "LMMS\0" VALUE "ProductVersion", "@VERSION@\0"