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/data/locale/de.qm b/data/locale/de.qm index 57adb4878..0d300fb4e 100644 Binary files a/data/locale/de.qm and b/data/locale/de.qm differ 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 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 @@ - + diff --git a/data/themes/default/chord.png b/data/themes/default/chord.png index 3f475b0ea..8aa83cdbe 100644 Binary files a/data/themes/default/chord.png and b/data/themes/default/chord.png differ diff --git a/data/themes/default/note.png b/data/themes/default/note.png index 3f475b0ea..f81425dae 100644 Binary files a/data/themes/default/note.png and b/data/themes/default/note.png differ diff --git a/data/themes/default/note_double_whole.png b/data/themes/default/note_double_whole.png index 90b7a7c0e..4b1e934bd 100644 Binary files a/data/themes/default/note_double_whole.png and b/data/themes/default/note_double_whole.png differ diff --git a/data/themes/default/note_eighth.png b/data/themes/default/note_eighth.png index 762888208..f81425dae 100644 Binary files a/data/themes/default/note_eighth.png and b/data/themes/default/note_eighth.png differ diff --git a/data/themes/default/note_half.png b/data/themes/default/note_half.png index d73ccb4f5..aff73e593 100644 Binary files a/data/themes/default/note_half.png and b/data/themes/default/note_half.png differ diff --git a/data/themes/default/note_quarter.png b/data/themes/default/note_quarter.png index 017468855..6c3acf2cd 100644 Binary files a/data/themes/default/note_quarter.png and b/data/themes/default/note_quarter.png differ diff --git a/data/themes/default/note_sixteenth.png b/data/themes/default/note_sixteenth.png index 33cb9ad7a..661bcb7be 100644 Binary files a/data/themes/default/note_sixteenth.png and b/data/themes/default/note_sixteenth.png differ diff --git a/data/themes/default/note_thirtysecond.png b/data/themes/default/note_thirtysecond.png index 80f970a7c..a3daee579 100644 Binary files a/data/themes/default/note_thirtysecond.png and b/data/themes/default/note_thirtysecond.png differ diff --git a/data/themes/default/note_tripleteighth.png b/data/themes/default/note_tripleteighth.png index b33cd4fb6..4e4d214de 100644 Binary files a/data/themes/default/note_tripleteighth.png and b/data/themes/default/note_tripleteighth.png differ diff --git a/data/themes/default/note_triplethalf.png b/data/themes/default/note_triplethalf.png index 58343a8fc..5c8a7e90c 100644 Binary files a/data/themes/default/note_triplethalf.png and b/data/themes/default/note_triplethalf.png differ diff --git a/data/themes/default/note_tripletquarter.png b/data/themes/default/note_tripletquarter.png index 032549b9c..39c037b2a 100644 Binary files a/data/themes/default/note_tripletquarter.png and b/data/themes/default/note_tripletquarter.png differ diff --git a/data/themes/default/note_tripletsixteenth.png b/data/themes/default/note_tripletsixteenth.png index 6314c398a..a307b542a 100644 Binary files a/data/themes/default/note_tripletsixteenth.png and b/data/themes/default/note_tripletsixteenth.png differ diff --git a/data/themes/default/note_tripletthirtysecond.png b/data/themes/default/note_tripletthirtysecond.png index 81ab31411..20e2ab65a 100644 Binary files a/data/themes/default/note_tripletthirtysecond.png and b/data/themes/default/note_tripletthirtysecond.png differ diff --git a/data/themes/default/note_whole.png b/data/themes/default/note_whole.png index 3764b94b9..f178b5023 100644 Binary files a/data/themes/default/note_whole.png and b/data/themes/default/note_whole.png differ diff --git a/data/themes/default/scale.png b/data/themes/default/scale.png index 3f475b0ea..fa680ab76 100644 Binary files a/data/themes/default/scale.png and b/data/themes/default/scale.png differ 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/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/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/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..7b6a813d5 --- /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; + float ppqPos; + int timeSigNumer; + int timeSigDenom; + bool isCycle; + bool hasSHM; + float cycleStart; + float cycleEnd; + int m_bufferSize; + int m_sampleRate; + int m_bpm; + +#ifdef VST_SNC_LATENCY + float 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/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/include/midi.h b/include/midi.h index 61a9e8ebf..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 * @@ -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; @@ -122,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; @@ -134,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; } @@ -145,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 ) { } @@ -212,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) @@ -226,6 +248,10 @@ struct midiEvent const char * m_sysExData; const void * m_sourcePort; + +private: + bool m_fromMidiPort; + } ; 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/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" 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; +} + diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index c97aa2c7a..729e9e429 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(); } @@ -919,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 ); 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() ); } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 46630d740..eeb536f58 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -117,22 +117,31 @@ 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]; - QStringList list1; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; + QStringList s_dumpValues; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + for( int i = 0; i < paramCount; i++ ) + { + 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] = 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 ) ); - 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] ); @@ -167,9 +176,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 +233,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( @@ -250,7 +263,7 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } - m_plugin->showEditor(); + m_plugin->showEditor( NULL, false ); if( set_ch_name ) { @@ -308,6 +321,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 ) { @@ -685,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; } @@ -790,10 +847,16 @@ 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() + + tr( " - VST plugin control" ) ); + } // m_pluginMutex.unlock(); } @@ -814,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->m_plugin->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); @@ -831,49 +895,77 @@ 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 ); + + + 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 ); + } + 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; + QStringList s_dumpValues; 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(":"); + 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] ); } } 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); @@ -892,29 +984,84 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume +void manageVestigeInstrumentView::closeWindow() +{ + m_vi->m_subWindow->hide(); +} + + + + 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 ); + } + } +} + + + + +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 ) + { + 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 +1076,8 @@ manageVestigeInstrumentView::~manageVestigeInstrumentView() delete m_vi->m_subWindow; m_vi->m_subWindow = NULL; } + + m_vi->p_subWindow = NULL; } @@ -989,6 +1138,8 @@ void manageVestigeInstrumentView::dropEvent( QDropEvent * _de ) void manageVestigeInstrumentView::paintEvent( QPaintEvent * ) { + 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 632b6dee6..600e87e22 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; @@ -110,7 +111,9 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); protected: @@ -127,6 +130,8 @@ private: QWidget *widget; 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 aaae0c270..122dda3bc 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 + { + float lastppqPos; + float m_Timestamp; + } ; + + in * m_in; + + int m_shmID; + sncVST * m_SncVSTplug; + } ; @@ -324,12 +349,59 @@ 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 ) { @@ -347,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 ); @@ -451,7 +538,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; @@ -486,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 @@ -558,10 +651,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 @@ -1349,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 ) / (float)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/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 59d75fb53..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 { @@ -574,7 +605,7 @@ void VstPlugin::setParam( int i, float f ) { lock(); sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); - waitForMessage( IdVstSetParameter ); + //waitForMessage( IdVstSetParameter ); unlock(); } 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/VstEffect.cpp b/plugins/vst_effect/VstEffect.cpp index 2ada8ca14..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 ) ); } @@ -162,6 +163,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..2261f2466 100644 --- a/plugins/vst_effect/VstEffectControlDialog.cpp +++ b/plugins/vst_effect/VstEffectControlDialog.cpp @@ -36,34 +36,54 @@ #include #include - +#include "gui_templates.h" +#include +#include + VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), - m_pluginWidget( NULL ) + m_pluginWidget( NULL ), + m_plugin( NULL ), + tbLabel( 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" ) ); + setMinimumWidth( 250 ); + + QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); btn->setCheckable( true ); - l->addWidget( btn, 0, 0, 1, 13, Qt::AlignCenter ); connect( btn, SIGNAL( toggled( bool ) ), m_pluginWidget, SLOT( setVisible( bool ) ) ); - btn->setMinimumWidth( 200 ); - btn->setMaximumWidth( 200 ); + emit btn->click(); + + btn->setMinimumWidth( 78 ); + btn->setMaximumWidth( 78 ); btn->setMinimumHeight( 24 ); btn->setMaximumHeight( 24 ); - m_managePluginButton = new pixmapButton( this, "" ); m_managePluginButton->setCheckable( false ); m_managePluginButton->setCursor( Qt::PointingHandCursor ); @@ -78,13 +98,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 +122,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 +131,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 +147,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 +156,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 +172,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 +182,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 +201,46 @@ 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 ); + 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->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->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( 1, 1 ); - 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 ); + 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 ); } -#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 +248,12 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : void VstEffectControlDialog::paintEvent( QPaintEvent * ) { - + if( m_plugin != NULL && tbLabel != NULL ) + { + tbLabel->setText( tr( "Effect by: " ) + m_plugin->vendorString() + + tr( "       
" ) + + m_plugin->currentProgramName() ); + } } @@ -226,7 +261,7 @@ void VstEffectControlDialog::paintEvent( QPaintEvent * ) VstEffectControlDialog::~VstEffectControlDialog() { - delete m_pluginWidget; + //delete m_pluginWidget; } diff --git a/plugins/vst_effect/VstEffectControlDialog.h b/plugins/vst_effect/VstEffectControlDialog.h index bd4687511..de8b748dd 100644 --- a/plugins/vst_effect/VstEffectControlDialog.h +++ b/plugins/vst_effect/VstEffectControlDialog.h @@ -26,9 +26,11 @@ #define _VST_EFFECT_CONTROL_DIALOG_H #include "EffectControlDialog.h" +#include "VstPlugin.h" #include #include +#include class VstEffectControls; @@ -57,6 +59,9 @@ private: pixmapButton * m_managePluginButton; pixmapButton * m_savePresetButton; + VstPlugin * m_plugin; + + QLabel * tbLabel; } ; #endif diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index 866c8c4ed..0dd31ed10 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -61,30 +61,39 @@ 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 ) { 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]; - QStringList list1; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; + QStringList s_dumpValues; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + for( int i = 0; i < paramCount; i++ ) + { + 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] = 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 ) ); - 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] ); @@ -120,13 +129,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(); @@ -303,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); @@ -320,51 +331,80 @@ 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 ); + + + 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 ); + } + 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; } char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; 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(":"); + 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 ] ); } } 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); @@ -383,24 +423,63 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * +void manageVSTEffectView::closeWindow() +{ + m_vi2->m_subWindow->hide(); +} + + + + 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 ); + } } } +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 ) { @@ -418,8 +497,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..bc33a2088 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; @@ -112,7 +113,9 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); private: @@ -128,6 +131,8 @@ private: QGridLayout * l; QPushButton * m_syncButton; + QPushButton * m_displayAutomatedOnly; + QPushButton * m_closeButton; } ; 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 78733faed..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 * @@ -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(); @@ -447,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() ); } 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 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" diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 30c3aef6b..5868d19b4 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 } }, @@ -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 } }, @@ -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 } }, } ; 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(); diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 49c61e674..eb6949356 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 * @@ -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" ) ) { @@ -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 ); } } @@ -164,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 || diff --git a/src/core/song.cpp b/src/core/song.cpp index 58a1e31a7..6fdba2b21 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 * @@ -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 / + ( (float) 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 / + ( (float) 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 ) / (float)48 ) - + m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)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 ) + / (float)48 ) + - m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (float)48 ); +#endif } } m_playPos[m_playMode].setTicks( ticks ); if( check_loop ) { + m_SncVSTplug->isCycle = true; + m_SncVSTplug->cycleStart = + ( tl->loopBegin().getTicks() ) + / (float)48; + m_SncVSTplug->cycleEnd = + ( tl->loopEnd().getTicks() ) + / (float)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; } @@ -1096,8 +1236,9 @@ void song::importProject() tr("MIDI sequences") + " (*.mid *.midi *.rmi);;" + tr("FL Studio projects") + - " (*.flp" - ");;" + + " (*.flp);;" + + tr("Hydrogen projects") + + " (*.h2song);;" + tr("All file types") + " (*.*)"); @@ -1215,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 / + ( (float) m_SncVSTplug->m_sampleRate * 60 ); +#endif +} + + + + void song::setModified() { if( !m_loadingProject ) 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" 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 { 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 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 diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index 93527c657..2a385093c 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 * @@ -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 ); @@ -103,6 +104,8 @@ void exportProjectDialog::reject() { (*it)->abortProcessing(); } + + QDialog::reject(); } @@ -198,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(); } @@ -219,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(); } @@ -290,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; } } 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; 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/gui/widgets/pixmap_button.cpp b/src/gui/widgets/pixmap_button.cpp index 0320b4932..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()->value() || m_pressed ) + if( ( model() != NULL && model()->value() ) || m_pressed ) { if( !m_activePixmap.isNull() ) { diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 267af765b..5dcac3f11 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 @@ -315,6 +327,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 ); @@ -1036,9 +1059,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; } 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 ); }