diff --git a/.travis.yml b/.travis.yml index 24c6df1c9..337591273 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ matrix: - env: TARGET_OS=win64 - os: osx before_install: - - sh ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.before_install.sh + - sh ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.before_install.sh install: - sh ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.install.sh before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ba886503..f801852f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "1") SET(VERSION_MINOR "1") -SET(VERSION_PATCH "0") +SET(VERSION_PATCH "3") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) diff --git a/data/locale/ca.ts b/data/locale/ca.ts index edc368108..bfd41347d 100644 --- a/data/locale/ca.ts +++ b/data/locale/ca.ts @@ -3435,7 +3435,7 @@ Per favor, comprova que tens permís d'escriptura per a aquest fitxer i tor Obre projecte existent - Recently opened project + Recently opened projects Projecte obert recentment @@ -5936,7 +5936,7 @@ Latència: %2 ms Track - Muted + Mute diff --git a/data/locale/cs.ts b/data/locale/cs.ts index b0381f897..f08c7b394 100644 --- a/data/locale/cs.ts +++ b/data/locale/cs.ts @@ -3435,7 +3435,7 @@ Ujistěte se prosím, že máte k souboru právo zápisu a zkuste to znovu.Otevřít existující projekt - Recently opened project + Recently opened projects Naposledy otevřené projekty @@ -5938,7 +5938,7 @@ Zpoždění %2 ms Track - Muted + Mute Ztlumený diff --git a/data/locale/de.ts b/data/locale/de.ts index 090e2bc79..f75a18bca 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -3456,7 +3456,7 @@ Bitte überprüfen Sie Ihre Rechte und versuchen es erneut. Existierendes Projekt öffnen - Recently opened project + Recently opened projects Zuletzt geöffnete Projekte @@ -5974,7 +5974,7 @@ Latenz: %2 ms Track - Muted + Mute Stumm diff --git a/data/locale/en.ts b/data/locale/en.ts index 0c4bae00b..2512f04c6 100644 --- a/data/locale/en.ts +++ b/data/locale/en.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. - Recently opened project + Recently opened projects @@ -5927,7 +5927,7 @@ Latency: %2 ms Track - Muted + Mute diff --git a/data/locale/es.ts b/data/locale/es.ts index 3ba7312a3..f4fca86c3 100644 --- a/data/locale/es.ts +++ b/data/locale/es.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. Abrir proyecto existente - Recently opened project + Recently opened projects @@ -5927,7 +5927,7 @@ Latency: %2 ms Track - Muted + Mute diff --git a/data/locale/fa.ts b/data/locale/fa.ts index 50d1e790a..028f75631 100644 --- a/data/locale/fa.ts +++ b/data/locale/fa.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. باز کردن پروژه ی موجود - Recently opened project + Recently opened projects @@ -5927,7 +5927,7 @@ Latency: %2 ms Track - Muted + Mute diff --git a/data/locale/fr.ts b/data/locale/fr.ts index 6a3b0d36e..7051a47a8 100644 --- a/data/locale/fr.ts +++ b/data/locale/fr.ts @@ -3448,7 +3448,7 @@ Veuillez vérifier que vous avez les droits d'accès en écriture pour ce f Ouvrir un projet existant - Recently opened project + Recently opened projects Projets ouverts récemment @@ -5947,7 +5947,7 @@ Veuillez vérifier que vous avez les droits en lecture pour ce fichier et le ré Track - Muted + Mute Coupée diff --git a/data/locale/gl.ts b/data/locale/gl.ts index 98c130485..16d0345fd 100644 --- a/data/locale/gl.ts +++ b/data/locale/gl.ts @@ -3448,7 +3448,7 @@ Please make sure you have write-access to the file and try again. Abrir un projecto existente - Recently opened project + Recently opened projects Proxecto aberto recentemente @@ -5946,7 +5946,7 @@ Latencia: %2 ms Track - Muted + Mute Silenciado diff --git a/data/locale/it.ts b/data/locale/it.ts index 99bc63bf4..76ed7b446 100644 --- a/data/locale/it.ts +++ b/data/locale/it.ts @@ -3456,7 +3456,7 @@ Assicurati di avere i permessi in scrittura per il file e riprova. Apri un progetto esistente - Recently opened project + Recently opened projects Progetti aperti di recente @@ -5971,7 +5971,7 @@ Assicurati di avere almeno i permessi di lettura del file e prova di nuovo. Track - Muted + Mute Muto diff --git a/data/locale/ja.ts b/data/locale/ja.ts index 2aa1b1e50..dfd218cb1 100644 --- a/data/locale/ja.ts +++ b/data/locale/ja.ts @@ -3450,7 +3450,7 @@ Please make sure you have write-access to the file and try again. 既存プロジェクトを開く - Recently opened project + Recently opened projects 最近開いたプロジェクト @@ -5950,7 +5950,7 @@ Latency: %2 ms Track - Muted + Mute ミュート diff --git a/data/locale/ko.ts b/data/locale/ko.ts index aea6c5611..af28adb04 100644 --- a/data/locale/ko.ts +++ b/data/locale/ko.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. 기존 프로젝트 열기 - Recently opened project + Recently opened projects 최근 열린 프로젝트 @@ -5932,7 +5932,7 @@ Latency: %2 ms Track - Muted + Mute 무음 diff --git a/data/locale/nl.ts b/data/locale/nl.ts index 2b4416f46..25d761d00 100644 --- a/data/locale/nl.ts +++ b/data/locale/nl.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. Open bestaand project - Recently opened project + Recently opened projects @@ -5929,7 +5929,7 @@ Vertraging: %2 ms Track - Muted + Mute diff --git a/data/locale/pl.ts b/data/locale/pl.ts index 60a86b97c..afec439e7 100644 --- a/data/locale/pl.ts +++ b/data/locale/pl.ts @@ -3453,7 +3453,7 @@ Upewnij się, że masz prawo zapisu do tego pliku i spróbuj ponownie.Otwórz istniejący projekt - Recently opened project + Recently opened projects Ostatnio otwierane projekty @@ -5952,7 +5952,7 @@ Upewnij się, że masz przynajmniej uprawnienia odczytu tego pliku a następnie Track - Muted + Mute Wyciszone diff --git a/data/locale/pt.ts b/data/locale/pt.ts index b797a15d5..b26c78813 100644 --- a/data/locale/pt.ts +++ b/data/locale/pt.ts @@ -3489,7 +3489,7 @@ Double click to pick a file. Mostrar/esconder Editor de Automação - Recently opened project + Recently opened projects Projetos usados recentemente @@ -5950,7 +5950,7 @@ Latência: %2 ms Track - Muted + Mute Mudo diff --git a/data/locale/ru.ts b/data/locale/ru.ts index 8d8fe034b..22bb33375 100644 --- a/data/locale/ru.ts +++ b/data/locale/ru.ts @@ -3477,7 +3477,7 @@ Please make sure you have write-access to the file and try again. Открыть существующий проект - Recently opened project + Recently opened projects Недавние проекты @@ -6003,7 +6003,7 @@ Latency: %2 ms Track - Muted + Mute Тихо diff --git a/data/locale/sv.ts b/data/locale/sv.ts index 632307fa6..6d0d7e3ee 100644 --- a/data/locale/sv.ts +++ b/data/locale/sv.ts @@ -3433,7 +3433,7 @@ Please make sure you have write-access to the file and try again. Öppna existerande projekt - Recently opened project + Recently opened projects @@ -5928,7 +5928,7 @@ Latency: %2 ms Track - Muted + Mute diff --git a/data/locale/zh.ts b/data/locale/zh.ts index 07c696fb2..edb1afd95 100644 --- a/data/locale/zh.ts +++ b/data/locale/zh.ts @@ -55,15 +55,15 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com LMMS - LMMS + LMMS Involved - + 参与者 Contributors ordered by number of commits: - + 贡献者名单(以提交次数排序): @@ -151,7 +151,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com Amplify: - 放大: + 放大: With this knob you can set the amplify ratio. When you set a value of 100% your sample isn't changed. Otherwise it will be amplified up or down (your actual sample-file isn't touched!) @@ -203,7 +203,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com Loopback point: - + 循环点: With this knob you can set the point where the loop starts. @@ -237,11 +237,11 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com CLIENT-NAME - 客户端名称 + 客户端名称 CHANNELS - 声道数 + 声道数 @@ -335,7 +335,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com AutomationEditor Please open an automation pattern with the context menu of a control! - + 请使用控制的上下文菜单打开一个自动控制样式! Values copied @@ -350,35 +350,35 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com AutomationEditorWindow Play/pause current pattern (Space) - 播放/暂停当前片段(空格) + 播放/暂停当前片段(空格) Click here if you want to play the current pattern. This is useful while editing it. The pattern is automatically looped when the end is reached. - 点击这里播放片段。编辑时很有用,片段会自动循环播放。 + 点击这里播放片段。编辑时很有用,片段会自动循环播放。 Stop playing of current pattern (Space) - 停止当前片段(空格) + 停止当前片段(空格) Click here if you want to stop playing of the current pattern. - 点击这里停止播放片段。 + 点击这里停止播放片段。 Draw mode (Shift+D) - 绘制模式 (Shift+D) + 绘制模式 (Shift+D) Erase mode (Shift+E) - 擦除模式 (Shift+E) + 擦除模式 (Shift+E) Flip vertically - + 垂直翻转 Flip horizontally - + 水平翻转 Click here and the pattern will be inverted.The points are flipped in the y direction. @@ -530,31 +530,31 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com BBEditor Beat+Bassline Editor - 节拍+低音线编辑器 + 节拍+Bassline编辑器 Play/pause current beat/bassline (Space) - 播放/暂停当前节拍/低音线(空格) + 播放/暂停当前节拍/Bassline(空格) Stop playback of current beat/bassline (Space) - 停止播放当前节拍/低音线(空格) + 停止播放当前节拍/Bassline(空格) Click here to play the current beat/bassline. The beat/bassline is automatically looped when its end is reached. - 点击这里停止播放当前节拍/低音线。当结束时节拍/低音线会自动循环播放。 + 点击这里停止播放当前节拍/Bassline。当结束时节拍/Bassline会自动循环播放。 Click here to stop playing of current beat/bassline. - 点击这里停止播发当前节拍/低音线。 + 点击这里停止播发当前节拍/Bassline。 Add beat/bassline - 添加节拍/低音线 + 添加节拍/Bassline Add automation-track - + 添加自动控制轨道 Remove steps @@ -569,7 +569,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com BBTCOView Open in Beat+Bassline-Editor - 在节拍+低音线编辑器中打开 + 在节拍+Bassline编辑器中打开 Reset name @@ -592,7 +592,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com BBTrack Beat/Bassline %1 - 节拍/低音线 %1 + 节拍/Bassline %1 Clone of %1 @@ -728,11 +728,11 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com CaptionMenu &Help - + 帮助(&H) Help (not available) - + 帮助(不可用) @@ -1132,26 +1132,26 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com DummyEffect NOT FOUND - + 未找到 Editor Play (Space) - + 播放(空格) Stop (Space) - + 停止(空格) Record - + 录音 Record while playing - + 播放时录音 @@ -1344,7 +1344,7 @@ Right clicking will bring up a context menu where you can change the order in wh EnvelopeAndLfoView DEL - + DEL Predelay: @@ -3177,7 +3177,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri Please enter a new value between %1 and %2: - + 请输入一个介于%1和%2之间的数值: @@ -3224,7 +3224,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri LcdSpinBox Please enter a new value between %1 and %2: - + 请输入一个介于%1和%2之间的数值: @@ -3298,7 +3298,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri Modulation amount: - 调制量 + 调制量: Use this knob for setting modulation amount of the LFO. The bigger this value, the more the connected control (e.g. volume or cutoff-frequency) will be influenced by the LFO. @@ -3443,7 +3443,7 @@ Please make sure you have write-access to the file and try again. 打开已有工程 - Recently opened project + Recently opened projects 最近打开的工程 @@ -3557,12 +3557,12 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS. 重做 - LMMS Project + LMMS Project LMMS 工程 - LMMS Project Template - LMMS 工程模板 + LMMS Project Template + LMMS 工程模板 Volumes @@ -4399,15 +4399,15 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator Step length: - + 步进长度: Dry - + 干声 Dry Gain: - + 干声增益: Stages @@ -4674,11 +4674,11 @@ use mouse wheel to set volume of a step Modulation amount: - 调制量 + 调制量: Attack: - 打进声 + 打进声: Release: @@ -5654,7 +5654,7 @@ Latency: %2 ms untitled - 有标题 + 未标题 Select file for project-export... @@ -5662,7 +5662,7 @@ Latency: %2 ms The following errors occured while loading: - + 载入时发生以下错误: @@ -5742,51 +5742,51 @@ Latency: %2 ms SongEditorWindow Song-Editor - 歌曲编辑器 + 歌曲编辑器 Play song (Space) - 播放歌曲(空格) + 播放歌曲(空格) Record samples from Audio-device - 从音频设备录制样本 + 从音频设备录制样本 Record samples from Audio-device while playing song or BB track - 在播放歌曲或BB轨道时从音频设备录入样本 + 在播放歌曲或BB轨道时从音频设备录入样本 Stop song (Space) - 停止歌曲(空格) + 停止歌曲(空格) Add beat/bassline - 添加节拍/低音线 + 添加节拍/Bassline Add sample-track - 添加采样轨道 + 添加采样轨道 Add automation-track - + 添加自动控制轨道 Draw mode - 绘制模式 + 绘制模式 Edit mode (select and move) - 编辑模式(选定和移动) + 编辑模式(选定和移动) Click here, if you want to play your whole song. Playing will be started at the song-position-marker (green). You can also move it while playing. - 点击这里完整播放歌曲。将从绿色歌曲标记开始播放。在播放的同时可以对它进行移动。 + 点击这里完整播放歌曲。将从绿色歌曲标记开始播放。在播放的同时可以对它进行移动。 Click here, if you want to stop playing of your song. The song-position-marker will be set to the start of your song. - 点击这里停止播放,歌曲位置标记会跳到歌曲的开头。 + 点击这里停止播放,歌曲位置标记会跳到歌曲的开头。 @@ -5942,7 +5942,7 @@ Latency: %2 ms Track - Muted + Mute 静音 @@ -6250,7 +6250,7 @@ Please make sure you have read-permission to the file and the directory containi VestigeInstrumentView Open other VST-plugin - + 打开其他的VST插件 Click here, if you want to open another VST-plugin. After clicking on this button, a file-open-dialog appears and you can select your file. @@ -6258,19 +6258,19 @@ Please make sure you have read-permission to the file and the directory containi Show/hide GUI - + 显示/隐藏界面 Click here to show or hide the graphical user interface (GUI) of your VST-plugin. - + 点此显示/隐藏VST插件的界面。 Turn off all notes - + 全部静音 Open VST-plugin - + 打开VST插件 DLL-files (*.dll) @@ -6282,7 +6282,7 @@ Please make sure you have read-permission to the file and the directory containi No VST-plugin loaded - + 未载入VST插件 Control VST-plugin from LMMS host @@ -6326,7 +6326,7 @@ Please make sure you have read-permission to the file and the directory containi Preset - + 预置 by @@ -6334,7 +6334,7 @@ Please make sure you have read-permission to the file and the directory containi - VST plugin control - + - VST插件控制 @@ -6352,7 +6352,7 @@ Please make sure you have read-permission to the file and the directory containi VstEffectControlDialog Show/hide - + 显示/隐藏 Control VST-plugin from LMMS host @@ -6400,7 +6400,7 @@ Please make sure you have read-permission to the file and the directory containi &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /> - + @@ -6411,15 +6411,15 @@ Please make sure you have read-permission to the file and the directory containi Open Preset - + 打开预置 Vst Plugin Preset (*.fxp *.fxb) - + VST插件预置文件(*.fxp *.fxb) : default - + : 默认 " @@ -6431,7 +6431,7 @@ Please make sure you have read-permission to the file and the directory containi Save Preset - + 保存预置 .fxp @@ -6451,11 +6451,11 @@ Please make sure you have read-permission to the file and the directory containi Please wait while loading VST plugin... - + 正在载入VST插件,请稍候…… The VST plugin %1 could not be loaded. - + 无法载入VST插件 %1。 @@ -8565,7 +8565,7 @@ This chip was used in the Commodore 64 computer. Patch - + 音色 Gain @@ -8613,7 +8613,7 @@ This chip was used in the Commodore 64 computer. A soundfont %1 could not be loaded. - + 无法载入Soundfont %1。 diff --git a/data/themes/default/add_folder.png b/data/themes/default/add_folder.png new file mode 100644 index 000000000..710c7e80e Binary files /dev/null and b/data/themes/default/add_folder.png differ diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index b388f727a..a52ba52be 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -45,7 +45,11 @@ public: #ifdef LMMS_HAVE_PORTAUDIO +#if defined(__FreeBSD__) +#include +#else #include +#endif #include "AudioDevice.h" diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 57c2a6f0f..9f53c9451 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -255,7 +255,7 @@ public: // has to be accessed by more than one object, then this function shouldn't be used. bool isValueChanged() { - if( m_valueChanged ) + if( m_valueChanged || valueBuffer() ) { m_valueChanged = false; return true; diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 5257cecab..58127d02c 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -56,7 +56,7 @@ public: AutomationPattern( const AutomationPattern & _pat_to_copy ); virtual ~AutomationPattern(); - void addObject( AutomatableModel * _obj, bool _search_dup = true ); + bool addObject( AutomatableModel * _obj, bool _search_dup = true ); const AutomatableModel * firstObject() const; diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 12668e8a1..9e2b17259 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -41,8 +41,12 @@ class Engine; const QString PROJECTS_PATH = "projects/"; +const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; const QString SAMPLES_PATH = "samples/"; +const QString GIG_PATH = "samples/gig/"; +const QString SF2_PATH = "samples/sf2/"; +const QString LADSPA_PATH ="plugins/ladspa/"; const QString DEFAULT_THEME_PATH = "themes/default/"; const QString TRACK_ICON_PATH = "track_icons/"; const QString LOCALE_PATH = "locale/"; @@ -76,6 +80,11 @@ public: return workingDir() + PROJECTS_PATH; } + QString userTemplateDir() const + { + return workingDir() + TEMPLATE_PATH; + } + QString userPresetsDir() const { return workingDir() + PRESETS_PATH; @@ -86,6 +95,26 @@ public: return workingDir() + SAMPLES_PATH; } + QString userGigDir() const + { + return workingDir() + GIG_PATH; + } + + QString userSf2Dir() const + { + return workingDir() + SF2_PATH; + } + + QString userLadspaDir() const + { + return workingDir() + LADSPA_PATH; + } + + QString userVstDir() const + { + return m_vstDir; + } + QString factoryProjectsDir() const { return dataDir() + PROJECTS_PATH; @@ -126,6 +155,16 @@ public: return m_dataDir + LOCALE_PATH; } + const QString & gigDir() const + { + return m_gigDir; + } + + const QString & sf2Dir() const + { + return m_sf2Dir; + } + const QString & vstDir() const { return m_vstDir; @@ -195,6 +234,8 @@ public: void setSTKDir( const QString & _fd ); void setDefaultSoundfont( const QString & _sf ); void setBackgroundArtwork( const QString & _ba ); + void setGIGDir( const QString & gd ); + void setSF2Dir( const QString & sfd ); private: @@ -214,6 +255,8 @@ private: QString m_vstDir; QString m_flDir; QString m_ladDir; + QString m_gigDir; + QString m_sf2Dir; QString m_version; #ifdef LMMS_HAVE_STK QString m_stkDir; diff --git a/include/Engine.h b/include/Engine.h index 1e87f336e..2d61dceac 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -27,6 +27,9 @@ #define ENGINE_H #include +#include +#include + #include "export.h" @@ -39,8 +42,9 @@ class Song; class Ladspa2LMMS; -class EXPORT Engine +class EXPORT Engine : public QObject { + Q_OBJECT public: static void init(); static void destroy(); @@ -91,6 +95,19 @@ public: } static void updateFramesPerTick(); + static inline Engine * inst() + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new Engine(); + } + return s_instanceOfMe; + } + +signals: + void initProgress(const QString &msg); + + private: // small helper function which sets the pointer to NULL before actually deleting // the object it refers to @@ -114,6 +131,9 @@ private: static Ladspa2LMMS * s_ladspaManager; + // even though most methods are static, an instance is needed for Qt slots/signals + static Engine * s_instanceOfMe; + friend class GuiApplication; }; diff --git a/include/Fader.h b/include/Fader.h index 3e808802a..d21b7fa31 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -100,14 +100,12 @@ private: int knobPosY() const { - float fRange = m_model->maxValue() - m_model->minValue(); - float realVal = m_model->value() - m_model->minValue(); + float fRange = model()->maxValue() - model()->minValue(); + float realVal = model()->value() - model()->minValue(); return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } - FloatModel * m_model; - void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ); int calculateDisplayPeak( float fPeak ); diff --git a/include/FileBrowser.h b/include/FileBrowser.h index f9bc34820..b2d1e83bf 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -58,6 +58,8 @@ public slots: void filterItems( const QString & filter ); void reloadTree( void ); +private slots: + void giveFocusToFilter(); private: bool filterItems( QTreeWidgetItem * item, const QString & filter ); diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 66a9143b4..08d24fd09 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -53,6 +53,8 @@ public: public: FxChannelView(QWidget * _parent, FxMixerView * _mv, int _chIndex ); + void setChannelIndex( int index ); + FxLine * m_fxLine; PixmapButton * m_muteBtn; PixmapButton * m_soloBtn; @@ -97,6 +99,7 @@ public: // move the channel to the left or right void moveChannelLeft(int index); + void moveChannelLeft(int index, int focusIndex); void moveChannelRight(int index); // make sure the display syncs up with the fx mixer. diff --git a/include/GuiApplication.h b/include/GuiApplication.h index c35994e81..40fb4578d 100644 --- a/include/GuiApplication.h +++ b/include/GuiApplication.h @@ -25,8 +25,12 @@ #ifndef GUIAPPLICATION_H #define GUIAPPLICATION_H +#include + #include "export.h" +class QLabel; + class AutomationEditorWindow; class BBEditor; class ControllerRackView; @@ -36,8 +40,9 @@ class PianoRollWindow; class ProjectNotes; class SongEditorWindow; -class EXPORT GuiApplication +class EXPORT GuiApplication : public QObject { + Q_OBJECT; public: explicit GuiApplication(); ~GuiApplication(); @@ -53,6 +58,9 @@ public: AutomationEditorWindow* automationEditor() { return m_automationEditor; } ControllerRackView* getControllerRackView() { return m_controllerRackView; } +public slots: + void displayInitProgress(const QString &msg); + private: static GuiApplication* s_instance; @@ -64,6 +72,7 @@ private: PianoRollWindow* m_pianoRoll; ProjectNotes* m_projectNotes; ControllerRackView* m_controllerRackView; + QLabel* m_loadingProgressLabel; }; #define gui GuiApplication::instance() diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index c8bcf67a0..ffbfbce73 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -84,7 +84,6 @@ public: private: Instrument* m_instrument; - InstrumentTrack * m_instrumentTrack; } ; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 8c8c963fa..6475a8622 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -52,6 +52,7 @@ class InstrumentMidiIOView; class InstrumentMiscView; class Knob; class LcdSpinBox; +class LeftRightNav; class midiPortMenu; class DataFile; class PluginView; @@ -416,16 +417,19 @@ protected: protected slots: void saveSettingsBtnClicked(); - + void viewNextInstrument(); + void viewPrevInstrument(); private: virtual void modelChanged(); + void viewInstrumentInDirection(int d); InstrumentTrack * m_track; InstrumentTrackView * m_itv; // widgets on the top of an instrument-track-window QLineEdit * m_nameLineEdit; + LeftRightNav * m_leftRightNav; Knob * m_volumeKnob; Knob * m_panningKnob; Knob * m_pitchKnob; diff --git a/include/LeftRightNav.h b/include/LeftRightNav.h new file mode 100644 index 000000000..b675446ca --- /dev/null +++ b/include/LeftRightNav.h @@ -0,0 +1,49 @@ +/* + * LeftRightNav.cpp - side-by-side left-facing and right-facing arrows for navigation (looks like < > ) + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://lmms.io + * + * 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 LEFT_RIGHT_NAV_H +#define LEFT_RIGHT_NAV_H + +#include "PixmapButton.h" + +#include + +class LeftRightNav : public QWidget +{ + Q_OBJECT +public: + LeftRightNav(QWidget *parent=NULL); + PixmapButton* getLeftBtn(); + PixmapButton* getRightBtn(); + void setShortcuts(const QKeySequence &leftShortcut=Qt::Key_Minus, const QKeySequence &rightShortcut=Qt::Key_Plus); +signals: + void onNavLeft(); + void onNavRight(); +private: + QHBoxLayout m_layout; + PixmapButton m_leftBtn; + PixmapButton m_rightBtn; +}; + +#endif \ No newline at end of file diff --git a/include/MainWindow.h b/include/MainWindow.h index 5e1f294ae..8ab93d34d 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -121,6 +121,7 @@ public slots: void updatePlayPauseIcons(); + void updateUndoRedoButtons(); void undo(); void redo(); @@ -167,6 +168,8 @@ private: } m_keyMods; QMenu * m_toolsMenu; + QAction * m_undoAction; + QAction * m_redoAction; QList m_tools; QBasicTimer m_updateTimer; @@ -190,6 +193,7 @@ private slots: signals: void periodicUpdate(); + void initProgress(const QString &msg); } ; diff --git a/include/Mixer.h b/include/Mixer.h index f8f9cf313..03a815975 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -25,13 +25,7 @@ #ifndef MIXER_H #define MIXER_H -// denormals stripping -#ifdef __SSE__ -#include -#endif -#ifdef __SSE3__ -#include -#endif +#include "denormals.h" #include "lmmsconfig.h" diff --git a/include/PianoRoll.h b/include/PianoRoll.h index f4f313174..9deeeb9e1 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -384,6 +384,8 @@ signals: void currentPatternChanged(); private: + void focusInEvent(QFocusEvent * event); + PianoRoll* m_editor; ComboBox * m_zoomingComboBox; diff --git a/include/PixmapButton.h b/include/PixmapButton.h index 7e02c4047..04a7a7f5b 100644 --- a/include/PixmapButton.h +++ b/include/PixmapButton.h @@ -42,6 +42,7 @@ public: void setActiveGraphic( const QPixmap & _pm ); void setInactiveGraphic( const QPixmap & _pm, bool _update = true ); + QSize sizeHint() const; signals: void doubleClicked(); diff --git a/include/ProjectJournal.h b/include/ProjectJournal.h index 5cb7920f6..28beaa82f 100644 --- a/include/ProjectJournal.h +++ b/include/ProjectJournal.h @@ -45,6 +45,9 @@ public: void undo(); void redo(); + bool canUndo() const; + bool canRedo() const; + void addJournalCheckPoint( JournallingObject *jo ); bool isJournalling() const diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 8b5abfeb7..3357bf54c 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -1,3 +1,4 @@ + /* * SetupDialog.h - dialog for setting up LMMS * @@ -71,6 +72,8 @@ private slots: // path settings widget void setWorkingDir( const QString & _wd ); void setVSTDir( const QString & _vd ); + void setGIGDir( const QString & _gd ); + void setSF2Dir( const QString & _sfd ); void setArtworkDir( const QString & _ad ); void setFLDir( const QString & _fd ); void setLADSPADir( const QString & _ld ); @@ -96,6 +99,8 @@ private slots: void openWorkingDir(); void openVSTDir(); + void openGIGDir(); + void openSF2Dir(); void openArtworkDir(); void openFLDir(); void openLADSPADir(); @@ -138,6 +143,8 @@ private: QLineEdit * m_adLineEdit; QLineEdit * m_fdLineEdit; QLineEdit * m_ladLineEdit; + QLineEdit * m_gigLineEdit; + QLineEdit * m_sf2LineEdit; #ifdef LMMS_HAVE_FLUIDSYNTH QLineEdit * m_sfLineEdit; #endif @@ -151,6 +158,8 @@ private: QString m_artworkDir; QString m_flDir; QString m_ladDir; + QString m_gigDir; + QString m_sf2Dir; #ifdef LMMS_HAVE_FLUIDSYNTH QString m_defaultSoundfont; #endif diff --git a/include/Song.h b/include/Song.h index 053e16345..3f52cbbb1 100644 --- a/include/Song.h +++ b/include/Song.h @@ -57,7 +57,6 @@ public: { Mode_None, Mode_PlaySong, - Mode_PlayTrack, Mode_PlayBB, Mode_PlayPattern, Mode_PlayAutomationPattern, @@ -75,7 +74,6 @@ public: PlayPos( const int abs = 0 ) : MidiTime( abs ), m_timeLine( NULL ), - m_timeLineUpdate( true ), m_currentFrame( 0.0f ) { } @@ -88,7 +86,6 @@ public: return m_currentFrame; } TimeLineWidget * m_timeLine; - bool m_timeLineUpdate; private: float m_currentFrame; @@ -258,7 +255,6 @@ public slots: void playSong(); void record(); void playAndRecord(); - void playTrack( Track * trackToPlay ); void playBB(); void playPattern( const Pattern * patternToPlay, bool loop = true ); void togglePause(); @@ -355,7 +351,6 @@ private: PlayPos m_playPos[Mode_Count]; tact_t m_length; - Track * m_trackToPlay; const Pattern* m_patternToPlay; bool m_loopPattern; diff --git a/include/Track.h b/include/Track.h index 1bebffbf1..73db6017a 100644 --- a/include/Track.h +++ b/include/Track.h @@ -441,7 +441,7 @@ public: static Track * create( TrackTypes tt, TrackContainer * tc ); static Track * create( const QDomElement & element, TrackContainer * tc ); - void clone(); + Track * clone(); // pure virtual functions diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 42e56128f..4544d26b3 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -103,8 +103,15 @@ public: return m_tc; } - void moveTrackViewUp( TrackView * _tv ); - void moveTrackViewDown( TrackView * _tv ); + const QList & trackViews() const + { + return( m_trackViews ); + } + + void moveTrackView( TrackView * trackView, int indexTo ); + void moveTrackViewUp( TrackView * trackView ); + void moveTrackViewDown( TrackView * trackView ); + void scrollToTrackView( TrackView * _tv ); // -- for usage by trackView only --------------- TrackView * addTrackView( TrackView * _tv ); @@ -121,7 +128,7 @@ public: public slots: void realignTracks(); - void createTrackView( Track * _t ); + TrackView * createTrackView( Track * _t ); void deleteTrackView( TrackView * _tv ); virtual void dropEvent( QDropEvent * _de ); @@ -141,11 +148,6 @@ public slots: protected: static const int DEFAULT_PIXELS_PER_TACT = 16; - const QList & trackViews() const - { - return( m_trackViews ); - } - virtual void mousePressEvent( QMouseEvent * _me ); virtual void mouseMoveEvent( QMouseEvent * _me ); virtual void mouseReleaseEvent( QMouseEvent * _me ); diff --git a/include/denormals.h b/include/denormals.h new file mode 100644 index 000000000..f89325ac1 --- /dev/null +++ b/include/denormals.h @@ -0,0 +1,32 @@ +// Denormals stripping. +// These snippets should be common enough to be considered public domain. + +#ifndef DENORMALS_H +#define DENORMALS_H + + +#ifdef __SSE__ +#include +#endif +#ifdef __SSE3__ +#include +#endif + + +// Set denormal protection for this thread. +// To be on the safe side, don't set the DAZ flag for SSE2 builds, +// even if most SSE2 CPUs can handle it. +void inline disable_denormals() { +#ifdef __SSE3__ + /* DAZ flag */ + _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); +#endif + +#ifdef __SSE__ + /* FTZ flag */ + _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); +#endif +} + +#endif + diff --git a/include/lmms_math.h b/include/lmms_math.h index 1e3b530ae..b4d8995a0 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -34,7 +34,7 @@ #include using namespace std; -#if defined (LMMS_BUILD_WIN32) || defined (LMMS_BUILD_APPLE) || defined(LMMS_BUILD_HAIKU) +#if defined (LMMS_BUILD_WIN32) || defined (LMMS_BUILD_APPLE) || defined(LMMS_BUILD_HAIKU) || defined (__FreeBSD__) #ifndef isnanf #define isnanf(x) isnan(x) #endif diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3d1c9f2ee..86e53b01b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,6 +1,10 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES( + ${SAMPLERATE_INCLUDE_DIRS} +) + ADD_SUBDIRECTORY(Amplifier) ADD_SUBDIRECTORY(audio_file_processor) ADD_SUBDIRECTORY(BassBooster) diff --git a/plugins/Delay/DelayControls.cpp b/plugins/Delay/DelayControls.cpp index f8241b95e..15ccdb705 100644 --- a/plugins/Delay/DelayControls.cpp +++ b/plugins/Delay/DelayControls.cpp @@ -32,10 +32,10 @@ DelayControls::DelayControls( DelayEffect* effect ): EffectControls( effect ), m_effect ( effect ), - m_delayTimeModel( 0.5, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Delay Samples" )) , + m_delayTimeModel( 0.5, 0.01, 5.0, 0.0001, 20000.0, this, tr( "Delay Samples" )) , m_feedbackModel(0.0f,0.0f,1.0f,0.01f,this,tr( "Feedback" ) ), - m_lfoTimeModel(2.0, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Lfo Frequency" ) ), - m_lfoAmountModel(0.0, 0.0, 2.0, 0.0001, 2000.0, this, tr ( "Lfo Amount" ) ), + m_lfoTimeModel(2.0, 0.01, 5.0, 0.0001, 20000.0, this, tr( "Lfo Frequency" ) ), + m_lfoAmountModel(0.0, 0.0, 0.5, 0.0001, 2000.0, this, tr ( "Lfo Amount" ) ), m_outGainModel( 0.0, -60.0, 20.0, 0.01, this, tr( "Output gain" ) ) { connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changeSampleRate() ) ); diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 7f41653a2..af45c2bc9 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -118,7 +118,7 @@ bool DelayEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) m_delay->setFeedback( *feedbackPtr ); m_lfo->setFrequency( *lfoTimePtr ); sampleLength = *lengthPtr * Engine::mixer()->processingSampleRate(); - m_currentLength = linearInterpolate( sampleLength, m_currentLength, 0.9999 ); + m_currentLength = sampleLength; m_delay->setLength( m_currentLength + ( *amplitudePtr * ( float )m_lfo->tick() ) ); m_delay->tick( buf[f] ); diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index 21e637740..2093627a9 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -57,24 +57,15 @@ StereoDelay::~StereoDelay() void StereoDelay::tick( sampleFrame frame ) { - m_buffer[m_index][0] = frame[0]; - m_buffer[m_index][1] = frame[1]; - - int readIndex = m_index - ( int )m_length - 1; - if( readIndex < 0 ) - { - readIndex += m_maxLength; - } - float fract = 1.0f - fraction( m_length ); - frame[0] = linearInterpolate( m_buffer[readIndex][0] , - m_buffer[( readIndex+1) % m_maxLength][0], fract ); - frame[1] = linearInterpolate( m_buffer[readIndex][1] , - m_buffer[( readIndex+1) % m_maxLength][1], fract ); - - m_buffer[m_index][0] += frame[0] * m_feedback; - m_buffer[m_index][1] += frame[1] * m_feedback; - - m_index = ( m_index + 1) % (int) m_maxLength; + m_index = ( int )m_length > 0 + ? ( m_index + 1 ) % ( int ) m_length + : m_index; + float lOut = m_buffer[ m_index ][ 0 ]; + float rOut = m_buffer[ m_index ] [1 ]; + m_buffer[ m_index ][ 0 ] = frame[ 0 ] + ( lOut * m_feedback ); + m_buffer[ m_index ][ 1 ] = frame[ 1 ] + ( rOut * m_feedback ); + frame[ 0 ] = lOut; + frame[ 1 ] = rOut; } diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index c78d68a29..b7874ed38 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -153,7 +153,7 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames if( ( ( *cut1Ptr != m_currentCut1 || *res1Ptr != m_currentRes1 ) ) || m_filter1changed ) { - m_filter1->calcFilterCoeffs( *cut1Ptr, *res2Ptr ); + m_filter1->calcFilterCoeffs( *cut1Ptr, *res1Ptr ); m_filter1changed = false; m_currentCut1 = *cut1Ptr; m_currentRes1 = *res1Ptr; diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp index 76e5b4a95..d1fd747a2 100644 --- a/plugins/Eq/EqControls.cpp +++ b/plugins/Eq/EqControls.cpp @@ -56,14 +56,14 @@ EqControls::EqControls( EqEffect *effect ) : m_para4FreqModel( 4000.0, 27.0, 20000, 0.001, this , tr( "Peak 4 freq" ) ), m_highShelfFreqModel( 12000.0, 27.0, 20000, 0.001, this , tr( "High shelf freq" ) ), m_lpFreqModel( 18000.0, 27.0, 20000, 0.001, this , tr( "LP freq" ) ), - m_hpActiveModel( false, this , tr( "HP active" ) ), - m_lowShelfActiveModel( false, this , tr( "Low shelf active" ) ), - m_para1ActiveModel(false, this , tr( "Peak 1 active" ) ), - m_para2ActiveModel( false, this , tr( "Peak 2 active" ) ), - m_para3ActiveModel( false, this , tr( "Peak 3 active" ) ), - m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), - m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), - m_lpActiveModel( false, this , tr( "LP active" ) ), + m_hpActiveModel( true, this , tr( "HP active" ) ), + m_lowShelfActiveModel( true, this , tr( "Low shelf active" ) ), + m_para1ActiveModel(true, this , tr( "Peak 1 active" ) ), + m_para2ActiveModel( true, this , tr( "Peak 2 active" ) ), + m_para3ActiveModel( true, this , tr( "Peak 3 active" ) ), + m_para4ActiveModel( true, this , tr( "Peak 4 active" ) ), + m_highShelfActiveModel( true, this , tr( "High shelf active" ) ), + m_lpActiveModel( true, this , tr( "LP active" ) ), m_lp12Model( false, this , tr( "LP 12" ) ), m_lp24Model( false, this , tr( "LP 24" ) ), m_lp48Model( false, this , tr( "LP 48" ) ), diff --git a/plugins/Flanger/MonoDelay.cpp b/plugins/Flanger/MonoDelay.cpp index 9afc3ee17..9342d49c5 100644 --- a/plugins/Flanger/MonoDelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -25,6 +25,7 @@ #include "MonoDelay.h" #include "interpolation.h" #include "lmms_math.h" +#include "string.h" MonoDelay::MonoDelay( int maxTime , int sampleRate ) { @@ -51,27 +52,14 @@ MonoDelay::~MonoDelay() - void MonoDelay::tick( sample_t* sample ) { - m_buffer[m_index] = *sample; - int readIndex = m_index - ( int )m_length - 1; - if(readIndex < 0) - { - readIndex += m_maxLength; - } - float fract = 1.0f - fraction( m_length ); - if(readIndex != m_maxLength-1 ) - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[readIndex+1], fract ); - } else - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[0], fract ); - } - m_buffer[m_index] += *sample * m_feedback; - m_index = ( m_index +1 ) % m_maxLength; + m_index = ( int )m_length > 0 + ? ( m_index + 1 ) % ( int )m_length + : m_index; + float out = m_buffer[ m_index ]; + m_buffer[ m_index ] = *sample + ( out * m_feedback ); + *sample = out; } @@ -85,5 +73,6 @@ void MonoDelay::setSampleRate( int sampleRate ) } - m_buffer = new sample_t[( int )( sampleRate * m_maxTime )]; + m_buffer = new sample_t[( int )( sampleRate * m_maxTime ) ]; + memset( m_buffer, 0, sizeof(float) * ( int )( sampleRate * m_maxTime ) ); } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 8d17b1f2e..22d539a63 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -1069,7 +1069,7 @@ void GigInstrumentView::showFileDialog() if( QFileInfo( f ).isRelative() ) { - f = ConfigManager::inst()->userSamplesDir() + f; + f = ConfigManager::inst()->gigDir() + f; if( QFileInfo( f ).exists() == false ) { @@ -1082,7 +1082,7 @@ void GigInstrumentView::showFileDialog() } else { - ofd.setDirectory( ConfigManager::inst()->userSamplesDir() ); + ofd.setDirectory( ConfigManager::inst()->gigDir() ); } m_fileDialogButton->setEnabled( false ); diff --git a/plugins/LadspaEffect/swh/mbeq_1197.c b/plugins/LadspaEffect/swh/mbeq_1197.c index e1c88d33e..c10da5919 100644 --- a/plugins/LadspaEffect/swh/mbeq_1197.c +++ b/plugins/LadspaEffect/swh/mbeq_1197.c @@ -286,11 +286,10 @@ static LADSPA_Handle instantiateMbeq( // Create raised cosine window table for (i=0; i < FFT_LENGTH; i++) { - window[i] = -0.5f*cos(2.0f*M_PI*(double)i/(double)FFT_LENGTH)+0.5f; - window[i] *= 2.0f; + window[i] = -0.5f * cos(2.0f*M_PI*(double)i/(double)FFT_LENGTH) + 0.5f; } - // Create db->coeffiecnt lookup table + // Create db->coefficient lookup table db_table = malloc(1000 * sizeof(float)); for (i=0; i < 1000; i++) { db = ((float)i/10) - 70; @@ -472,8 +471,12 @@ static void runMbeq(LADSPA_Handle instance, unsigned long sample_count) { // Window into the output accumulator for (i = 0; i < FFT_LENGTH; i++) { - out_accum[i] += 0.9186162f * window[i] * real[i]/(FFT_LENGTH * OVER_SAMP); + // correction factor for window measured from white noise + // reduce intermediate output by (number of coefficients) * OVER_SAMP + out_accum[i] += real[i] * window[i] * 1.27519f / + ((FFT_LENGTH/2) * OVER_SAMP); } + for (i = 0; i < step_size; i++) { out_fifo[i] = out_accum[i]; } diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 67e7ba3b4..a81b0e705 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -215,8 +215,9 @@ public: bool isSF2; bool hasNotes; MidiTime lastEnd; + QString trackName; - smfMidiChannel * create( TrackContainer* tc ) + smfMidiChannel * create( TrackContainer* tc, QString tn ) { if( !it ) { it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) ); @@ -238,7 +239,10 @@ public: #else it_inst = it->loadInstrument( "patman" ); #endif - + trackName = tn; + if( trackName != "") { + it->setName( tn ); + } lastEnd = 0; } return this; @@ -353,6 +357,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) // Tracks for( int t = 0; t < seq->tracks(); ++t ) { + QString trackName = ""; Alg_track_ptr trk = seq->track( t ); pd.setValue( t + preTrackSteps ); @@ -368,15 +373,34 @@ bool MidiImport::readSMF( TrackContainer* tc ) if( evt->chan == -1 ) { - printf("MISSING GLOBAL THINGY\n"); - printf(" %d %d %f %s\n", (int) evt->chan, - evt->get_type_code(), evt->time, - evt->get_attribute() ); - // Global stuff + bool handled = false; + if( evt->is_update() ) + { + QString attr = evt->get_attribute(); + if( attr == "tracknames" && evt->get_update_type() == 'a' ) { + trackName = evt->get_atom_value(); + handled = true; + } + } + if( !handled ) { + // Write debug output + printf("MISSING GLOBAL HANDLER\n"); + printf(" Chn: %d, Type Code: %d, Time: %f", (int) evt->chan, + evt->get_type_code(), evt->time ); + if ( evt->is_update() ) + { + printf( ", Update Type: %s", evt->get_attribute() ); + if ( evt->get_update_type() == 'a' ) + { + printf( ", Atom: %s", evt->get_atom_value() ); + } + } + printf( "\n" ); + } } else if( evt->is_note() && evt->chan < 256 ) { - smfMidiChannel * ch = chs[evt->chan].create( tc ); + smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); Alg_note_ptr noteEvt = dynamic_cast( evt ); Note n( noteEvt->get_duration() * ticksPerBeat, @@ -389,7 +413,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) else if( evt->is_update() ) { - smfMidiChannel * ch = chs[evt->chan].create( tc ); + smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); double time = evt->time*ticksPerBeat; QString update( evt->get_attribute() ); @@ -415,12 +439,6 @@ bool MidiImport::readSMF( TrackContainer* tc ) } } } - else if( update == "tracknames" ) - { - QString trackName( evt->get_string_value() ); - ch->it->setName( trackName ); - //ch.p->setName( trackName ); - } else if( update.startsWith( "control" ) || update == "bendr" ) { diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index be7519179..7225a28de 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -716,7 +716,7 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * ) void AudioFileProcessorView::sampleUpdated( void ) { - newWaveView(); + m_waveView->updateSampleRange(); m_waveView->update(); update(); } @@ -733,7 +733,7 @@ void AudioFileProcessorView::openAudioFile( void ) { castModel()->setAudioFile( af ); Engine::getSong()->setModified(); - newWaveView(); + m_waveView->updateSampleRange(); } } @@ -759,6 +759,16 @@ void AudioFileProcessorView::modelChanged( void ) +void AudioFileProcessorWaveView::updateSampleRange() +{ + if( m_sampleBuffer.frames() > 1 ) + { + const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1; + m_from = qMax( 0, m_sampleBuffer.startFrame() - marging ); + m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() ); + } +} + AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, SampleBuffer& buf ) : QWidget( _parent ), m_sampleBuffer( buf ), @@ -779,12 +789,7 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ setFixedSize( _w, _h ); setMouseTracking( true ); - if( m_sampleBuffer.frames() > 1 ) - { - const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1; - m_from = qMax( 0, m_sampleBuffer.startFrame() - marging ); - m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() ); - } + updateSampleRange(); m_graph.fill( Qt::transparent ); update(); diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index 2e7a2b3a5..988cc5382 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -261,6 +261,7 @@ public: void setKnobs(knob *_start, knob *_end, knob *_loop ); + void updateSampleRange(); private: void zoom( const bool _out = false ); void slide( int _px ); diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index 0831bba50..de65aa1e3 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -263,6 +263,9 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: handleUiClosed(); break; + case NATIVE_HOST_OPCODE_HOST_IDLE: + qApp->processEvents(); + break; } return ret; diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index b84edf0b9..8e95d83a6 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -141,9 +141,6 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : vib_depth_mdl(false, this, tr( "Vibrato Depth" ) ), trem_depth_mdl(false, this, tr( "Tremolo Depth" ) ) { - // Connect the plugin to the mixer... - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); - Engine::mixer()->addPlayHandle( iph ); // Create an emulator - samplerate, 16 bit, mono emulatorMutex.lock(); @@ -220,6 +217,10 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : MOD_CON( fm_mdl ); MOD_CON( vib_depth_mdl ); MOD_CON( trem_depth_mdl ); + + // Connect the plugin to the mixer... + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); + Engine::mixer()->addPlayHandle( iph ); } opl2instrument::~opl2instrument() { diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index f2acd2f07..96ce38b99 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -118,9 +118,6 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : // everytime we load a new soundfont. m_synth = new_fluid_synth( m_settings ); - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); - Engine::mixer()->addPlayHandle( iph ); - loadFile( ConfigManager::inst()->defaultSoundfont() ); updateSampleRate(); @@ -152,6 +149,8 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : connect( &m_chorusSpeed, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) ); connect( &m_chorusDepth, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); + Engine::mixer()->addPlayHandle( iph ); } @@ -928,7 +927,7 @@ sf2InstrumentView::sf2InstrumentView( Instrument * _instrument, QWidget * _paren m_chorusButton->move( 14, 226 ); m_chorusButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "chorus_on" ) ); m_chorusButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "chorus_off" ) ); - ToolTip::add( m_reverbButton, tr( "Apply chorus (if supported)" ) ); + ToolTip::add( m_chorusButton, tr( "Apply chorus (if supported)" ) ); m_chorusButton->setWhatsThis( tr( "This button enables the chorus effect. " "This is useful for cool echo effects, but only works on " @@ -1068,7 +1067,7 @@ void sf2InstrumentView::showFileDialog() QString f = k->m_filename; if( QFileInfo( f ).isRelative() ) { - f = ConfigManager::inst()->userSamplesDir() + f; + f = ConfigManager::inst()->sf2Dir() + f; if( QFileInfo( f ).exists() == false ) { f = ConfigManager::inst()->factorySamplesDir() + k->m_filename; @@ -1079,7 +1078,7 @@ void sf2InstrumentView::showFileDialog() } else { - ofd.setDirectory( ConfigManager::inst()->userSamplesDir() ); + ofd.setDirectory( ConfigManager::inst()->sf2Dir() ); } m_fileDialogButton->setEnabled( false ); diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index d06356c3e..6d9b8a451 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -133,9 +133,10 @@ void SfxrSynth::resetSample( bool restart ) env_vol=0.0f; env_stage=0; env_time=0; - env_length[0]=(int)(s->m_attModel.value()*s->m_attModel.value()*100000.0f); - env_length[1]=(int)(s->m_holdModel.value()*s->m_holdModel.value()*100000.0f); - env_length[2]=(int)(s->m_decModel.value()*s->m_decModel.value()*100000.0f); + + env_length[0]=(int)(s->m_attModel.value()*s->m_attModel.value()*99999.0f)+1; + env_length[1]=(int)(s->m_holdModel.value()*s->m_holdModel.value()*99999.0f)+1; + env_length[2]=(int)(s->m_decModel.value()*s->m_decModel.value()*99999.0f)+1; fphase=pow(s->m_phaserOffsetModel.value(), 2.0f)*1020.0f; if(s->m_phaserOffsetModel.value()<0.0f) fphase=-fphase; @@ -337,8 +338,8 @@ sfxrInstrument::sfxrInstrument( InstrumentTrack * _instrument_track ) : m_changeAmtModel(0.0f, this, "Change Amount"), m_changeSpeedModel(0.0f, this, "Change Speed"), - m_sqrDutyModel(0.0f, this, "Squre Duty"), - m_sqrSweepModel(0.0f, this, "Squre Sweep"), + m_sqrDutyModel(0.0f, this, "Square Duty"), + m_sqrSweepModel(0.0f, this, "Duty Sweep"), m_repeatSpeedModel(0.0f, this, "Repeat Speed"), @@ -633,8 +634,8 @@ sfxrInstrumentView::sfxrInstrumentView( Instrument * _instrument, m_changeAmtKnob ->setObjectName( "changeKnob" ); m_changeSpeedKnob ->setObjectName( "changeKnob" ); - createKnob(m_sqrDutyKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Squre Duty(Square wave only)"); - createKnob(m_sqrSweepKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Squre Sweep(Square wave only)"); + createKnob(m_sqrDutyKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Square Duty (Square wave only)"); + createKnob(m_sqrSweepKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Duty Sweep (Square wave only)"); m_sqrDutyKnob ->setObjectName( "sqrKnob" ); m_sqrSweepKnob ->setObjectName( "sqrKnob" ); diff --git a/plugins/sfxr/sfxr.h b/plugins/sfxr/sfxr.h index c16b993fa..4f75c8f16 100644 --- a/plugins/sfxr/sfxr.h +++ b/plugins/sfxr/sfxr.h @@ -263,8 +263,8 @@ private: Knob * m_changeAmtKnob; //Change Amount Knob * m_changeSpeedKnob; //Change Speed - Knob * m_sqrDutyKnob; //Squre Duty - Knob * m_sqrSweepKnob; //Squre Sweep + Knob * m_sqrDutyKnob; //Square Wave Duty + Knob * m_sqrSweepKnob; //Square Wave Duty Sweep Knob * m_repeatSpeedKnob; //Repeat Speed @@ -278,8 +278,8 @@ private: Knob * m_hpFilCutSweepKnob; //HP Filter Cutoff Sweep automatableButtonGroup * m_waveBtnGroup; - PixmapButton * m_sqrWaveBtn; //NOTE: This button has Squre Duty - //and Squre Speed configurable + PixmapButton * m_sqrWaveBtn; //NOTE: This button has Square Duty + //and Square Speed configurable PixmapButton * m_sawWaveBtn; PixmapButton * m_sinWaveBtn; PixmapButton * m_noiseWaveBtn; diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index 70d93d068..2818feffd 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -335,6 +335,16 @@ malletsInstrumentView::malletsInstrumentView( malletsInstrument * _instrument, m_spreadKnob->setLabel( tr( "Spread" ) ); m_spreadKnob->move( 190, 140 ); m_spreadKnob->setHintText( tr( "Spread:" ), "" ); + + // try to inform user about missing Stk-installation + if( _instrument->m_filesMissing && Engine::hasGUI() ) + { + QMessageBox::information( 0, tr( "Missing files" ), + tr( "Your Stk-installation seems to be " + "incomplete. Please make sure " + "the full Stk-package is installed!" ), + QMessageBox::Ok ); + } } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 26967fca1..11ef82793 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -5,13 +5,14 @@ INCLUDE(BuildPlugin) IF(LMMS_BUILD_WIN32) ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") + IF(QT5) TARGET_LINK_LIBRARIES(RemoteVstPlugin Qt5::Core) ELSE() TARGET_LINK_LIBRARIES(RemoteVstPlugin -lQtCore4) ENDIF() TARGET_LINK_LIBRARIES(RemoteVstPlugin -lpthread -lgdi32 -lws2_32) - SET_TARGET_PROPERTIES(RemoteVstPlugin PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O3") + SET_TARGET_PROPERTIES(RemoteVstPlugin PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O0") ADD_CUSTOM_COMMAND(TARGET RemoteVstPlugin POST_BUILD COMMAND "${STRIP}" "$") INSTALL(TARGETS RemoteVstPlugin RUNTIME DESTINATION "${PLUGIN_DIR}") diff --git a/plugins/vst_base/Win64/CMakeLists.txt b/plugins/vst_base/Win64/CMakeLists.txt index 270f8e68b..a815e9c5f 100644 --- a/plugins/vst_base/Win64/CMakeLists.txt +++ b/plugins/vst_base/Win64/CMakeLists.txt @@ -5,7 +5,7 @@ ADD_EXECUTABLE(RemoteVstPlugin32 "${CMAKE_CURRENT_SOURCE_DIR}/../RemoteVstPlugin TARGET_LINK_LIBRARIES(RemoteVstPlugin32 -lQtCore4 -lpthread -lgdi32 -lws2_32) ADD_CUSTOM_COMMAND(TARGET RemoteVstPlugin32 POST_BUILD COMMAND "${STRIP}" "$") -SET_TARGET_PROPERTIES(RemoteVstPlugin32 PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O3") +SET_TARGET_PROPERTIES(RemoteVstPlugin32 PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O0") INSTALL(TARGETS RemoteVstPlugin32 RUNTIME DESTINATION "${PLUGIN_DIR}/32") INSTALL(FILES "${MINGW_PREFIX32}/bin/QtCore4.dll" "${MINGW_PREFIX32}/bin/zlib1.dll" "${MINGW_PREFIX32}/${CMAKE_SYSTEM_PROCESSOR32}-w64-mingw32/bin/libwinpthread-1.dll" diff --git a/plugins/zynaddsubfx/CMakeLists.txt b/plugins/zynaddsubfx/CMakeLists.txt index b469351f8..227e32d99 100644 --- a/plugins/zynaddsubfx/CMakeLists.txt +++ b/plugins/zynaddsubfx/CMakeLists.txt @@ -147,7 +147,7 @@ ENDIF(LMMS_BUILD_WIN32) # FLTK needs X IF(LMMS_BUILD_LINUX) - TARGET_LINK_LIBRARIES(RemoteZynAddSubFx -ldl) + TARGET_LINK_LIBRARIES(RemoteZynAddSubFx ${CMAKE_DL_LIBS}) ENDIF(LMMS_BUILD_LINUX) diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index bdb0c6e1f..2a9f60224 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -350,14 +350,16 @@ bool ZynAddSubFxInstrument::handleMidiEvent( const MidiEvent& event, const MidiT return true; } + MidiEvent localEvent = event; + localEvent.setChannel( 0 ); m_pluginMutex.lock(); if( m_remotePlugin ) { - m_remotePlugin->processMidiEvent( event, 0 ); + m_remotePlugin->processMidiEvent( localEvent, 0 ); } else { - m_plugin->processMidiEvent( event ); + m_plugin->processMidiEvent( localEvent ); } m_pluginMutex.unlock(); diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index f14e6cb70..93934c2a2 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -31,7 +31,6 @@ #include "ProjectJournal.h" #include "BBTrackContainer.h" #include "Song.h" -#include "TextFloat.h" #include "embed.h" int AutomationPattern::s_quantization = 1; @@ -107,7 +106,7 @@ AutomationPattern::~AutomationPattern() -void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) +bool AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) { if( _search_dup ) { @@ -115,10 +114,8 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) it != m_objects.end(); ++it ) { if( *it == _obj ) - { - TextFloat::displayMessage( _obj->displayName(), tr( "Model is already connected " - "to this pattern." ), embed::getIconPixmap( "automation" ), 2000 ); - return; + { + return false; } } } @@ -138,6 +135,7 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) emit dataChanged(); + return true; } diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 81fb9022c..200767e5e 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -61,6 +61,8 @@ ConfigManager::ConfigManager() : m_artworkDir( defaultArtworkDir() ), m_vstDir( m_workingDir + "vst" + QDir::separator() ), m_flDir( QDir::home().absolutePath() ), + m_gigDir( m_workingDir + GIG_PATH ), + m_sf2Dir( m_workingDir + SF2_PATH ), m_version( defaultVersion() ) { if (! qgetenv("LMMS_DATA_DIR").isEmpty()) @@ -176,6 +178,16 @@ void ConfigManager::setBackgroundArtwork( const QString & _ba ) #endif } +void ConfigManager::setGIGDir(const QString &gd) +{ + m_gigDir = gd; +} + +void ConfigManager::setSF2Dir(const QString &sfd) +{ + m_sf2Dir = sfd; +} + @@ -237,31 +249,6 @@ void ConfigManager::setValue( const QString & _class, -#ifdef LMMS_BUILD_WIN32 -#include -#include - -// taken from qt-win-opensource-src-4.2.2/src/corelib/io/qsettings.cpp -static QString windowsConfigPath( int _type ) -{ - QString result; - - QLibrary library( "shell32" ); - typedef BOOL( WINAPI* GetSpecialFolderPath )( HWND, char *, int, BOOL ); - GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath) - library.resolve( "SHGetSpecialFolderPathA" ); - if( SHGetSpecialFolderPath ) - { - char path[MAX_PATH]; - SHGetSpecialFolderPath( 0, path, _type, false ); - result = QString::fromLocal8Bit( path ); - } - return result; -} -#endif - - - void ConfigManager::loadConfigFile() { // read the XML file and create DOM tree @@ -336,6 +323,9 @@ void ConfigManager::loadConfigFile() } } setWorkingDir( value( "paths", "workingdir" ) ); + + setGIGDir( value( "paths", "gigdir" ) == "" ? gigDir() : value( "paths", "gigdir" ) ); + setSF2Dir( value( "paths", "sf2dir" ) == "" ? sf2Dir() : value( "paths", "sf2dir" ) ); setVSTDir( value( "paths", "vstdir" ) ); setFLDir( value( "paths", "fldir" ) ); setLADSPADir( value( "paths", "laddir" ) ); @@ -363,10 +353,10 @@ void ConfigManager::loadConfigFile() !QDir( m_vstDir ).exists() ) { #ifdef LMMS_BUILD_WIN32 - m_vstDir = windowsConfigPath( CSIDL_PROGRAM_FILES ) + - QDir::separator() + "VstPlugins"; + QString programFiles = QString::fromLocal8Bit( getenv( "ProgramFiles" ) ); + m_vstDir = programFiles + QDir::separator() + "VstPlugins" + QDir::separator(); #else - m_vstDir = ensureTrailingSlash( QDir::home().absolutePath() ); + m_vstDir = m_workingDir + "plugins/vst/"; #endif } @@ -385,6 +375,7 @@ void ConfigManager::loadConfigFile() #else m_ladDir = qApp->applicationDirPath() + '/' + LIB_DIR + "/ladspa/"; #endif + m_ladDir += ","+userLadspaDir(); } #ifdef LMMS_HAVE_STK @@ -422,8 +413,14 @@ void ConfigManager::loadConfigFile() if( QDir( m_workingDir ).exists() ) { QDir().mkpath( userProjectsDir() ); + QDir().mkpath( userTemplateDir() ); QDir().mkpath( userSamplesDir() ); QDir().mkpath( userPresetsDir() ); + QDir().mkpath( userGigDir() ); + QDir().mkpath( userSf2Dir() ); + QDir().mkpath( userVstDir() ); + QDir().mkpath( userLadspaDir() ); + } upgrade(); @@ -438,6 +435,8 @@ void ConfigManager::saveConfigFile() setValue( "paths", "workingdir", m_workingDir ); setValue( "paths", "vstdir", m_vstDir ); setValue( "paths", "fldir", m_flDir ); + setValue( "paths", "gigdir", m_gigDir ); + setValue( "paths", "sf2dir", m_sf2Dir ); setValue( "paths", "laddir", m_ladDir ); #ifdef LMMS_HAVE_STK setValue( "paths", "stkdir", m_stkDir ); diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index de0a4e6e0..348009eb2 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -52,9 +52,13 @@ DummyTrackContainer * Engine::s_dummyTC = NULL; void Engine::init() { + Engine *engine = inst(); + + emit engine->initProgress(tr("Generating wavetables")); // generate (load from file) bandlimited wavetables BandLimitedWave::generateWaves(); + emit engine->initProgress(tr("Initializing data structures")); s_projectJournal = new ProjectJournal; s_mixer = new Mixer; s_song = new Song; @@ -65,11 +69,13 @@ void Engine::init() s_projectJournal->setJournalling( true ); + emit engine->initProgress(tr("Opening audio and midi devices")); s_mixer->initDevices(); PresetPreviewPlayHandle::init(); s_dummyTC = new DummyTrackContainer; + emit engine->initProgress(tr("Launching mixer threads")); s_mixer->startProcessing(); } @@ -114,3 +120,5 @@ void Engine::updateFramesPerTick() s_framesPerTick = s_mixer->processingSampleRate() * 60.0f * 4 / DefaultTicksPerTact / s_song->getTempo(); } + +Engine * Engine::s_instanceOfMe = NULL; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 5a2c8a272..083a84b9f 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -98,8 +98,8 @@ inline void FxChannel::processed() void FxChannel::incrementDeps() { - m_dependenciesMet.ref(); - if( m_dependenciesMet >= m_receives.size() && ! m_queued ) + int i = m_dependenciesMet.fetchAndAddOrdered( 1 ) + 1; + if( i >= m_receives.size() && ! m_queued ) { m_queued = true; MixerWorkerThread::addJob( this ); @@ -327,6 +327,19 @@ void FxMixer::deleteChannel( int index ) for( int i = index; i < m_fxChannels.size(); ++i ) { validateChannelName( i, i + 1 ); + + // set correct channel index + m_fxChannels[i]->m_channelIndex = i; + + // now check all routes and update names of the send models + foreach( FxRoute * r, m_fxChannels[i]->m_sends ) + { + r->updateName(); + } + foreach( FxRoute * r, m_fxChannels[i]->m_receives ) + { + r->updateName(); + } } } @@ -364,18 +377,9 @@ void FxMixer::moveChannelLeft( int index ) { inst->effectChannelModel()->setValue(a); } - } } } - - // actually do the swap - FxChannel * tmpChannel = m_fxChannels[a]; - m_fxChannels[a] = m_fxChannels[b]; - m_fxChannels[b] = tmpChannel; - - validateChannelName( a, b ); - validateChannelName( b, a ); } @@ -761,22 +765,9 @@ void FxMixer::loadSettings( const QDomElement & _this ) void FxMixer::validateChannelName( int index, int oldIndex ) { - FxChannel * fxc = m_fxChannels[ index ]; - if( fxc->m_name == tr( "FX %1" ).arg( oldIndex ) ) + if( m_fxChannels[index]->m_name == tr( "FX %1" ).arg( oldIndex ) ) { - fxc->m_name = tr( "FX %1" ).arg( index ); - } - // set correct channel index - fxc->m_channelIndex = index; - - // now check all routes and update names of the send models - foreach( FxRoute * r, fxc->m_sends ) - { - r->updateName(); - } - foreach( FxRoute * r, fxc->m_receives ) - { - r->updateName(); + m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index ); } } diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp index 11a4fb5b8..e44cf4e00 100644 --- a/src/core/InstrumentPlayHandle.cpp +++ b/src/core/InstrumentPlayHandle.cpp @@ -28,8 +28,7 @@ InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) : PlayHandle( TypeInstrumentPlayHandle ), - m_instrument( instrument ), - m_instrumentTrack( instrumentTrack ) + m_instrument( instrument ) { setAudioPort( instrumentTrack->audioPort() ); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 845abbf93..686e2c5c7 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -930,15 +930,7 @@ void Mixer::fifoWriter::finish() void Mixer::fifoWriter::run() { -// set denormal protection for this thread -#ifdef __SSE3__ -/* DAZ flag */ - _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); -#endif -#ifdef __SSE__ -/* FTZ flag */ - _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); -#endif + disable_denormals(); #if 0 #ifdef LMMS_BUILD_LINUX diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index ef011f55f..45554708c 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -29,12 +29,7 @@ #include "ThreadableJob.h" #include "Mixer.h" -#ifdef __SSE__ -#include -#endif -#ifdef __SSE3__ -#include -#endif +#include "denormals.h" MixerWorkerThread::JobQueue MixerWorkerThread::globalJobQueue; QWaitCondition * MixerWorkerThread::queueReadyWaitCond = NULL; @@ -159,15 +154,8 @@ void MixerWorkerThread::startAndWaitForJobs() void MixerWorkerThread::run() { -// set denormal protection for this thread -#ifdef __SSE3__ -/* DAZ flag */ - _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); -#endif -#ifdef __SSE__ -/* FTZ flag */ - _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); -#endif + disable_denormals(); + QMutex m; while( m_quit == false ) { diff --git a/src/core/ProjectJournal.cpp b/src/core/ProjectJournal.cpp index 77c5c461f..4e5c9229b 100644 --- a/src/core/ProjectJournal.cpp +++ b/src/core/ProjectJournal.cpp @@ -97,6 +97,15 @@ void ProjectJournal::redo() } } +bool ProjectJournal::canUndo() const +{ + return !m_undoCheckPoints.isEmpty(); +} + +bool ProjectJournal::canRedo() const +{ + return !m_redoCheckPoints.isEmpty(); +} diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 0f688a1b1..b336aec82 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -94,7 +94,6 @@ Song::Song() : m_errors( new QList() ), m_playMode( Mode_None ), m_length( 0 ), - m_trackToPlay( NULL ), m_patternToPlay( NULL ), m_loopPattern( false ), m_elapsedMilliSeconds( 0 ), @@ -199,14 +198,17 @@ void Song::savePos() void Song::processNextBuffer() { + // if not playing, nothing to do if( m_playing == false ) { return; } TrackList trackList; - int tcoNum = -1; + int tcoNum = -1; // track content object number + // determine the list of tracks to play and the track content object + // (TCO) number switch( m_playMode ) { case Mode_PlaySong: @@ -218,10 +220,6 @@ void Song::processNextBuffer() } break; - case Mode_PlayTrack: - trackList.push_back( m_trackToPlay ); - break; - case Mode_PlayBB: if( Engine::getBBTrackContainer()->numOfBBs() > 0 ) { @@ -247,6 +245,7 @@ void Song::processNextBuffer() } + // if we have no tracks to play, nothing to do if( trackList.empty() == true ) { return; @@ -259,6 +258,8 @@ void Song::processNextBuffer() if( checkLoop ) { + // if looping-mode is enabled and we are outside of the looping + // range, go to the beginning of the range if( m_playPos[m_playMode] < tl->loopBegin() || m_playPos[m_playMode] >= tl->loopEnd() ) { @@ -269,16 +270,13 @@ void Song::processNextBuffer() } } - f_cnt_t totalFramesPlayed = 0; + f_cnt_t framesPlayed = 0; const float framesPerTick = Engine::framesPerTick(); - while( totalFramesPlayed < Engine::mixer()->framesPerPeriod() ) + while( framesPlayed < Engine::mixer()->framesPerPeriod() ) { m_vstSyncController.update(); - f_cnt_t playedFrames = Engine::mixer()->framesPerPeriod() - - totalFramesPlayed; - float currentFrame = m_playPos[m_playMode].currentFrame(); // did we play a tick? if( currentFrame >= framesPerTick ) @@ -335,6 +333,9 @@ void Song::processNextBuffer() m_vstSyncController.startCycle( tl->loopBegin().getTicks(), tl->loopEnd().getTicks() ); + // if looping-mode is enabled and we have got + // past the looping range, return to the + // beginning of the range if( m_playPos[m_playMode] >= tl->loopEnd() ) { m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); @@ -353,23 +354,26 @@ void Song::processNextBuffer() m_playPos[m_playMode].setCurrentFrame( currentFrame ); } - f_cnt_t lastFrames = ( f_cnt_t )framesPerTick - - ( f_cnt_t )currentFrame; + f_cnt_t framesToPlay = + Engine::mixer()->framesPerPeriod() - framesPlayed; + + f_cnt_t framesLeft = ( f_cnt_t )framesPerTick - + ( f_cnt_t )currentFrame; // skip last frame fraction - if( lastFrames == 0 ) + if( framesLeft == 0 ) { - ++totalFramesPlayed; + ++framesPlayed; m_playPos[m_playMode].setCurrentFrame( currentFrame + 1.0f ); continue; } - // do we have some samples left in this tick but these are - // less then samples we have to play? - if( lastFrames < playedFrames ) + // do we have samples left in this tick but these are less + // than samples we have to play? + if( framesLeft < framesToPlay ) { - // then set played_samples to remaining samples, the + // then set framesToPlay to remaining samples, the // rest will be played in next loop - playedFrames = lastFrames; + framesToPlay = framesLeft; } if( ( f_cnt_t ) currentFrame == 0 ) @@ -378,25 +382,25 @@ void Song::processNextBuffer() { m_globalAutomationTrack->play( m_playPos[m_playMode], - playedFrames, - totalFramesPlayed, tcoNum ); + framesToPlay, + framesPlayed, tcoNum ); } // loop through all tracks and play them for( int i = 0; i < trackList.size(); ++i ) { trackList[i]->play( m_playPos[m_playMode], - playedFrames, - totalFramesPlayed, tcoNum ); + framesToPlay, + framesPlayed, tcoNum ); } } // update frame-counters - totalFramesPlayed += playedFrames; - m_playPos[m_playMode].setCurrentFrame( playedFrames + + framesPlayed += framesToPlay; + m_playPos[m_playMode].setCurrentFrame( framesToPlay + currentFrame ); m_elapsedMilliSeconds += - ( ( playedFrames / framesPerTick ) * 60 * 1000 / 48 ) + ( ( framesToPlay / framesPerTick ) * 60 * 1000 / 48 ) / getTempo(); m_elapsedTacts = m_playPos[Mode_PlaySong].getTact(); m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerTact() ) / 48; @@ -470,28 +474,6 @@ void Song::playAndRecord() -void Song::playTrack( Track * trackToPlay ) -{ - if( isStopped() == false ) - { - stop(); - } - m_trackToPlay = trackToPlay; - - m_playMode = Mode_PlayTrack; - m_playing = true; - m_paused = false; - - m_vstSyncController.setPlaybackState( true ); - - savePos(); - - emit playbackStateChanged(); -} - - - - void Song::playBB() { if( isStopped() == false ) @@ -847,8 +829,10 @@ void Song::clearProject() // create new file void Song::createNewProject() { - QString defaultTemplate = ConfigManager::inst()->userProjectsDir() - + "templates/default.mpt"; + + QString defaultTemplate = ConfigManager::inst()->userTemplateDir() + + "default.mpt"; + if( QFile::exists( defaultTemplate ) ) { diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 4f5440af0..456cb7cd9 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -78,11 +78,6 @@ */ const int RESIZE_GRIP_WIDTH = 4; -/*! The size of the track buttons in pixels - */ -const int TRACK_OP_BTN_WIDTH = 20; -const int TRACK_OP_BTN_HEIGHT = 14; - /*! A pointer for that text bubble used when moving segments, etc. * @@ -108,7 +103,7 @@ TrackContentObject::TrackContentObject( Track * track ) : m_name( QString::null ), m_startPosition(), m_length(), - m_mutedModel( false, this, tr( "Muted" ) ), + m_mutedModel( false, this, tr( "Mute" ) ), m_selectViewOnCreate( false ) { if( getTrack() ) @@ -1688,13 +1683,18 @@ void TrackOperationsWidget::paintEvent( QPaintEvent * pe ) - /*! \brief Clone this track * */ void TrackOperationsWidget::cloneTrack() { - m_trackView->getTrack()->clone(); + TrackContainerView *tcView = m_trackView->trackContainerView(); + + Track *newTrack = m_trackView->getTrack()->clone(); + TrackView *newTrackView = tcView->createTrackView( newTrack ); + + int index = tcView->trackViews().indexOf( m_trackView ); + tcView->moveTrackView( newTrackView, index + 1 ); } @@ -1810,7 +1810,7 @@ Track::Track( TrackTypes type, TrackContainer * tc ) : m_trackContainer( tc ), /*!< The track container object */ m_type( type ), /*!< The track type */ m_name(), /*!< The track's name */ - m_mutedModel( false, this, tr( "Muted" ) ), + m_mutedModel( false, this, tr( "Mute" ) ), /*!< For controlling track muting */ m_soloModel( false, this, tr( "Solo" ) ), /*!< For controlling track soloing */ @@ -1904,12 +1904,12 @@ Track * Track::create( const QDomElement & element, TrackContainer * tc ) /*! \brief Clone a track from this track * */ -void Track::clone() +Track * Track::clone() { QDomDocument doc; QDomElement parent = doc.createElement( "clone" ); saveState( doc, parent ); - create( parent.firstChild().toElement(), m_trackContainer ); + return create( parent.firstChild().toElement(), m_trackContainer ); } diff --git a/src/core/main.cpp b/src/core/main.cpp index 5313811d1..bba357b01 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -27,13 +27,7 @@ #include "lmmsversion.h" #include "versioninfo.h" -// denormals stripping -#ifdef __SSE__ -#include -#endif -#ifdef __SSE3__ -#include -#endif +#include "denormals.h" #include #include @@ -103,15 +97,7 @@ int main( int argc, char * * argv ) // intialize RNG srand( getpid() + time( 0 ) ); - // set denormal protection for this thread - #ifdef __SSE3__ - /* DAZ flag */ - _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); - #endif - #ifdef __SSE__ - /* FTZ flag */ - _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); - #endif + disable_denormals(); bool core_only = false; bool fullscreen = true; @@ -217,12 +203,10 @@ int main( int argc, char * * argv ) else if( argc > i+1 && ( QString( argv[i] ) == "--upgrade" || QString( argv[i] ) == "-u" ) ) { - QString inFile( argv[i + 1] ); - DataFile dataFile( inFile ); + DataFile dataFile( QString::fromLocal8Bit( argv[i + 1] ) ); if (argc > i+2) { - const QString outFile = argv[i + 2]; - dataFile.writeFile( outFile ); + dataFile.writeFile( QString::fromLocal8Bit( argv[i + 2] ) ); } else { @@ -246,7 +230,7 @@ int main( int argc, char * * argv ) else if( argc > i && ( QString( argv[i] ) == "--dump" || QString( argv[i] ) == "-d" ) ) { - QFile f( argv[i + 1] ); + QFile f( QString::fromLocal8Bit( argv[i + 1] ) ); f.open( QIODevice::ReadOnly ); QString d = qUncompress( f.readAll() ); printf( "%s\n", d.toUtf8().constData() ); @@ -255,14 +239,14 @@ int main( int argc, char * * argv ) else if( argc > i && ( QString( argv[i] ) == "--render" || QString( argv[i] ) == "-r" ) ) { - file_to_load = QString( argv[i + 1] ); + file_to_load = QString::fromLocal8Bit( argv[i + 1] ); render_out = baseName( file_to_load ) + "."; ++i; } else if( argc > i && ( QString( argv[i] ) == "--output" || QString( argv[i] ) == "-o" ) ) { - render_out = baseName( QString( argv[i + 1] ) ) + "."; + render_out = baseName( QString::fromLocal8Bit( argv[i + 1] ) ) + "."; ++i; } else if( argc > i && @@ -386,7 +370,7 @@ int main( int argc, char * * argv ) else if( argc > i && ( QString( argv[i] ) == "--import" ) ) { - file_to_import = argv[i+1]; + file_to_import = QString::fromLocal8Bit( argv[i+1] ); ++i; // exit after import? (only for debugging) if( argc > i && QString( argv[i+1] ) == "-e" ) @@ -396,7 +380,7 @@ int main( int argc, char * * argv ) } else if( argc > i && ( QString( argv[i] ) == "--profile" || QString( argv[i] ) == "-p" ) ) { - profilerOutputFile = argv[i+1]; + profilerOutputFile = QString::fromLocal8Bit( argv[i+1] ); ++i; } else @@ -407,7 +391,7 @@ int main( int argc, char * * argv ) "Try \"%s --help\" for more information.\n\n", argv[i], argv[0] ); return( EXIT_FAILURE ); } - file_to_load = argv[i]; + file_to_load = QString::fromLocal8Bit( argv[i] ); } } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 166552a39..e92b5eb71 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -35,6 +35,7 @@ #include "ProjectJournal.h" #include "RenameDialog.h" #include "StringPairDrag.h" +#include "TextFloat.h" #include "ToolTip.h" @@ -420,7 +421,15 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) journallingObject( val.toInt() ) ); if( mod != NULL ) { - m_pat->addObject( mod ); + bool added = m_pat->addObject( mod ); + if ( !added ) + { + TextFloat::displayMessage( mod->displayName(), + tr( "Model is already connected " + "to this pattern." ), + embed::getIconPixmap( "automation" ), + 2000 ); + } } update(); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 548e4ad14..a23c37f81 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -55,6 +55,7 @@ SET(LMMS_SRCS gui/widgets/InstrumentFunctionViews.cpp gui/widgets/InstrumentMidiIOView.cpp gui/widgets/InstrumentSoundShapingView.cpp + gui/widgets/LeftRightNav.cpp gui/widgets/Knob.cpp gui/widgets/LadspaControlView.cpp gui/widgets/LcdSpinBox.cpp diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index fb70da6ab..b1a0c3fb3 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -30,6 +30,8 @@ #include "embed.h" #include "PluginFactory.h" +#include + EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : QDialog( _parent ), @@ -168,25 +170,64 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { m_currentSelection = m_effectKeys[m_model.mapToSource( _idx ).row()]; } - if( m_currentSelection.desc && m_currentSelection.desc->subPluginFeatures ) + if( m_currentSelection.desc ) { m_descriptionWidget = new QWidget; - QVBoxLayout * l = new QVBoxLayout( m_descriptionWidget ); - l->setMargin( 4 ); - l->setSpacing( 0 ); - ui->scrollArea->setWidget( m_descriptionWidget ); + QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); - m_currentSelection.desc->subPluginFeatures-> - fillDescriptionWidget( m_descriptionWidget, &m_currentSelection ); - foreach( QWidget * w, m_descriptionWidget->findChildren() ) - { - if( w->parent() == m_descriptionWidget ) - { - l->addWidget( w ); - } - } - l->setSizeConstraint( QLayout::SetFixedSize ); + Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); + + if ( descriptor.logo ) + { + QLabel *logoLabel = new QLabel( m_descriptionWidget ); + logoLabel->setPixmap( descriptor.logo->pixmap() ); + logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + hbox->addWidget( logoLabel ); + hbox->setAlignment( logoLabel, Qt::AlignTop); + } + + QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); + + hbox->addWidget(textualInfoWidget); + + QVBoxLayout * textWidgetLayout = new QVBoxLayout( textualInfoWidget); + textWidgetLayout->setMargin( 4 ); + textWidgetLayout->setSpacing( 0 ); + + std::string stdName(descriptor.name); + if ( stdName != "ladspaeffect" ) + { + QLabel *label = new QLabel(m_descriptionWidget); + QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; + labelText += "

" + tr("Description") + ": " + QString::fromUtf8(descriptor.description) + "

"; + labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; + + label->setText(labelText); + textWidgetLayout->addWidget(label); + } + + if ( m_currentSelection.desc->subPluginFeatures ) + { + QWidget *subWidget = new QWidget(textualInfoWidget); + QVBoxLayout * subLayout = new QVBoxLayout( subWidget ); + subLayout->setMargin( 4 ); + subLayout->setSpacing( 0 ); + m_currentSelection.desc->subPluginFeatures-> + fillDescriptionWidget( subWidget, &m_currentSelection ); + foreach( QWidget * w, subWidget->findChildren() ) + { + if( w->parent() == subWidget ) + { + subLayout->addWidget( w ); + } + } + + textWidgetLayout->addWidget(subWidget); + } + + ui->scrollArea->setWidget( m_descriptionWidget ); m_descriptionWidget->show(); } } diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index e7d946acc..cd95e5a7d 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "FileBrowser.h" #include "BBTrackContainer.h" @@ -98,6 +99,10 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, addContentWidget( ops ); + // Whenever the FileBrowser has focus, Ctrl+F should direct focus to its filter box. + QShortcut *filterFocusShortcut = new QShortcut( QKeySequence( QKeySequence::Find ), this, SLOT(giveFocusToFilter()) ); + filterFocusShortcut->setContext(Qt::WidgetWithChildrenShortcut); + reloadTree(); show(); } @@ -254,7 +259,15 @@ void FileBrowser::reloadTree( void ) filterItems( text ); } - +void FileBrowser::giveFocusToFilter() +{ + if (!m_filterEdit->hasFocus()) + { + // give focus to filter text box and highlight its text for quick editing if not previously focused + m_filterEdit->setFocus(); + m_filterEdit->selectAll(); + } +} void FileBrowser::addItems(const QString & path ) @@ -872,8 +885,6 @@ bool Directory::addItems(const QString & path ) addChild( new Directory( cur_file, path, m_filter ) ); m_dirCount++; - //recurse for each dir - addItems( path + cur_file + QDir::separator() ); } added_something = true; diff --git a/src/gui/Forms/EffectSelectDialog.ui b/src/gui/Forms/EffectSelectDialog.ui index a58cd6c8c..a19233ac8 100644 --- a/src/gui/Forms/EffectSelectDialog.ui +++ b/src/gui/Forms/EffectSelectDialog.ui @@ -7,7 +7,7 @@ 0 0 585 - 547 + 550 @@ -40,41 +40,20 @@ - - - - 0 - 200 - + + + QFrame::NoFrame - - - 16777215 - 210 - - - - Plugin description - - - - - - QFrame::NoFrame - - - - - 0 - 0 - 497 - 109 - - - - - - + + + + 0 + 0 + 497 + 109 + + + diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 141a37619..2a8271048 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -267,19 +267,20 @@ void FxMixerView::loadSettings( const QDomElement & _this ) FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, - int _chIndex ) + int channelIndex ) { - m_fxLine = new FxLine(_parent, _mv, _chIndex); + m_fxLine = new FxLine(_parent, _mv, channelIndex); - FxMixer * m = Engine::fxMixer(); - m_fader = new Fader( &m->effectChannel(_chIndex)->m_volumeModel, - tr( "FX Fader %1" ).arg( _chIndex ), m_fxLine ); + FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); + + m_fader = new Fader( &fxChannel->m_volumeModel, + tr( "FX Fader %1" ).arg( channelIndex ), m_fxLine ); m_fader->move( 16-m_fader->width()/2, m_fxLine->height()- m_fader->height()-5 ); m_muteBtn = new PixmapButton( m_fxLine, tr( "Mute" ) ); - m_muteBtn->setModel( &m->effectChannel(_chIndex)->m_muteModel ); + m_muteBtn->setModel( &fxChannel->m_muteModel ); m_muteBtn->setActiveGraphic( embed::getIconPixmap( "led_off" ) ); m_muteBtn->setInactiveGraphic( @@ -289,23 +290,34 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, ToolTip::add( m_muteBtn, tr( "Mute this FX channel" ) ); m_soloBtn = new PixmapButton( m_fxLine, tr( "Solo" ) ); - m_soloBtn->setModel( &m->effectChannel(_chIndex)->m_soloModel ); + m_soloBtn->setModel( &fxChannel->m_soloModel ); m_soloBtn->setActiveGraphic( embed::getIconPixmap( "led_red" ) ); m_soloBtn->setInactiveGraphic( embed::getIconPixmap( "led_off" ) ); m_soloBtn->setCheckable( true ); m_soloBtn->move( 9, m_fader->y()-21); - connect(&m->effectChannel(_chIndex)->m_soloModel, SIGNAL( dataChanged() ), + connect(&fxChannel->m_soloModel, SIGNAL( dataChanged() ), _mv, SLOT ( toggledSolo() ) ); ToolTip::add( m_soloBtn, tr( "Solo FX channel" ) ); // Create EffectRack for the channel - m_rackView = new EffectRackView( &m->effectChannel(_chIndex)->m_fxChain, _mv->m_racksWidget ); + m_rackView = new EffectRackView( &fxChannel->m_fxChain, _mv->m_racksWidget ); m_rackView->setFixedSize( 245, FxLine::FxLineHeight ); } +void FxMixerView::FxChannelView::setChannelIndex( int index ) +{ + FxChannel* fxChannel = Engine::fxMixer()->effectChannel( index ); + + m_fader->setModel( &fxChannel->m_volumeModel ); + m_muteBtn->setModel( &fxChannel->m_muteModel ); + m_soloBtn->setModel( &fxChannel->m_soloModel ); + m_rackView->setModel( &fxChannel->m_fxChain ); +} + + void FxMixerView::toggledSolo() { Engine::fxMixer()->toggledSolo(); @@ -432,53 +444,39 @@ void FxMixerView::deleteUnusedChannels() -void FxMixerView::moveChannelLeft(int index) +void FxMixerView::moveChannelLeft(int index, int focusIndex) { // can't move master or first channel left or last channel right if( index <= 1 || index >= m_fxChannelViews.size() ) return; - int selIndex = m_currentFxLine->channelIndex(); + FxMixer *m = Engine::fxMixer(); - FxMixer * mix = Engine::fxMixer(); - mix->moveChannelLeft(index); + // Move instruments channels + m->moveChannelLeft( index ); - // refresh the two mixer views - for( int i = index-1; i <= index; ++i ) - { - // delete the mixer view - int replaceIndex = chLayout->indexOf(m_fxChannelViews[i]->m_fxLine); + // Update widgets models + m_fxChannelViews[index]->setChannelIndex( index - 1 ); + m_fxChannelViews[index - 1]->setChannelIndex( index ); - chLayout->removeWidget(m_fxChannelViews[i]->m_fxLine); - m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); - delete m_fxChannelViews[i]->m_fader; - delete m_fxChannelViews[i]->m_muteBtn; - delete m_fxChannelViews[i]->m_soloBtn; - delete m_fxChannelViews[i]->m_fxLine; - delete m_fxChannelViews[i]; + // Swap positions in array + qSwap(m->m_fxChannels[index], m->m_fxChannels[index - 1]); - // add it again - m_fxChannelViews[i] = new FxChannelView( m_channelAreaWidget, this, i ); - chLayout->insertWidget( replaceIndex, m_fxChannelViews[i]->m_fxLine ); - m_racksLayout->insertWidget( replaceIndex, m_fxChannelViews[i]->m_rackView ); - } + // Focus on new position + setCurrentFxLine( focusIndex ); +} - // keep selected channel - if( selIndex == index ) - { - selIndex = index-1; - } - else if( selIndex == index - 1 ) - { - selIndex = index; - } - setCurrentFxLine(selIndex); + + +void FxMixerView::moveChannelLeft(int index) +{ + moveChannelLeft( index, index - 1 ); } void FxMixerView::moveChannelRight(int index) { - moveChannelLeft(index+1); + moveChannelLeft( index + 1, index + 1 ); } @@ -512,6 +510,12 @@ void FxMixerView::keyPressEvent(QKeyEvent * e) setCurrentFxLine( m_currentFxLine->channelIndex()+1 ); } break; + case Qt::Key_Insert: + if ( e->modifiers() & Qt::ShiftModifier ) + { + addNewChannel(); + } + break; } } diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 78cf609ef..4a9b35f73 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -49,6 +49,7 @@ GuiApplication* GuiApplication::instance() return s_instance; } + GuiApplication::GuiApplication() { // Init style and palette @@ -64,27 +65,60 @@ GuiApplication::GuiApplication() // Show splash screen QSplashScreen splashScreen( embed::getIconPixmap( "splash" ) ); splashScreen.show(); - splashScreen.showMessage( MainWindow::tr( "Version %1" ).arg( LMMS_VERSION ), - Qt::AlignRight | Qt::AlignBottom, Qt::white ); + + QHBoxLayout layout; + layout.setAlignment(Qt::AlignBottom); + splashScreen.setLayout(&layout); + + // Create a left-aligned label for loading progress + // & a right-aligned label for version info + QLabel loadingProgressLabel; + m_loadingProgressLabel = &loadingProgressLabel; + QLabel versionLabel(MainWindow::tr( "Version %1" ).arg( LMMS_VERSION )); + + loadingProgressLabel.setAlignment(Qt::AlignLeft); + versionLabel.setAlignment(Qt::AlignRight); + + layout.addWidget(&loadingProgressLabel); + layout.addWidget(&versionLabel); + + // may have long gaps between future frames, so force update now + splashScreen.update(); qApp->processEvents(); + connect(Engine::inst(), SIGNAL(initProgress(const QString&)), + this, SLOT(displayInitProgress(const QString&))); + // Init central engine which handles all components of LMMS Engine::init(); s_instance = this; - m_mainWindow = new MainWindow; + displayInitProgress(tr("Preparing UI")); + m_mainWindow = new MainWindow; + connect(m_mainWindow, SIGNAL(initProgress(const QString&)), + this, SLOT(displayInitProgress(const QString&))); + + displayInitProgress(tr("Preparing song editor")); m_songEditor = new SongEditorWindow(Engine::getSong()); + displayInitProgress(tr("Preparing mixer")); m_fxMixerView = new FxMixerView; + displayInitProgress(tr("Preparing controller rack")); m_controllerRackView = new ControllerRackView; + displayInitProgress(tr("Preparing project notes")); m_projectNotes = new ProjectNotes; + displayInitProgress(tr("Preparing beat/bassline editor")); m_bbEditor = new BBEditor(Engine::getBBTrackContainer()); + displayInitProgress(tr("Preparing piano roll")); m_pianoRoll = new PianoRollWindow(); + displayInitProgress(tr("Preparing automation editor")); m_automationEditor = new AutomationEditorWindow; m_mainWindow->finalize(); splashScreen.finish(m_mainWindow); + + m_loadingProgressLabel = nullptr; } GuiApplication::~GuiApplication() @@ -92,3 +126,14 @@ GuiApplication::~GuiApplication() InstrumentTrackView::cleanupWindowCache(); s_instance = nullptr; } + + +void GuiApplication::displayInitProgress(const QString &msg) +{ + Q_ASSERT(m_loadingProgressLabel != nullptr); + + m_loadingProgressLabel->setText(msg); + // must force a UI update and process events, as there may be long gaps between processEvents() calls during init + m_loadingProgressLabel->repaint(); + qApp->processEvents(); +} diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index b7c3eced7..f13928969 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -50,7 +50,6 @@ const int CD_KNOB_X_SPACING = 32; const int CD_LFO_SHAPES_X = 6; const int CD_LFO_SHAPES_Y = 36; -const int CD_LFO_GRAPH_X = 6; const int CD_LFO_GRAPH_Y = CD_ENV_KNOBS_LBL_Y+15; const int CD_LFO_CD_KNOB_Y = CD_LFO_GRAPH_Y-2; const int CD_LFO_BASE_CD_KNOB_X = CD_LFO_SHAPES_X + 64; @@ -243,7 +242,6 @@ LfoControllerDialog::LfoControllerDialog( Controller * _model, QWidget * _parent LfoControllerDialog::~LfoControllerDialog() { m_userWaveBtn->disconnect( this ); - //delete m_subWindow; } @@ -287,13 +285,6 @@ void LfoControllerDialog::contextMenuEvent( QContextMenuEvent * ) } -/* -void lfoControllerDialog::paintEvent( QPaintEvent * _pe ) -{ - QWidget::paintEvent( _pe ); -} -*/ - void LfoControllerDialog::modelChanged() { diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 154133882..80406ec7c 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -97,7 +98,9 @@ MainWindow::MainWindow() : ConfigManager* confMgr = ConfigManager::inst(); + emit initProgress(tr("Preparing plugin browser")); sideBar->appendTab( new PluginBrowser( splitter ) ); + emit initProgress(tr("Preparing file browsers")); sideBar->appendTab( new FileBrowser( confMgr->userProjectsDir() + "*" + confMgr->factoryProjectsDir(), @@ -151,6 +154,7 @@ MainWindow::MainWindow() : m_workspace = new QMdiArea( splitter ); // Load background + emit initProgress(tr("Loading background artwork")); QString bgArtwork = ConfigManager::inst()->backgroundArtwork(); QImage bgImage; if( !bgArtwork.isEmpty() ) @@ -232,12 +236,12 @@ void MainWindow::finalize() project_menu->addAction( embed::getIconPixmap( "project_new" ), tr( "&New" ), this, SLOT( createNewProject() ), - Qt::CTRL + Qt::Key_N ); + QKeySequence::New ); project_menu->addAction( embed::getIconPixmap( "project_open" ), tr( "&Open..." ), this, SLOT( openProject() ), - Qt::CTRL + Qt::Key_O ); + QKeySequence::Open ); m_recentlyOpenedProjectsMenu = project_menu->addMenu( embed::getIconPixmap( "project_open_recent" ), @@ -250,7 +254,7 @@ void MainWindow::finalize() project_menu->addAction( embed::getIconPixmap( "project_save" ), tr( "&Save" ), this, SLOT( saveProject() ), - Qt::CTRL + Qt::Key_S ); + QKeySequence::Save ); project_menu->addAction( embed::getIconPixmap( "project_saveas" ), tr( "Save &As..." ), this, SLOT( saveProjectAs() ), @@ -289,18 +293,29 @@ void MainWindow::finalize() QMenu * edit_menu = new QMenu( this ); menuBar()->addMenu( edit_menu )->setText( tr( "&Edit" ) ); - edit_menu->addAction( embed::getIconPixmap( "edit_undo" ), + m_undoAction = edit_menu->addAction( embed::getIconPixmap( "edit_undo" ), tr( "Undo" ), this, SLOT( undo() ), - Qt::CTRL + Qt::Key_Z ); - edit_menu->addAction( embed::getIconPixmap( "edit_redo" ), + QKeySequence::Undo ); + m_redoAction = edit_menu->addAction( embed::getIconPixmap( "edit_redo" ), tr( "Redo" ), this, SLOT( redo() ), - Qt::CTRL + Qt::Key_Y ); + QKeySequence::Redo ); + // Ensure that both (Ctrl+Y) and (Ctrl+Shift+Z) activate redo shortcut regardless of OS defaults + if (QKeySequence(QKeySequence::Redo) != QKeySequence(Qt::CTRL + Qt::Key_Y)) + { + new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_Y ), this, SLOT(redo()) ); + } + if (QKeySequence(QKeySequence::Redo) != QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z )) + { + new QShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_Z ), this, SLOT(redo()) ); + } + edit_menu->addSeparator(); edit_menu->addAction( embed::getIconPixmap( "setup_general" ), tr( "Settings" ), this, SLOT( showSettingsDialog() ) ); + connect( edit_menu, SIGNAL(aboutToShow()), this, SLOT(updateUndoRedoButtons()) ); m_viewMenu = new QMenu( this ); menuBar()->addMenu( m_viewMenu )->setText( tr( "&View" ) ); @@ -379,7 +394,7 @@ void MainWindow::finalize() ToolButton * project_open_recent = new ToolButton( embed::getIconPixmap( "project_open_recent" ), - tr( "Recently opened project" ), + tr( "Recently opened projects" ), this, SLOT( emptySlot() ), m_toolBar ); project_open_recent->setMenu( m_recentlyOpenedProjectsMenu ); project_open_recent->setPopupMode( ToolButton::InstantPopup ); @@ -732,6 +747,14 @@ void MainWindow::createNewProject() { Engine::getSong()->createNewProject(); } + QString default_template = ConfigManager::inst()->userTemplateDir() + + "default.mpt"; + + //if we dont have a user default template, make one + if( !QFile::exists( default_template ) ) + { + Engine::getSong()->saveProjectFile( default_template ); + } } @@ -1059,35 +1082,37 @@ void MainWindow::updateViewMenu() qa = new QAction(tr( "Volume as dBV" ), this); qa->setData("displaydbv"); qa->setCheckable( true ); - qa->setChecked( ConfigManager::inst()->value( "app", "displaydbv" ). - toInt() ? true : false ); + qa->setChecked( ConfigManager::inst()->value( "app", "displaydbv" ).toInt() ); m_viewMenu->addAction(qa); // Maybe this is impossible? /* qa = new QAction(tr( "Tooltips" ), this); qa->setData("tooltips"); qa->setCheckable( true ); - qa->setChecked( ConfigManager::inst()->value( "tooltips", "disabled" ). - toInt() ? false : true ); + qa->setChecked( !ConfigManager::inst()->value( "tooltips", "disabled" ).toInt() ); m_viewMenu->addAction(qa); */ - // Should be doable. qa = new QAction(tr( "Smooth scroll" ), this); qa->setData("smoothscroll"); qa->setCheckable( true ); - qa->setChecked( ConfigManager::inst()->value( "ui", "smoothscroll" ). - toInt() ? true : false ); + qa->setChecked( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ); m_viewMenu->addAction(qa); // Not yet. /* qa = new QAction(tr( "One instrument track window" ), this); qa->setData("oneinstrument"); qa->setCheckable( true ); - qa->setChecked( ConfigManager::inst()->value( "ui", "oneinstrumenttrackwindow" ). - toInt() ? true : false ); + qa->setChecked( ConfigManager::inst()->value( "ui", "oneinstrumenttrackwindow" ).toInt() ); m_viewMenu->addAction(qa); */ + + qa = new QAction(tr( "Enable note labels in piano roll" ), this); + qa->setData("printnotelabels"); + qa->setCheckable( true ); + qa->setChecked( ConfigManager::inst()->value( "ui", "printnotelabels" ).toInt() ); + m_viewMenu->addAction(qa); + } @@ -1116,6 +1141,11 @@ void MainWindow::updateConfig( QAction * _who ) ConfigManager::inst()->setValue( "ui", "oneinstrumenttrackwindow", QString::number(checked) ); } + else if ( tag == "printnotelabels" ) + { + ConfigManager::inst()->setValue( "ui", "printnotelabels", + QString::number(checked) ); + } } @@ -1161,6 +1191,14 @@ void MainWindow::updatePlayPauseIcons() } +void MainWindow::updateUndoRedoButtons() +{ + // when the edit menu is shown, grey out the undo/redo buttons if there's nothing to undo/redo + // else, un-grey them + m_undoAction->setEnabled(Engine::projectJournal()->canUndo()); + m_redoAction->setEnabled(Engine::projectJournal()->canRedo()); +} + void MainWindow::undo() diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 91984f696..7ca9911ce 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "SetupDialog.h" #include "TabBar.h" @@ -105,6 +106,8 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_artworkDir( QDir::toNativeSeparators( ConfigManager::inst()->artworkDir() ) ), m_flDir( QDir::toNativeSeparators( ConfigManager::inst()->flDir() ) ), m_ladDir( QDir::toNativeSeparators( ConfigManager::inst()->ladspaDir() ) ), + m_gigDir( QDir::toNativeSeparators( ConfigManager::inst()->gigDir() ) ), + m_sf2Dir( QDir::toNativeSeparators( ConfigManager::inst()->sf2Dir() ) ), #ifdef LMMS_HAVE_FLUIDSYNTH m_defaultSoundfont( QDir::toNativeSeparators( ConfigManager::inst()->defaultSoundfont() ) ), #endif @@ -378,15 +381,34 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : dir_layout->setSpacing( 0 ); dir_layout->setMargin( 0 ); labelWidget( paths, tr( "Paths" ) ); + QLabel * title = new QLabel( tr( "Directories" ), paths ); + QFont f = title->font(); + f.setBold( true ); + title->setFont( pointSize<12>( f ) ); + + + QScrollArea *pathScroll = new QScrollArea( paths ); + + QWidget *pathSelectors = new QWidget( ws ); + QVBoxLayout *pathSelectorLayout = new QVBoxLayout; + pathScroll->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + pathScroll->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + pathScroll->resize( 362, pathsHeight - 50 ); + pathScroll->move( 0, 30 ); + pathSelectors->resize( 360, pathsHeight - 50 ); + + const int txtLength = 285; + const int btnStart = 305; + // working-dir TabWidget * lmms_wd_tw = new TabWidget( tr( "LMMS working directory" ).toUpper(), - paths ); + pathSelectors ); lmms_wd_tw->setFixedHeight( 48 ); m_wdLineEdit = new QLineEdit( m_workingDir, lmms_wd_tw ); - m_wdLineEdit->setGeometry( 10, 20, 300, 16 ); + m_wdLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_wdLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setWorkingDir( const QString & ) ) ); @@ -394,37 +416,19 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", lmms_wd_tw ); workingdir_select_btn->setFixedSize( 24, 24 ); - workingdir_select_btn->move( 320, 16 ); + workingdir_select_btn->move( btnStart, 16 ); connect( workingdir_select_btn, SIGNAL( clicked() ), this, SLOT( openWorkingDir() ) ); - // vst-dir - TabWidget * vst_tw = new TabWidget( tr( - "VST-plugin directory" ).toUpper(), - paths ); - vst_tw->setFixedHeight( 48 ); - - m_vdLineEdit = new QLineEdit( m_vstDir, vst_tw ); - m_vdLineEdit->setGeometry( 10, 20, 300, 16 ); - connect( m_vdLineEdit, SIGNAL( textChanged( const QString & ) ), this, - SLOT( setVSTDir( const QString & ) ) ); - - QPushButton * vstdir_select_btn = new QPushButton( - embed::getIconPixmap( "project_open", 16, 16 ), - "", vst_tw ); - vstdir_select_btn->setFixedSize( 24, 24 ); - vstdir_select_btn->move( 320, 16 ); - connect( vstdir_select_btn, SIGNAL( clicked() ), this, - SLOT( openVSTDir() ) ); // artwork-dir TabWidget * artwork_tw = new TabWidget( tr( - "Artwork directory" ).toUpper(), - paths ); + "Themes directory" ).toUpper(), + pathSelectors ); artwork_tw->setFixedHeight( 48 ); m_adLineEdit = new QLineEdit( m_artworkDir, artwork_tw ); - m_adLineEdit->setGeometry( 10, 20, 300, 16 ); + m_adLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_adLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setArtworkDir( const QString & ) ) ); @@ -432,7 +436,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", artwork_tw ); artworkdir_select_btn->setFixedSize( 24, 24 ); - artworkdir_select_btn->move( 320, 16 ); + artworkdir_select_btn->move( btnStart, 16 ); connect( artworkdir_select_btn, SIGNAL( clicked() ), this, SLOT( openArtworkDir() ) ); @@ -445,7 +449,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_baLineEdit = new QLineEdit( m_backgroundArtwork, backgroundArtwork_tw ); - m_baLineEdit->setGeometry( 10, 20, 300, 16 ); + m_baLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_baLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setBackgroundArtwork( const QString & ) ) ); @@ -453,7 +457,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", backgroundArtwork_tw ); backgroundartworkdir_select_btn->setFixedSize( 24, 24 ); - backgroundartworkdir_select_btn->move( 320, 16 ); + backgroundartworkdir_select_btn->move( btnStart, 16 ); connect( backgroundartworkdir_select_btn, SIGNAL( clicked() ), this, SLOT( openBackgroundArtwork() ) ); @@ -468,7 +472,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : fl_tw->setFixedHeight( 48 ); m_fdLineEdit = new QLineEdit( m_flDir, fl_tw ); - m_fdLineEdit->setGeometry( 10, 20, 300, 16 ); + m_fdLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_fdLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setFLDir( const QString & ) ) ); @@ -476,28 +480,89 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", fl_tw ); fldir_select_btn->setFixedSize( 24, 24 ); - fldir_select_btn->move( 320, 16 ); + fldir_select_btn->move( btnStart, 16 ); connect( fldir_select_btn, SIGNAL( clicked() ), this, SLOT( openFLDir() ) ); + + // vst-dir + TabWidget * vst_tw = new TabWidget( tr( + "VST-plugin directory" ).toUpper(), + pathSelectors ); + vst_tw->setFixedHeight( 48 ); + + m_vdLineEdit = new QLineEdit( m_vstDir, vst_tw ); + m_vdLineEdit->setGeometry( 10, 20, txtLength, 16 ); + connect( m_vdLineEdit, SIGNAL( textChanged( const QString & ) ), this, + SLOT( setVSTDir( const QString & ) ) ); + + QPushButton * vstdir_select_btn = new QPushButton( + embed::getIconPixmap( "project_open", 16, 16 ), + "", vst_tw ); + vstdir_select_btn->setFixedSize( 24, 24 ); + vstdir_select_btn->move( btnStart, 16 ); + connect( vstdir_select_btn, SIGNAL( clicked() ), this, + SLOT( openVSTDir() ) ); + + // gig-dir + TabWidget * gig_tw = new TabWidget( tr( + "GIG directory" ).toUpper(), + pathSelectors ); + gig_tw->setFixedHeight( 48 ); + + m_gigLineEdit = new QLineEdit( m_gigDir, gig_tw ); + m_gigLineEdit->setGeometry( 10, 20, txtLength, 16 ); + connect( m_gigLineEdit, SIGNAL( textChanged( const QString & ) ), this, + SLOT( setGIGDir( const QString & ) ) ); + + QPushButton * gigdir_select_btn = new QPushButton( + embed::getIconPixmap( "project_open", 16, 16 ), + "", gig_tw ); + gigdir_select_btn->setFixedSize( 24, 24 ); + gigdir_select_btn->move( btnStart, 16 ); + connect( gigdir_select_btn, SIGNAL( clicked() ), this, + SLOT( openGIGDir() ) ); + + // sf2-dir + TabWidget * sf2_tw = new TabWidget( tr( + "SF2 directory" ).toUpper(), + pathSelectors ); + sf2_tw->setFixedHeight( 48 ); + + m_sf2LineEdit = new QLineEdit( m_sf2Dir, sf2_tw ); + m_sf2LineEdit->setGeometry( 10, 20, txtLength, 16 ); + connect( m_sf2LineEdit, SIGNAL( textChanged( const QString & ) ), this, + SLOT( setSF2Dir( const QString & ) ) ); + + QPushButton * sf2dir_select_btn = new QPushButton( + embed::getIconPixmap( "project_open", 16, 16 ), + "", sf2_tw ); + sf2dir_select_btn->setFixedSize( 24, 24 ); + sf2dir_select_btn->move( btnStart, 16 ); + connect( sf2dir_select_btn, SIGNAL( clicked() ), this, + SLOT( openSF2Dir() ) ); + + + // LADSPA-dir TabWidget * lad_tw = new TabWidget( tr( - "LADSPA plugin paths" ).toUpper(), + "LADSPA plugin directories" ).toUpper(), paths ); lad_tw->setFixedHeight( 48 ); m_ladLineEdit = new QLineEdit( m_ladDir, lad_tw ); - m_ladLineEdit->setGeometry( 10, 20, 300, 16 ); + m_ladLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_ladLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setLADSPADir( const QString & ) ) ); QPushButton * laddir_select_btn = new QPushButton( - embed::getIconPixmap( "project_open", 16, 16 ), + embed::getIconPixmap( "add_folder", 16, 16 ), "", lad_tw ); laddir_select_btn->setFixedSize( 24, 24 ); - laddir_select_btn->move( 320, 16 ); + laddir_select_btn->move( btnStart, 16 ); connect( laddir_select_btn, SIGNAL( clicked() ), this, SLOT( openLADSPADir() ) ); + #ifdef LMMS_HAVE_STK // STK-dir TabWidget * stk_tw = new TabWidget( tr( @@ -506,7 +571,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : stk_tw->setFixedHeight( 48 ); m_stkLineEdit = new QLineEdit( m_stkDir, stk_tw ); - m_stkLineEdit->setGeometry( 10, 20, 300, 16 ); + m_stkLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_stkLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setSTKDir( const QString & ) ) ); @@ -514,7 +579,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", stk_tw ); stkdir_select_btn->setFixedSize( 24, 24 ); - stkdir_select_btn->move( 320, 16 ); + stkdir_select_btn->move( btnStart, 16 ); connect( stkdir_select_btn, SIGNAL( clicked() ), this, SLOT( openSTKDir() ) ); #endif @@ -526,7 +591,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : sf_tw->setFixedHeight( 48 ); m_sfLineEdit = new QLineEdit( m_defaultSoundfont, sf_tw ); - m_sfLineEdit->setGeometry( 10, 20, 300, 16 ); + m_sfLineEdit->setGeometry( 10, 20, txtLength, 16 ); connect( m_sfLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( setDefaultSoundfont( const QString & ) ) ); @@ -534,34 +599,42 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed::getIconPixmap( "project_open", 16, 16 ), "", sf_tw ); sf_select_btn->setFixedSize( 24, 24 ); - sf_select_btn->move( 320, 16 ); + sf_select_btn->move( btnStart, 16 ); connect( sf_select_btn, SIGNAL( clicked() ), this, SLOT( openDefaultSoundfont() ) ); #endif + pathSelectors->setLayout( pathSelectorLayout ); - dir_layout->addWidget( lmms_wd_tw ); - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( vst_tw ); - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( artwork_tw ); - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( backgroundArtwork_tw ); - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( fl_tw ); - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( lad_tw ); + pathSelectorLayout->addWidget( lmms_wd_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( gig_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( sf2_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( vst_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( lad_tw ); #ifdef LMMS_HAVE_STK - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( stk_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( stk_tw ); #endif #ifdef LMMS_HAVE_FLUIDSYNTH - dir_layout->addSpacing( 10 ); - dir_layout->addWidget( sf_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( sf_tw ); #endif - dir_layout->addStretch(); + pathSelectorLayout->addWidget( fl_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addWidget( artwork_tw ); + pathSelectorLayout->addSpacing( 10 ); + pathSelectorLayout->addStretch(); + pathSelectorLayout->addWidget( backgroundArtwork_tw ); + pathSelectorLayout->addSpacing( 10 ); + dir_layout->addWidget( pathSelectors ); + pathScroll->setWidget( pathSelectors ); + pathScroll->setWidgetResizable( true ); @@ -905,6 +978,8 @@ void SetupDialog::accept() ConfigManager::inst()->setWorkingDir( m_workingDir ); ConfigManager::inst()->setVSTDir( m_vstDir ); + ConfigManager::inst()->setGIGDir( m_gigDir ); + ConfigManager::inst()->setSF2Dir( m_sf2Dir ); ConfigManager::inst()->setArtworkDir( m_artworkDir ); ConfigManager::inst()->setFLDir( m_flDir ); ConfigManager::inst()->setLADSPADir( m_ladDir ); @@ -1128,6 +1203,28 @@ void SetupDialog::openWorkingDir() } } +void SetupDialog::openGIGDir() +{ + QString new_dir = FileDialog::getExistingDirectory( this, + tr( "Choose your GIG directory" ), + m_gigDir ); + if( new_dir != QString::null ) + { + m_gigLineEdit->setText( new_dir ); + } +} + +void SetupDialog::openSF2Dir() +{ + QString new_dir = FileDialog::getExistingDirectory( this, + tr( "Choose your SF2 directory" ), + m_sf2Dir ); + if( new_dir != QString::null ) + { + m_sf2LineEdit->setText( new_dir ); + } +} + @@ -1158,6 +1255,16 @@ void SetupDialog::setVSTDir( const QString & _vd ) m_vstDir = _vd; } +void SetupDialog::setGIGDir(const QString &_gd) +{ + m_gigDir = _gd; +} + +void SetupDialog::setSF2Dir(const QString &_sfd) +{ + m_sf2Dir = _sfd; +} + @@ -1218,7 +1325,6 @@ void SetupDialog::openLADSPADir() - void SetupDialog::openSTKDir() { #ifdef LMMS_HAVE_STK diff --git a/src/gui/StringPairDrag.cpp b/src/gui/StringPairDrag.cpp index 244362a70..d9e01d065 100644 --- a/src/gui/StringPairDrag.cpp +++ b/src/gui/StringPairDrag.cpp @@ -52,7 +52,7 @@ StringPairDrag::StringPairDrag( const QString & _key, const QString & _value, } QString txt = _key + ":" + _value; QMimeData * m = new QMimeData(); - m->setData( mimeType(), txt.toLatin1() ); + m->setData( mimeType(), txt.toUtf8() ); setMimeData( m ); start( Qt::IgnoreAction ); } @@ -95,7 +95,7 @@ bool StringPairDrag::processDragEnterEvent( QDragEnterEvent * _dee, QString StringPairDrag::decodeMimeKey( const QMimeData * mimeData ) { - return( QString( mimeData->data( mimeType() ) ).section( ':', 0, 0 ) ); + return( QString::fromUtf8( mimeData->data( mimeType() ) ).section( ':', 0, 0 ) ); } @@ -103,7 +103,7 @@ QString StringPairDrag::decodeMimeKey( const QMimeData * mimeData ) QString StringPairDrag::decodeMimeValue( const QMimeData * mimeData ) { - return( QString( mimeData->data( mimeType() ) ).section( ':', 1, -1 ) ); + return( QString::fromUtf8( mimeData->data( mimeType() ) ).section( ':', 1, -1 ) ); } diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index e3471c444..94f6e0de7 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include @@ -157,50 +158,75 @@ void TrackContainerView::removeTrackView( TrackView * _tv ) -void TrackContainerView::moveTrackViewUp( TrackView * _tv ) +void TrackContainerView::moveTrackView( TrackView * trackView, int indexTo ) { - for( int i = 1; i < m_trackViews.size(); ++i ) - { - TrackView * t = m_trackViews[i]; - if( t == _tv ) - { - BBTrack::swapBBTracks( t->getTrack(), - m_trackViews[i - 1]->getTrack() ); - m_scrollLayout->removeWidget( t ); - m_scrollLayout->insertWidget( i - 1, t ); - qSwap( m_tc->m_tracks[i-1], m_tc->m_tracks[i] ); - m_trackViews.swap( i - 1, i ); - realignTracks(); - break; - } - } + // Can't move out of bounds + if ( indexTo >= m_trackViews.size() || indexTo < 0 ) { return; } + + // Does not need to move to itself + int indexFrom = m_trackViews.indexOf( trackView ); + if ( indexFrom == indexTo ) { return; } + + BBTrack::swapBBTracks( trackView->getTrack(), + m_trackViews[indexTo]->getTrack() ); + + m_scrollLayout->removeWidget( trackView ); + m_scrollLayout->insertWidget( indexTo, trackView ); + + Track * track = m_tc->m_tracks[indexFrom]; + + m_tc->m_tracks.remove( indexFrom ); + m_tc->m_tracks.insert( indexTo, track ); + m_trackViews.move( indexFrom, indexTo ); + + realignTracks(); } -void TrackContainerView::moveTrackViewDown( TrackView * _tv ) +void TrackContainerView::moveTrackViewUp( TrackView * trackView ) { - for( int i = 0; i < m_trackViews.size()-1; ++i ) - { - TrackView * t = m_trackViews[i]; - if( t == _tv ) - { - BBTrack::swapBBTracks( t->getTrack(), - m_trackViews[i + 1]->getTrack() ); - m_scrollLayout->removeWidget( t ); - m_scrollLayout->insertWidget( i + 1, t ); - qSwap( m_tc->m_tracks[i], m_tc->m_tracks[i+1] ); - m_trackViews.swap( i, i + 1 ); - realignTracks(); - break; - } - } + int index = m_trackViews.indexOf( trackView ); + + moveTrackView( trackView, index - 1 ); } +void TrackContainerView::moveTrackViewDown( TrackView * trackView ) +{ + int index = m_trackViews.indexOf( trackView ); + + moveTrackView( trackView, index + 1 ); +} + +void TrackContainerView::scrollToTrackView( TrackView * _tv ) +{ + if (!m_trackViews.contains(_tv)) + { + qWarning("TrackContainerView::scrollToTrackView: TrackView is not owned by this"); + } + else + { + int currentScrollTop = m_scrollArea->verticalScrollBar()->value(); + int scrollAreaHeight = m_scrollArea->size().height(); + int trackViewTop = _tv->pos().y(); + int trackViewBottom = trackViewTop + _tv->size().height(); + + // displayed_location = widget_location - currentScrollTop + // want to make sure that the widget top has displayed location > 0, + // and widget bottom < scrollAreaHeight + // trackViewTop - scrollY > 0 && trackViewBottom - scrollY < scrollAreaHeight + // therefore scrollY < trackViewTop && scrollY > trackViewBottom - scrollAreaHeight + int newScroll = std::max( trackViewBottom-scrollAreaHeight, std::min(currentScrollTop, trackViewTop) ); + m_scrollArea->verticalScrollBar()->setValue(newScroll); + } +} + + + void TrackContainerView::realignTracks() { @@ -220,11 +246,18 @@ void TrackContainerView::realignTracks() -void TrackContainerView::createTrackView( Track * _t ) +TrackView * TrackContainerView::createTrackView( Track * _t ) { //m_tc->addJournalCheckPoint(); - _t->createView( this ); + // Avoid duplicating track views + for( trackViewList::iterator it = m_trackViews.begin(); + it != m_trackViews.end(); ++it ) + { + if ( ( *it )->getTrack() == _t ) { return ( *it ); } + } + + return _t->createView( this ); } diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 3146d49b1..f51e7bce9 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -624,11 +624,6 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) float level = getLevel( mouseEvent->y() ); int x = mouseEvent->x(); - if( mouseEvent->x() <= VALUES_WIDTH ) - { - update(); - return; - } x -= VALUES_WIDTH; if( m_action == MOVE_VALUE ) { @@ -699,12 +694,12 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) { if( QApplication::overrideCursor() ) { - if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor ) + if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor ) { - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } QCursor c( Qt::SizeAllCursor ); QApplication::setOverrideCursor( @@ -2304,7 +2299,15 @@ void AutomationEditorWindow::dropEvent( QDropEvent *_de ) journallingObject( val.toInt() ) ); if( mod != NULL ) { - m_editor->m_pattern->addObject( mod ); + bool added = m_editor->m_pattern->addObject( mod ); + if ( !added ) + { + TextFloat::displayMessage( mod->displayName(), + tr( "Model is already connected " + "to this pattern." ), + embed::getIconPixmap( "automation" ), + 2000 ); + } setCurrentPattern( m_editor->m_pattern ); } } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 5a6805657..1ca1a51ca 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1081,7 +1081,16 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke ) if( ke->modifiers() & Qt::ControlModifier ) { ke->accept(); - selectAll(); + if (ke->modifiers() & Qt::ShiftModifier) + { + // Ctrl + Shift + A = deselect all notes + clearSelectedNotes(); + } + else + { + // Ctrl + A = select all notes + selectAll(); + } update(); } break; @@ -1848,54 +1857,50 @@ void PianoRoll::computeSelectedNotes(bool shift) -void PianoRoll::mouseReleaseEvent(QMouseEvent * me ) +void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) { - s_textFloat->hide(); bool mustRepaint = false; + s_textFloat->hide(); + if( me->button() & Qt::LeftButton ) { m_mouseDownLeft = false; mustRepaint = true; + + if( m_action == ActionSelectNotes && m_editMode == ModeSelect ) + { + // select the notes within the selection rectangle and + // then destroy the selection rectangle + computeSelectedNotes( + me->modifiers() & Qt::ShiftModifier ); + } + else if( m_action == ActionMoveNote ) + { + // we moved one or more notes so they have to be + // moved properly according to new starting- + // time in the note-array of pattern + m_pattern->rearrangeAllNotes(); + + } + + if( m_action == ActionMoveNote || m_action == ActionResizeNote ) + { + // if we only moved one note, deselect it so we can + // edit the notes in the note edit area + if( selectionCount() == 1 ) + { + clearSelectedNotes(); + } + } } + if( me->button() & Qt::RightButton ) { m_mouseDownRight = false; mustRepaint = true; } - if( me->button() & Qt::LeftButton && - m_editMode == ModeSelect && - m_action == ActionSelectNotes ) - { - // select the notes within the selection rectangle and - // then destroy the selection rectangle - - computeSelectedNotes( me->modifiers() & Qt::ShiftModifier ); - - } - else if( me->button() & Qt::LeftButton && - m_action == ActionMoveNote ) - { - // we moved one or more notes so they have to be - // moved properly according to new starting- - // time in the note-array of pattern - - m_pattern->rearrangeAllNotes(); - - } - if( me->button() & Qt::LeftButton && - ( m_action == ActionMoveNote || m_action == ActionResizeNote ) ) - { - // if we only moved one note, deselect it so we can - // edit the notes in the note edit area - if( selectionCount() == 1 ) - { - clearSelectedNotes(); - } - } - - if( hasValidPattern() ) { // turn off all notes that are playing @@ -1907,7 +1912,8 @@ void PianoRoll::mouseReleaseEvent(QMouseEvent * me ) Note *note = *it; if( note->isPlaying() ) { - m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( note->key() ); + m_pattern->instrumentTrack()->pianoModel()-> + handleKeyRelease( note->key() ); note->setIsPlaying( false ); } @@ -1915,11 +1921,11 @@ void PianoRoll::mouseReleaseEvent(QMouseEvent * me ) } // stop playing keys that we let go of - m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( m_lastKey ); + m_pattern->instrumentTrack()->pianoModel()-> + handleKeyRelease( m_lastKey ); } m_currentNote = NULL; - m_action = ActionNone; if( m_editMode == ModeDraw ) @@ -2092,7 +2098,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) if ( isUnderPosition ) { testPlayNote( n ); } // If note is the one under the cursor or is selected when alt is // not pressed - if ( isUnderPosition || ( n->selected() && !altPressed ) ) + if ( ( isUnderPosition && !isSelection() ) || ( n->selected() && !altPressed ) ) { if( m_noteEditMode == NoteEditVolume ) { @@ -2428,7 +2434,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) { if( m_moveBoundaryLeft + off_ticks < 0 ) { - off_ticks += 0 - (off_ticks + m_moveBoundaryLeft); + off_ticks -= (off_ticks + m_moveBoundaryLeft); } if( m_moveBoundaryTop + off_key > NumKeys ) { @@ -2436,92 +2442,161 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) } if( m_moveBoundaryBottom + off_key < 0 ) { - off_key += 0 - (m_moveBoundaryBottom + off_key); + off_key -= (m_moveBoundaryBottom + off_key); } } - int shift_offset = 0; - int shift_ref_pos = -1; // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); - // will be our iterator in the following loop - NoteVector::ConstIterator it = notes.begin(); - - int sNotes = selectionCount(); - while( it != notes.end() ) + if (m_action == ActionMoveNote) { - Note *note = *it; - const int pos = note->pos().getTicks(); + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) + { + Note *note = *it; + if( note->selected() ) + { + if( ! ( shift && ! m_startedWithShift ) ) + { + // moving note + int pos_ticks = note->oldPos().getTicks() + off_ticks; + int key_num = note->oldKey() + off_key; + // ticks can't be negative + pos_ticks = qMax(0, pos_ticks); + // upper/lower bound checks on key_num + key_num = qMax(0, key_num); + key_num = qMin(key_num, NumKeys); + + note->setPos( MidiTime( pos_ticks ) ); + note->setKey( key_num ); + } + else if( shift && ! m_startedWithShift ) + { + // quick resize, toggled by holding shift after starting a note move, but not before + int ticks_new = note->oldLength().getTicks() + off_ticks; + if( ticks_new <= 0 ) + { + ticks_new = 1; + } + note->setLength( MidiTime( ticks_new ) ); + m_lenOfNewNotes = note->length(); + } + } + } + } + else if (m_action == ActionResizeNote) + { // When resizing notes: + // If shift is not pressed, resize the selected notes but do not rearrange them // If shift is pressed we resize and rearrange only the selected notes // If shift + ctrl then we also rearrange all posterior notes (sticky) // If shift is pressed but only one note is selected, apply sticky - if( m_action == ActionResizeNote && shift && - ( note->selected() || ctrl || sNotes == 1 ) ) + + if (shift) { - int shifted_pos = note->oldPos().getTicks() + shift_offset; - if( shifted_pos && pos == shift_ref_pos ) + // Algorithm: + // Relative to the starting point of the left-most selected note, + // all selected note start-points and *endpoints* (not length) should be scaled by a calculated factor. + // This factor is such that the endpoint of the note whose handle is being dragged should lie under the cursor. + // first, determine the start-point of the left-most selected note: + int stretchStartTick = -1; + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) { - shifted_pos -= off_ticks; - } - note->setPos( MidiTime( shifted_pos ) ); - } - - if( note->selected() ) - { - if( m_action == ActionMoveNote && ! ( shift && ! m_startedWithShift ) ) - { - // moving note - int pos_ticks = note->oldPos().getTicks() + off_ticks; - int key_num = note->oldKey() + off_key; - - // ticks can't be negative - pos_ticks = qMax(0, pos_ticks); - // upper/lower bound checks on key_num - key_num = qMax(0, key_num); - key_num = qMin(key_num, NumKeys); - - note->setPos( MidiTime( pos_ticks ) ); - note->setKey( key_num ); - } - else if( m_action == ActionResizeNote ) - { - // resizing note - int ticks_new = note->oldLength().getTicks() + off_ticks; - if( ticks_new <= 0 ) + Note *note = *it; + if (note->selected() && (stretchStartTick < 0 || note->oldPos().getTicks() < stretchStartTick)) { - ticks_new = 1; + stretchStartTick = note->oldPos().getTicks(); } - else if( shift ) + } + // determine the ending tick of the right-most selected note + Note *posteriorNote = nullptr; + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) + { + Note *note = *it; + if (note->selected() && (posteriorNote == nullptr || + note->oldPos().getTicks() + note->oldLength().getTicks() > + posteriorNote->oldPos().getTicks() + posteriorNote->oldLength().getTicks())) { - // when holding shift: update the offset used to shift - // the following notes - if( pos > shift_ref_pos ) + posteriorNote = note; + } + } + int posteriorEndTick = posteriorNote->pos().getTicks() + posteriorNote->length().getTicks(); + // end-point of the note whose handle is being dragged: + int stretchEndTick = m_currentNote->oldPos().getTicks() + m_currentNote->oldLength().getTicks(); + // Calculate factor by which to scale the start-point and end-point of all selected notes + float scaleFactor = (float)(stretchEndTick - stretchStartTick + off_ticks) / qMax(1, stretchEndTick - stretchStartTick); + scaleFactor = qMax(0.0f, scaleFactor); + + // process all selected notes & determine how much the endpoint of the right-most note was shifted + int posteriorDeltaThisFrame = 0; + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) + { + Note *note = *it; + if(note->selected()) + { + // scale relative start and end positions by scaleFactor + int newStart = stretchStartTick + scaleFactor * + (note->oldPos().getTicks() - stretchStartTick); + int newEnd = stretchStartTick + scaleFactor * + (note->oldPos().getTicks()+note->oldLength().getTicks() - stretchStartTick); + // if not holding alt, quantize the offsets + if(!alt) { - shift_offset += off_ticks; - shift_ref_pos = pos; + // quantize start time + int oldStart = note->oldPos().getTicks(); + int startDiff = newStart - oldStart; + startDiff = floor(startDiff / quantization()) * quantization(); + newStart = oldStart + startDiff; + // quantize end time + int oldEnd = oldStart + note->oldLength().getTicks(); + int endDiff = newEnd - oldEnd; + endDiff = floor(endDiff / quantization()) * quantization(); + newEnd = oldEnd + endDiff; + } + int newLength = qMax(1, newEnd-newStart); + if (note == posteriorNote) + { + posteriorDeltaThisFrame = (newStart+newLength) - + (note->pos().getTicks() + note->length().getTicks()); + } + note->setLength( MidiTime(newLength) ); + note->setPos( MidiTime(newStart) ); + + m_lenOfNewNotes = note->length(); + } + } + if (ctrl || selectionCount() == 1) + { + // if holding ctrl or only one note is selected, reposition posterior notes + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) + { + Note *note = *it; + if (!note->selected() && note->pos().getTicks() >= posteriorEndTick) + { + int newStart = note->pos().getTicks() + posteriorDeltaThisFrame; + note->setPos( MidiTime(newStart) ); } } - note->setLength( MidiTime( ticks_new ) ); - - m_lenOfNewNotes = note->length(); - } - else if( m_action == ActionMoveNote && ( shift && ! m_startedWithShift ) ) - { - // quick resize, toggled by holding shift after starting a note move, but not before - int ticks_new = note->oldLength().getTicks() + off_ticks; - if( ticks_new <= 0 ) - { - ticks_new = 1; - } - note->setLength( MidiTime( ticks_new ) ); - m_lenOfNewNotes = note->length(); } } - ++it; + else + { + // shift is not pressed; stretch length of selected notes but not their position + for (NoteVector::ConstIterator it = notes.begin(); it != notes.end(); ++it ) + { + Note *note = *it; + if (note->selected()) + { + int newLength = note->oldLength() + off_ticks; + newLength = qMax(1, newLength); + note->setLength( MidiTime(newLength) ); + + m_lenOfNewNotes = note->length(); + } + } + } } m_pattern->dataChanged(); @@ -4230,3 +4305,9 @@ QSize PianoRollWindow::sizeHint() const { return {m_toolBar->sizeHint().width() + 10, INITIAL_PIANOROLL_HEIGHT}; } + +void PianoRollWindow::focusInEvent(QFocusEvent * event) +{ + // when the window is given focus, also give focus to the actual piano roll + m_editor->setFocus(event->reason()); +} diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 0f0bef69d..d8055f5ac 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -67,7 +67,6 @@ QPixmap * Fader::s_knob = NULL; Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : QWidget( _parent ), FloatModelView( _model, this ), - m_model( _model ), m_fPeakValue_L( 0.0 ), m_fPeakValue_R( 0.0 ), m_persistentPeak_L( 0.0 ), @@ -114,7 +113,6 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixmap * back, QPixmap * leds, QPixmap * knob ) : QWidget( parent ), FloatModelView( model, this ), - m_model( model ), m_fPeakValue_L( 0.0 ), m_fPeakValue_R( 0.0 ), m_persistentPeak_L( 0.0 ), @@ -170,7 +168,7 @@ void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) { int dy = m_moveStartPoint - mouseEvent->globalY(); - float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *m_knob ).height() ); + float delta = dy * ( model()->maxValue() - model()->minValue() ) / (float) ( height() - ( *m_knob ).height() ); model()->setValue( m_startValue + delta ); @@ -256,11 +254,11 @@ void Fader::wheelEvent ( QWheelEvent *ev ) if ( ev->delta() > 0 ) { - m_model->incValue( 1 ); + model()->incValue( 1 ); } else { - m_model->incValue( -1 ); + model()->incValue( -1 ); } updateTextFloat(); s_textFloat->setVisibilityTimeOut( 1000 ); @@ -326,7 +324,7 @@ void Fader::updateTextFloat() } else { - s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? m_model->value() * 100 : m_model->value() ) + " " + m_unit ); + s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? model()->value() * 100 : model()->value() ) + " " + m_unit ); } s_textFloat->moveGlobal( this, QPoint( width() - ( *m_knob ).width() - 5, knobPosY() - 46 ) ); } diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 75732679d..7c6b26c36 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -65,7 +65,8 @@ TextFloat * Knob::s_textFloat = NULL; m_volumeKnob( false ), \ m_volumeRatio( 100.0, 0.0, 1000000.0 ), \ m_buttonPressed( false ), \ - m_angle( -10 ) + m_angle( -10 ), \ + m_lineWidth(0) Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) : DEFAULT_KNOB_INITIALIZER_LIST, diff --git a/src/gui/widgets/LeftRightNav.cpp b/src/gui/widgets/LeftRightNav.cpp new file mode 100644 index 000000000..530d128d7 --- /dev/null +++ b/src/gui/widgets/LeftRightNav.cpp @@ -0,0 +1,91 @@ +/* + * LeftRightNav.cpp - side-by-side left-facing and right-facing arrows for navigation (looks like < > ) + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "LeftRightNav.h" +#include "ToolTip.h" +#include "embed.h" + + +LeftRightNav::LeftRightNav(QWidget *parent) + : QWidget(parent), + m_layout(this), + m_leftBtn(this, tr("Previous")), + m_rightBtn(this, tr("Next")) +{ + m_layout.setContentsMargins(0, 0, 0, 0); + m_layout.setSpacing(2); + + m_leftBtn.setCheckable(false); + m_rightBtn.setCheckable(false); + + m_leftBtn.setCursor(Qt::PointingHandCursor); + m_rightBtn.setCursor(Qt::PointingHandCursor); + + m_leftBtn.setActiveGraphic(embed::getIconPixmap( + "stepper-left-press")); + m_rightBtn.setActiveGraphic(embed::getIconPixmap( + "stepper-right-press" )); + + m_leftBtn.setInactiveGraphic(embed::getIconPixmap( + "stepper-left" )); + m_rightBtn.setInactiveGraphic(embed::getIconPixmap( + "stepper-right")); + + connect(&m_leftBtn, SIGNAL(clicked()), this, + SIGNAL(onNavLeft())); + connect(&m_rightBtn, SIGNAL(clicked()), this, + SIGNAL(onNavRight())); + + ToolTip::add(&m_leftBtn, tr("Previous")); + ToolTip::add(&m_rightBtn, tr("Next")); + + m_leftBtn.setWindowTitle(tr("Previous")); + m_rightBtn.setWindowTitle(tr("Next")); + + // AutomatableButton's right click menu (contains irrelevant options like copying and pasting values) + m_leftBtn.setContextMenuPolicy(Qt::NoContextMenu); + m_rightBtn.setContextMenuPolicy(Qt::NoContextMenu); + + m_layout.addWidget(&m_leftBtn); + m_layout.addWidget(&m_rightBtn); +} + +PixmapButton* LeftRightNav::getLeftBtn() +{ + return &m_leftBtn; +} +PixmapButton* LeftRightNav::getRightBtn() +{ + return &m_rightBtn; +} + +void LeftRightNav::setShortcuts(const QKeySequence &leftShortcut, const QKeySequence &rightShortcut) +{ + m_leftBtn.setShortcut(leftShortcut); + m_rightBtn.setShortcut(rightShortcut); + + ToolTip::add(&m_leftBtn, tr("Previous (%1)").arg(leftShortcut.toString())); + ToolTip::add(&m_rightBtn, tr("Next (%1)").arg(rightShortcut.toString())); +} \ No newline at end of file diff --git a/src/gui/widgets/PixmapButton.cpp b/src/gui/widgets/PixmapButton.cpp index e647c5e23..44e957eef 100644 --- a/src/gui/widgets/PixmapButton.cpp +++ b/src/gui/widgets/PixmapButton.cpp @@ -130,7 +130,17 @@ void PixmapButton::setInactiveGraphic( const QPixmap & _pm, bool _update ) } } - +QSize PixmapButton::sizeHint() const +{ + if( ( model() != NULL && model()->value() ) || m_pressed ) + { + return m_activePixmap.size(); + } + else + { + return m_inactivePixmap.size(); + } +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index d7e53d137..eb6a4a2b7 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -66,6 +66,7 @@ #include "Knob.h" #include "LcdSpinBox.h" #include "LedCheckbox.h" +#include "LeftRightNav.h" #include "MainWindow.h" #include "MidiClient.h" #include "MidiPortMenu.h" @@ -80,6 +81,7 @@ #include "StringPairDrag.h" #include "TabWidget.h" #include "ToolTip.h" +#include "TrackContainerView.h" #include "TrackLabelButton.h" #include "ValueBuffer.h" #include "volume.h" @@ -1280,13 +1282,36 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : generalSettingsLayout->setContentsMargins( 8, 18, 8, 8 ); generalSettingsLayout->setSpacing( 6 ); + QWidget* nameAndChangeTrackWidget = new QWidget( generalSettingsWidget ); + QHBoxLayout* nameAndChangeTrackLayout = new QHBoxLayout( nameAndChangeTrackWidget ); + nameAndChangeTrackLayout->setContentsMargins( 0, 0, 0, 0 ); + nameAndChangeTrackLayout->setSpacing( 2 ); + // setup line edit for changing instrument track name m_nameLineEdit = new QLineEdit; m_nameLineEdit->setFont( pointSize<9>( m_nameLineEdit->font() ) ); connect( m_nameLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( textChanged( const QString & ) ) ); - generalSettingsLayout->addWidget( m_nameLineEdit ); + m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + nameAndChangeTrackLayout->addWidget(m_nameLineEdit); + + + // set up left/right arrows for changing instrument + m_leftRightNav = new LeftRightNav(this); + connect( m_leftRightNav, SIGNAL( onNavLeft() ), this, + SLOT( viewPrevInstrument() ) ); + connect( m_leftRightNav, SIGNAL( onNavRight() ), this, + SLOT( viewNextInstrument() ) ); + m_leftRightNav->setWhatsThis( + tr( "Use these controls to view and edit the next/previous track in the song editor." ) ); + // m_leftRightNav->setShortcuts(); + nameAndChangeTrackLayout->addWidget(m_leftRightNav); + + + generalSettingsLayout->addWidget( nameAndChangeTrackWidget ); + + QHBoxLayout* basicControlsLayout = new QHBoxLayout; basicControlsLayout->setSpacing( 3 ); @@ -1693,3 +1718,61 @@ void InstrumentTrackWindow::loadSettings( const QDomElement& thisElement ) m_itv->m_tlb->setChecked( true ); } } + +void InstrumentTrackWindow::viewInstrumentInDirection(int d) +{ + // helper routine for viewNextInstrument, viewPrevInstrument + // d=-1 to view the previous instrument, + // d=+1 to view the next instrument + + const QList &trackViews = m_itv->trackContainerView()->trackViews(); + int idxOfMe = trackViews.indexOf(m_itv); + + // search for the next InstrumentTrackView (i.e. skip AutomationViews, etc) + // sometimes, the next InstrumentTrackView may already be open, in which case + // replace our window contents with the *next* closed Instrument Track and + // give focus to the InstrumentTrackView we skipped. + int idxOfNext = idxOfMe; + InstrumentTrackView *newView = nullptr; + InstrumentTrackView *bringToFront = nullptr; + do + { + idxOfNext = (idxOfNext + d + trackViews.size()) % trackViews.size(); + newView = dynamic_cast(trackViews[idxOfNext]); + // the window that should be brought to focus is the FIRST InstrumentTrackView that comes after us + if (bringToFront == nullptr && newView != nullptr) + { + bringToFront = newView; + } + // if the next instrument doesn't have an active window, then exit loop & load that one into our window. + if (newView != nullptr && !newView->m_tlb->isChecked()) + { + break; + } + } while (idxOfNext != idxOfMe); + + // avoid reloading the window if there is only one instrument, as that will just change the active tab + if (idxOfNext != idxOfMe) + { + // save current window pos and then hide the window by unchecking its button in the track list + QPoint curPos = parentWidget()->pos(); + m_itv->m_tlb->setChecked(false); + + // enable the new window by checking its track list button & moving it to where our window just was + newView->m_tlb->setChecked(true); + newView->getInstrumentTrackWindow()->parentWidget()->move(curPos); + + // scroll the SongEditor/BB-editor to make sure the new trackview label is visible + bringToFront->trackContainerView()->scrollToTrackView(bringToFront); + } + bringToFront->getInstrumentTrackWindow()->setFocus(); +} + +void InstrumentTrackWindow::viewNextInstrument() +{ + viewInstrumentInDirection(+1); +} +void InstrumentTrackWindow::viewPrevInstrument() +{ + viewInstrumentInDirection(-1); +} \ No newline at end of file