diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 41ee9d58b..8abf83c24 100755 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -2,7 +2,7 @@ set -e -PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev +PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" diff --git a/.travis/linux.win32.install.sh b/.travis/linux.win32.install.sh index cf60630c8..4c35555e3 100755 --- a/.travis/linux.win32.install.sh +++ b/.travis/linux.win32.install.sh @@ -12,7 +12,7 @@ MINGW_PACKAGES="mingw32-x-sdl mingw32-x-libvorbis mingw32-x-fluidsynth mingw32-x mingw32-x-glib2 mingw32-x-portaudio mingw32-x-libsndfile mingw32-x-fftw mingw32-x-flac mingw32-x-fltk mingw32-x-libsamplerate mingw32-x-pkgconfig mingw32-x-binutils mingw32-x-gcc mingw32-x-runtime - mingw32-x-libgig mingw32-x-libsoundio $MINGW_PACKAGES" + mingw32-x-libgig mingw32-x-libsoundio mingw32-x-lame $MINGW_PACKAGES" export MINGW_PACKAGES diff --git a/.travis/linux.win64.install.sh b/.travis/linux.win64.install.sh index 66ca0d16f..2d03e0320 100755 --- a/.travis/linux.win64.install.sh +++ b/.travis/linux.win64.install.sh @@ -15,7 +15,7 @@ MINGW_PACKAGES="mingw64-x-sdl mingw64-x-libvorbis mingw64-x-fluidsynth mingw64-x mingw64-x-glib2 mingw64-x-portaudio mingw64-x-libsndfile mingw64-x-fftw mingw64-x-flac mingw64-x-fltk mingw64-x-libsamplerate mingw64-x-pkgconfig mingw64-x-binutils mingw64-x-gcc mingw64-x-runtime - mingw64-x-libgig mingw64-x-libsoundio $MINGW_PACKAGES" + mingw64-x-libgig mingw64-x-libsoundio mingw64-x-lame $MINGW_PACKAGES" export MINGW_PACKAGES diff --git a/.travis/osx..install.sh b/.travis/osx..install.sh index 74e89c03c..e344ffcd9 100755 --- a/.travis/osx..install.sh +++ b/.travis/osx..install.sh @@ -2,7 +2,7 @@ set -e -PACKAGES="cmake pkg-config fftw libogg libvorbis libsndfile libsamplerate jack sdl stk portaudio node fltk" +PACKAGES="cmake pkg-config fftw libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk portaudio node fltk" if [ "$QT5" ]; then PACKAGES="$PACKAGES qt@5.5" diff --git a/CMakeLists.txt b/CMakeLists.txt index ecc9c4944..e4f6782ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) IF (CMAKE_MAJOR_VERSION GREATER 2) CMAKE_POLICY(SET CMP0026 OLD) + CMAKE_POLICY(SET CMP0050 OLD) ENDIF() ENDIF(COMMAND CMAKE_POLICY) @@ -29,7 +30,7 @@ SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") SET(VERSION_MINOR "2") SET(VERSION_RELEASE "0") -SET(VERSION_STAGE "rc2") +SET(VERSION_STAGE "rc3") SET(VERSION_BUILD "0") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") IF(VERSION_STAGE) @@ -50,6 +51,7 @@ OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON) OPTION(WANT_CARLA "Include Carla plugin" ON) OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON) OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON) +OPTION(WANT_MP3LAME "Include MP3/Lame support" ON) OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON) OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON) OPTION(WANT_PORTAUDIO "Include PortAudio support" ON) @@ -306,6 +308,21 @@ IF(NOT LMMS_HAVE_PULSEAUDIO) ENDIF(NOT LMMS_HAVE_PULSEAUDIO) +# check for MP3/Lame-libraries +IF(WANT_MP3LAME) + FIND_PACKAGE(Lame) + IF(LAME_FOUND) + SET(LMMS_HAVE_MP3LAME TRUE) + SET(STATUS_MP3LAME "OK") + ELSE(LAME_FOUND) + SET(STATUS_MP3LAME "not found, please install libmp3lame-dev (or similar)") + SET(LAME_LIBRARIES "") + SET(LAME_INCLUDE_DIRS "") + ENDIF(LAME_FOUND) +ELSE(WANT_MP3LAME) + SET(STATUS_MP3LAME "Disabled for build") +ENDIF(WANT_MP3LAME) + # check for OGG/Vorbis-libraries IF(WANT_OGGVORBIS) FIND_PACKAGE(OggVorbis) @@ -593,6 +610,7 @@ MESSAGE( "-----------------------------------------\n" "* WAVE : OK\n" "* OGG/VORBIS : ${STATUS_OGGVORBIS}\n" +"* MP3/Lame : ${STATUS_MP3LAME}\n" ) MESSAGE( diff --git a/README.md b/README.md index de99e5ec1..661b13121 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build status](https://img.shields.io/travis/LMMS/lmms.svg?maxAge=3600)](https://travis-ci.org/LMMS/lmms) [![Latest stable release](https://img.shields.io/github/release/LMMS/lmms.svg?maxAge=3600)](https://lmms.io/download) [![Overall downloads on Github](https://img.shields.io/github/downloads/LMMS/lmms/total.svg?maxAge=3600)](https://github.com/LMMS/lmms/releases) -[![Join the chat at Discord](https://img.shields.io/badge/chat-on%20discord-7289DA.svg)](https://discord.gg/5kSc32Z) +[![Join the chat at Discord](https://img.shields.io/badge/chat-on%20discord-7289DA.svg)](https://discord.gg/3sc5su7) [![Localise on transifex](https://img.shields.io/badge/localise-on_transifex-green.svg)](https://www.transifex.com/lmms/lmms/) What is LMMS? diff --git a/cmake/modules/FindLame.cmake b/cmake/modules/FindLame.cmake new file mode 100644 index 000000000..e5dc93bd5 --- /dev/null +++ b/cmake/modules/FindLame.cmake @@ -0,0 +1,16 @@ +# - Try to find LAME +# Once done this will define +# +# LAME_FOUND - system has liblame +# LAME_INCLUDE_DIRS - the liblame include directory +# LAME_LIBRARIES - The liblame libraries + +find_path(LAME_INCLUDE_DIRS lame/lame.h) +find_library(LAME_LIBRARIES mp3lame) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Lame DEFAULT_MSG LAME_INCLUDE_DIRS LAME_LIBRARIES) + +list(APPEND LAME_DEFINITIONS -DHAVE_LIBMP3LAME=1) + +mark_as_advanced(LAME_INCLUDE_DIRS LAME_LIBRARIES LAME_DEFINITIONS) diff --git a/cmake/nsis/CMakeLists.txt b/cmake/nsis/CMakeLists.txt index 8ddd28eca..b21c920e5 100644 --- a/cmake/nsis/CMakeLists.txt +++ b/cmake/nsis/CMakeLists.txt @@ -7,11 +7,17 @@ SET(CPACK_NSIS_URL_INFO_ABOUT "${PROJECT_URL}" PARENT_SCOPE) SET(CPACK_NSIS_CONTACT "${PROJECT_EMAIL}" PARENT_SCOPE) SET(CPACK_PACKAGE_EXECUTABLES "${CMAKE_PROJECT_NAME}.exe;${PROJECT_NAME_UCASE}" PARENT_SCOPE) SET(CPACK_NSIS_MENU_LINKS "${CMAKE_PROJECT_NAME}.exe;${PROJECT_NAME_UCASE}" PARENT_SCOPE) -SET(CPACK_NSIS_DEFINES "!include ${CMAKE_SOURCE_DIR}/cmake/nsis/FileAssociation.nsh") +SET(CPACK_NSIS_DEFINES " + !include ${CMAKE_SOURCE_DIR}/cmake/nsis/FileAssociation.nsh + !include LogicLib.nsh + !include WinVer.nsh") SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-win32") -SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " +SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " \\\${registerExtension} \\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" \\\".mmp\\\" \\\"${PROJECT_NAME_UCASE} Project\\\" \\\${registerExtension} \\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" \\\".mmpz\\\" \\\"${PROJECT_NAME_UCASE} Project (compressed)\\\" + \\\${IfNot} \\\${AtMostWin7} + WriteRegDWORD HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\SideBySide\\\" \\\"PreferExternalManifest\\\" \\\"1\\\" + \\\${EndIf} " PARENT_SCOPE) SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " \\\${unregisterExtension} \\\".mmp\\\" \\\"${PROJECT_NAME_UCASE} Project\\\" @@ -64,3 +70,4 @@ IF(LMMS_HAVE_STK) INSTALL(FILES ${RAWWAVES} DESTINATION "${DATA_DIR}/stk/rawwaves") ENDIF() +INSTALL(FILES "lmms.exe.manifest" DESTINATION .) diff --git a/cmake/nsis/lmms.exe.manifest b/cmake/nsis/lmms.exe.manifest new file mode 100644 index 000000000..1d1a5eb93 --- /dev/null +++ b/cmake/nsis/lmms.exe.manifest @@ -0,0 +1,8 @@ + + + + + false + + + diff --git a/data/locale/CMakeLists.txt b/data/locale/CMakeLists.txt index 25de01e24..4de6d5ff7 100644 --- a/data/locale/CMakeLists.txt +++ b/data/locale/CMakeLists.txt @@ -31,7 +31,7 @@ FOREACH(_ts_file ${lmms_LOCALES}) COMMAND "${QT_LUPDATE_EXECUTABLE}" -locations none -no-obsolete -I ${CMAKE_SOURCE_DIR}/include/ ${LMMS_SRCS} ${LMMS_INCLUDES} ${LMMS_UIS} `find "\"${CMAKE_SOURCE_DIR}/plugins/\"" -type f -name '*.cpp' -or -name '*.h'` -ts "\"${_ts_file}\"" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) ADD_CUSTOM_TARGET(${_qm_target} - COMMAND "${QT_LRELEASE_EXECUTABLE}" "\"${_ts_file}\"" -qm "\"${_qm_file}\"" + COMMAND "${QT_LRELEASE_EXECUTABLE}" "${_ts_file}" -qm "${_qm_file}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) LIST(APPEND ts_targets "${_ts_target}") LIST(APPEND qm_targets "${_qm_target}") diff --git a/data/locale/en.ts b/data/locale/en.ts index d72aa2ce8..49b55bd88 100644 --- a/data/locale/en.ts +++ b/data/locale/en.ts @@ -721,10 +721,6 @@ If you're interested in translating LMMS in another language or want to imp Input Gain: - - NOIS - - Input Noise: @@ -741,10 +737,6 @@ If you're interested in translating LMMS in another language or want to imp Output Clip: - - Rate - - Rate Enabled @@ -753,10 +745,6 @@ If you're interested in translating LMMS in another language or want to imp Enable samplerate-crushing - - Depth - - Depth Enabled @@ -769,20 +757,28 @@ If you're interested in translating LMMS in another language or want to imp Sample rate: - - STD - - Stereo difference: - Levels + Levels: - Levels: + NOISE + + + + FREQ + + + + STEREO + + + + QUANT @@ -2170,10 +2166,6 @@ Please make sure you have write permission to the file and the directory contain RATE - - Rate: - - AMNT @@ -2194,6 +2186,10 @@ Please make sure you have write permission to the file and the directory contain Invert + + Period: + + FxLine @@ -3370,22 +3366,10 @@ You can remove and move FX channels in the context menu, which is accessed by ri FX channel - - ENV/LFO - - - - FUNC - - FX - - MIDI - - Save preset @@ -3394,10 +3378,6 @@ You can remove and move FX channels in the context menu, which is accessed by ri XML preset file (*.xpf) - - PLUGIN - - Pitch range (semitones) @@ -3414,10 +3394,6 @@ You can remove and move FX channels in the context menu, which is accessed by ri Click here, if you want to save current instrument track settings in a preset file. Later you can load this preset by double-clicking it in the preset-browser. - - MISC - - Use these controls to view and edit the next/previous track in the song editor. @@ -3426,6 +3402,30 @@ You can remove and move FX channels in the context menu, which is accessed by ri SAVE + + Envelope, filter & LFO + + + + Chord stacking & arpeggio + + + + Effects + + + + MIDI settings + + + + Miscellaneous + + + + Plugin + + Knob @@ -3913,14 +3913,6 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS. Recover the file. Please don't run multiple instances of LMMS when you do this. - - Ignore - - - - Launch LMMS as usual but with automatic backup disabled to prevent the present recover file from being overwritten. - - Discard @@ -3993,10 +3985,6 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS. Recover session. Please save your work! - - Automatic backup disabled. Remember to save your work! - - Recovered project not saved @@ -5706,14 +5694,6 @@ Reason: "%2" ProjectNotes - - Project notes - - - - Put down your project notes here. - - Edit Actions @@ -5822,6 +5802,14 @@ Reason: "%2" &Color... + + Project Notes + + + + Enter project notes here + + ProjectRenderer diff --git a/data/projects/templates/default.mpt b/data/projects/templates/default.mpt new file mode 100644 index 000000000..18fcdd925 --- /dev/null +++ b/data/projects/templates/default.mpt @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Enter project notes here

]]>
+ + + + diff --git a/data/samples/misc/metronome01.ogg b/data/samples/misc/metronome01.ogg index ea8481ff8..0797c6e80 100644 Binary files a/data/samples/misc/metronome01.ogg and b/data/samples/misc/metronome01.ogg differ diff --git a/data/samples/misc/metronome02.ogg b/data/samples/misc/metronome02.ogg index 70b53e117..780530b3d 100644 Binary files a/data/samples/misc/metronome02.ogg and b/data/samples/misc/metronome02.ogg differ diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 63d72e94a..11a5e440e 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -139,6 +139,19 @@ PianoRoll { qproperty-textShadow: rgb( 240, 240, 240 ); } +TabWidget { + background-color: #5b6571; + qproperty-tabText: rgba(255, 255, 255, 180); + qproperty-tabTitleText: #fff; + qproperty-tabSelected: #61666b; + qproperty-tabBackground: #3c434b; + qproperty-tabBorder: #3c434b; +} + +GroupBox { + background-color: #5b6571; +} + /* main toolbar oscilloscope - can have transparent bg now */ VisualizationWidget { @@ -281,6 +294,11 @@ TrackContainerView QFrame{ background-color: #49515b; } +/* background for track controls */ +TrackView > QWidget { + background-color: #5b6571; +} + /* autoscroll, loop, stop behaviour toggle buttons */ /* track background colors */ @@ -523,6 +541,12 @@ PluginDescWidget:hover { background-color: #e0e0e0; } +/* piano widget */ + +PianoView { + background-color: #14171a; +} + /* font sizes for text buttons */ FxMixerView QPushButton, EffectRackView QPushButton, ControllerRackView QPushButton { @@ -543,6 +567,7 @@ FxLine { /* persistent peak markers for fx peak meters */ Fader { qproperty-peakGreen: rgb( 74, 253, 133); + qproperty-peakYellow: rgb(224, 222, 18); qproperty-peakRed: rgb( 255, 100, 100); } diff --git a/data/themes/default/env_lfo_tab.png b/data/themes/default/env_lfo_tab.png new file mode 100644 index 000000000..8916ea44a Binary files /dev/null and b/data/themes/default/env_lfo_tab.png differ diff --git a/data/themes/default/func_tab.png b/data/themes/default/func_tab.png new file mode 100644 index 000000000..8776571cd Binary files /dev/null and b/data/themes/default/func_tab.png differ diff --git a/data/themes/default/fx_tab.png b/data/themes/default/fx_tab.png new file mode 100644 index 000000000..58fbcb9bd Binary files /dev/null and b/data/themes/default/fx_tab.png differ diff --git a/data/themes/default/midi_tab.png b/data/themes/default/midi_tab.png new file mode 100644 index 000000000..6a006f6b1 Binary files /dev/null and b/data/themes/default/midi_tab.png differ diff --git a/data/themes/default/misc_tab.png b/data/themes/default/misc_tab.png new file mode 100644 index 000000000..efb0d8f41 Binary files /dev/null and b/data/themes/default/misc_tab.png differ diff --git a/data/themes/default/plugin_tab.png b/data/themes/default/plugin_tab.png new file mode 100644 index 000000000..788882f05 Binary files /dev/null and b/data/themes/default/plugin_tab.png differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 7e96d8ba0..7680b7aae 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -136,6 +136,19 @@ PianoRoll { qproperty-textShadow: #fff; } +TabWidget { + background-color: #262b30; + qproperty-tabText: rgba(255, 255, 255, 180); + qproperty-tabTitleText: #fff; + qproperty-tabSelected: #323940; + qproperty-tabBackground: #181b1f; + qproperty-tabBorder: #181b1f; +} + +GroupBox { + background-color: #262b30; +} + /* main toolbar oscilloscope - can have transparent bg now */ VisualizationWidget { diff --git a/data/themes/default/track_op_menu.png b/data/themes/default/track_op_menu.png deleted file mode 100755 index 50c2b478c..000000000 Binary files a/data/themes/default/track_op_menu.png and /dev/null differ diff --git a/data/themes/default/track_op_menu_active.png b/data/themes/default/track_op_menu_active.png deleted file mode 100755 index b26dd6ef2..000000000 Binary files a/data/themes/default/track_op_menu_active.png and /dev/null differ diff --git a/doc/AUTHORS b/doc/AUTHORS index e14e92732..8dc5a3039 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -85,3 +85,7 @@ Chrissy McManus Oskar Wallgren Development + +Michael Gregorius + + Development diff --git a/doc/lmms.1 b/doc/lmms.1 index b9c297caf..41f906ecf 100644 --- a/doc/lmms.1 +++ b/doc/lmms.1 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH LMMS 1 "February 17, 2016" +.TH LMMS 1 "June 15, 2017" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -52,6 +52,9 @@ lmms \- software for easy music production .RB "[ \--\fBloop\fP ]" .br .B lmms +.RB "[ \--\fBmode\fP \fIstereomode\fP ]" +.br +.B lmms .RB "[ \--\fBoutput\fP \fIpath\fP ]" .br .B lmms @@ -94,7 +97,7 @@ Get the configuration from \fIconfigfile\fP instead of ~/.lmmsrc.xml (default) .IP "\fB\-d, --dump\fP \fIin\fP Dump XML of compressed file \fIin\fP (i.e. MMPZ-file) .IP "\fB\-f, --format\fP \fIformat\fP -Specify format of render-output where \fIformat\fP is either 'wav' or 'ogg' +Specify format of render-output where \fIformat\fP is either 'wav', 'ogg' or 'mp3'. .IP "\fB\ --geometry\fP \fIgeometry\fP Specify the prefered size and position of the main window .br @@ -111,6 +114,8 @@ Import MIDI file \fIin\fP If -e is specified lmms exits after importing the file. .IP "\fB\-l, --loop Render the given file as a loop, i.e. stop rendering at exactly the end of the song. Additional silence or reverb tails at the end of the song are not rendered. +.IP "\fB\-m, --mode\fP \fIstereomode\fP +Set the stereo mode used for the MP3 export. \fIstereomode\fP can be either 's' (stereo mode), 'j' (joint stereo) or 'm' (mono). If no mode is given 'j' is used as the default. .IP "\fB\-o, --output\fP \fIpath\fP Render into \fIpath\fP .br diff --git a/include/AudioFileMP3.h b/include/AudioFileMP3.h new file mode 100644 index 000000000..497208e20 --- /dev/null +++ b/include/AudioFileMP3.h @@ -0,0 +1,74 @@ +/* + * AudioFileMP3.h - Audio-device which encodes a wave stream into + * an MP3 file. This is used for song export. + * + * Copyright (c) 2017 to present Michael Gregorius + * + * This file is part of LMMS - https://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 AUDIO_FILE_MP3_H +#define AUDIO_FILE_MP3_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_MP3LAME + +#include "AudioFileDevice.h" + +#include "lame/lame.h" + + +class AudioFileMP3 : public AudioFileDevice +{ +public: + AudioFileMP3( OutputSettings const & outputSettings, + const ch_cnt_t _channels, + bool & successful, + const QString & _file, + Mixer* mixer ); + virtual ~AudioFileMP3(); + + static AudioFileDevice * getInst( const QString & outputFilename, + OutputSettings const & outputSettings, + const ch_cnt_t channels, + Mixer* mixer, + bool & successful ) + { + return new AudioFileMP3( outputSettings, channels, successful, + outputFilename, mixer ); + } + +protected: + virtual void writeBuffer( const surroundSampleFrame * /* _buf*/, + const fpp_t /*_frames*/, + const float /*_master_gain*/ ); + +private: + void flushRemainingBuffers(); + bool initEncoder(); + void tearDownEncoder(); + +private: + lame_t m_lame; +}; + +#endif + +#endif diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index a4096f9ba..dd9bd7680 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -407,7 +407,7 @@ public: { } float getRoundedValue() const; - float getDigitCount(); + int getDigitCount() const; defaultTypedMethods(float); } ; diff --git a/include/ConfigManager.h b/include/ConfigManager.h index c5ef45b34..4c0a73e05 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -217,6 +217,9 @@ public: const QString & value( const QString & cls, const QString & attribute ) const; + const QString & value( const QString & cls, + const QString & attribute, + const QString & defaultVal ) const; void setValue( const QString & cls, const QString & attribute, const QString & value ); void deleteValue( const QString & cls, const QString & attribute); diff --git a/include/GroupBox.h b/include/GroupBox.h index 6b4a1e98b..39d9a522a 100644 --- a/include/GroupBox.h +++ b/include/GroupBox.h @@ -57,7 +57,7 @@ public: protected: virtual void mousePressEvent( QMouseEvent * _me ); - virtual void resizeEvent( QResizeEvent * _re ); + virtual void paintEvent( QPaintEvent * _pe ); private: diff --git a/include/MainWindow.h b/include/MainWindow.h index 1f839b371..41fc06d5d 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -110,8 +110,7 @@ public: enum SessionState { Normal, - Recover, - Limited, + Recover }; void setSession( SessionState session ) @@ -143,7 +142,7 @@ public: return m_keyMods.m_alt; } - static void saveWidgetState( QWidget * _w, QDomElement & _de, QSize const & sizeIfInvisible = QSize(0, 0) ); + static void saveWidgetState( QWidget * _w, QDomElement & _de ); static void restoreWidgetState( QWidget * _w, const QDomElement & _de ); public slots: diff --git a/include/OutputSettings.h b/include/OutputSettings.h index e4dccfd3a..2b294e13c 100644 --- a/include/OutputSettings.h +++ b/include/OutputSettings.h @@ -38,6 +38,13 @@ public: NumDepths }; + enum StereoMode + { + StereoMode_Stereo, + StereoMode_JointStereo, + StereoMode_Mono + }; + class BitRateSettings { public: @@ -60,10 +67,19 @@ public: public: OutputSettings( sample_rate_t sampleRate, BitRateSettings const & bitRateSettings, - BitDepth bitDepth ) : + BitDepth bitDepth, + StereoMode stereoMode ) : m_sampleRate(sampleRate), m_bitRateSettings(bitRateSettings), - m_bitDepth(bitDepth) + m_bitDepth(bitDepth), + m_stereoMode(stereoMode) + { + } + + OutputSettings( sample_rate_t sampleRate, + BitRateSettings const & bitRateSettings, + BitDepth bitDepth ) : + OutputSettings(sampleRate, bitRateSettings, bitDepth, StereoMode_Stereo ) { } @@ -76,10 +92,14 @@ public: BitDepth getBitDepth() const { return m_bitDepth; } void setBitDepth(BitDepth bitDepth) { m_bitDepth = bitDepth; } + StereoMode getStereoMode() const { return m_stereoMode; } + void setStereoMode(StereoMode stereoMode) { m_stereoMode = stereoMode; } + private: sample_rate_t m_sampleRate; BitRateSettings m_bitRateSettings; BitDepth m_bitDepth; + StereoMode m_stereoMode; }; #endif diff --git a/include/Pattern.h b/include/Pattern.h index 94eef796c..41b76553c 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -88,7 +88,7 @@ public: { return m_patternType; } - void checkType(); + // next/previous track based on position in the containing track Pattern * previousPattern() const; @@ -132,6 +132,7 @@ private: MidiTime beatPatternLength() const; void setType( PatternTypes _new_pattern_type ); + void checkType(); void resizeToFirstTrack(); diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index 20ccbc0de..15d043e51 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -39,11 +39,14 @@ public: { WaveFile, OggFile, + MP3File, NumFileFormats } ; struct FileEncodeDevice { + bool isAvailable() const { return m_getDevInst != nullptr; } + ExportFileFormats m_fileFormat; const char * m_description; const char * m_extension; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 25841486c..6d39ed355 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -153,6 +153,7 @@ public: public slots: void updateTcos(); + void setPlayingTcos( bool isPlaying ); private: FloatModel m_volumeModel; diff --git a/include/Song.h b/include/Song.h index ad644ed9f..be7f9e508 100644 --- a/include/Song.h +++ b/include/Song.h @@ -97,6 +97,10 @@ public: void processNextBuffer(); + inline int getLoadingTrackCount() const + { + return m_nLoadingTrack; + } inline int getMilliseconds() const { return m_elapsedMilliSeconds; @@ -339,6 +343,7 @@ private: ControllerVector m_controllers; + int m_nLoadingTrack; QString m_fileName; QString m_oldFileName; diff --git a/include/SongEditor.h b/include/SongEditor.h index e8961b8bc..db4d7d781 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -81,6 +81,7 @@ public slots: void setEditModeSelect(); void updatePosition( const MidiTime & t ); + void updatePositionLine(); protected: virtual void closeEvent( QCloseEvent * ce ); @@ -152,6 +153,9 @@ public: SongEditor* m_editor; +protected: + virtual void resizeEvent( QResizeEvent * event ); + protected slots: void play(); void record(); @@ -162,6 +166,7 @@ protected slots: signals: void playTriggered(); + void resized(); private: QAction* m_addBBTrackAction; diff --git a/include/TabWidget.h b/include/TabWidget.h index f4888f0cc..402d7c9cc 100644 --- a/include/TabWidget.h +++ b/include/TabWidget.h @@ -29,25 +29,47 @@ #include #include +const int TEXT_TAB_HEIGHT = 14; +const int GRAPHIC_TAB_HEIGHT = 17; class TabWidget : public QWidget { Q_OBJECT public: - TabWidget( const QString & _caption, QWidget * _parent ); + TabWidget( const QString & _caption, QWidget * _parent, bool usePixmap = false ); virtual ~TabWidget(); - void addTab( QWidget * _w, const QString & _name, int _idx = -1 ); + void addTab( QWidget * w, const QString & name, const char *pixmap = NULL, int idx = -1 ); - void setActiveTab( int _idx ); + void setActiveTab( int idx ); + + int findTabAtPos( const QPoint *pos ); inline int activeTab() const { return( m_activeTab ); } + // Themeability + Q_PROPERTY( QColor tabText READ tabText WRITE setTabText) + Q_PROPERTY( QColor tabTitleText READ tabTitleText WRITE setTabTitleText) + Q_PROPERTY( QColor tabSelected READ tabSelected WRITE setTabSelected) + Q_PROPERTY( QColor tabBackground READ tabBackground WRITE setTabBackground) + Q_PROPERTY( QColor tabBorder READ tabBorder WRITE setTabBorder) + + QColor tabText() const; + void setTabText( const QColor & c ); + QColor tabTitleText() const; + void setTabTitleText( const QColor & c ); + QColor tabSelected() const; + void setTabSelected( const QColor & c ); + QColor tabBackground() const; + void setTabBackground( const QColor & c ); + QColor tabBorder() const; + void setTabBorder( const QColor & c ); protected: + virtual bool event( QEvent * event ); virtual void mousePressEvent( QMouseEvent * _me ); virtual void paintEvent( QPaintEvent * _pe ); virtual void resizeEvent( QResizeEvent * _re ); @@ -57,16 +79,26 @@ protected: private: struct widgetDesc { - QWidget * w; // ptr to widget - QString name; // name for widget - int nwidth; // width of name when painting + QWidget * w; // ptr to widget + const char * pixmap; // artwork for the widget + QString name; // name for widget + int nwidth; // width of name when painting (only valid for text tab) } ; typedef QMap widgetStack; widgetStack m_widgets; - int m_activeTab; - QString m_caption; - quint8 m_tabheight; + + int m_activeTab; + QString m_caption; // Tab caption, used as the tooltip text on icon tabs + quint8 m_tabbarHeight; // The height of the tab bar + quint8 m_tabheight; // The height of the tabs + bool m_usePixmap; // true if the tabs are to be displayed with icons. False for text tabs. + + QColor m_tabText; // The color of the tabs' text. + QColor m_tabTitleText; // The color of the TabWidget's title text. + QColor m_tabSelected; // The highlighting color for the selected tab. + QColor m_tabBackground; // The TabWidget's background color. + QColor m_tabBorder; // The TabWidget's borders color. } ; #endif diff --git a/include/TrackLabelButton.h b/include/TrackLabelButton.h index 5b0e6c4a8..f1059bdbf 100644 --- a/include/TrackLabelButton.h +++ b/include/TrackLabelButton.h @@ -29,9 +29,10 @@ #include #include - class TrackView; +class TrackRenameLineEdit; + class TrackLabelButton : public QToolButton { @@ -60,7 +61,7 @@ protected: private: TrackView * m_trackView; QString m_iconName; - QLineEdit * m_renameLineEdit; + TrackRenameLineEdit * m_renameLineEdit; QRect m_buttonRect; QString elideName( const QString &name ); diff --git a/include/TrackRenameLineEdit.h b/include/TrackRenameLineEdit.h new file mode 100644 index 000000000..6883b9b05 --- /dev/null +++ b/include/TrackRenameLineEdit.h @@ -0,0 +1,46 @@ +/* + * TrackRenameLineEdit.h - class TrackRenameLineEdit + * + * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2017 Alexandre Almeida + * + * This file is part of LMMS - https://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 TRACK_RENAME_LINE_EDIT_H +#define TRACK_RENAME_LINE_EDIT_H + +#include + +class TrackRenameLineEdit : public QLineEdit +{ + Q_OBJECT +public: + TrackRenameLineEdit( QWidget * parent ); + void show(); + +protected: + virtual void keyPressEvent( QKeyEvent * ke ); + +private: + QString m_oldName; +} ; + +#endif diff --git a/plugins/Bitcrush/BitcrushControlDialog.cpp b/plugins/Bitcrush/BitcrushControlDialog.cpp old mode 100644 new mode 100755 index 1bbbd2c47..995378068 --- a/plugins/Bitcrush/BitcrushControlDialog.cpp +++ b/plugins/Bitcrush/BitcrushControlDialog.cpp @@ -41,73 +41,73 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 215, 120 ); + setFixedSize( 181, 128 ); // labels QLabel * inLabel = new QLabel( tr( "IN" ), this ); - inLabel->move( 12, 10); + inLabel->move( 24, 15 ); QLabel * outLabel = new QLabel( tr( "OUT" ), this ); - outLabel->move( 176, 10 ); + outLabel->move( 139, 15 ); // input knobs Knob * inGain = new Knob( knobBright_26, this ); - inGain->move( 12, 25 ); + inGain->move( 16, 32 ); inGain->setModel( & controls->m_inGain ); inGain->setLabel( tr( "GAIN" ) ); inGain->setHintText( tr( "Input Gain:" ) + " ", " dBFS" ); Knob * inNoise = new Knob( knobBright_26, this ); - inNoise->move( 12, 70 ); + inNoise->move( 14, 76 ); inNoise->setModel( & controls->m_inNoise ); - inNoise->setLabel( tr( "NOIS" ) ); + inNoise->setLabel( tr( "NOISE" ) ); inNoise->setHintText( tr( "Input Noise:" ) + " ", "%" ); // output knobs Knob * outGain = new Knob( knobBright_26, this ); - outGain->move( 176, 25 ); + outGain->move( 138, 32 ); outGain->setModel( & controls->m_outGain ); outGain->setLabel( tr( "GAIN" ) ); outGain->setHintText( tr( "Output Gain:" ) + " ", " dBFS" ); Knob * outClip = new Knob( knobBright_26, this ); - outClip->move( 176, 70 ); + outClip->move( 138, 76 ); outClip->setModel( & controls->m_outClip ); outClip->setLabel( tr( "CLIP" ) ); outClip->setHintText( tr( "Output Clip:" ) + " ", "%" ); // leds - LedCheckBox * rateEnabled = new LedCheckBox( tr( "Rate" ), this, tr( "Rate Enabled" ), LedCheckBox::Green ); - rateEnabled->move( 50, 30 ); + LedCheckBox * rateEnabled = new LedCheckBox( "", this, tr( "Rate Enabled" ), LedCheckBox::Green ); + rateEnabled->move( 64, 14 ); rateEnabled->setModel( & controls->m_rateEnabled ); ToolTip::add( rateEnabled, tr( "Enable samplerate-crushing" ) ); - LedCheckBox * depthEnabled = new LedCheckBox( tr( "Depth" ), this, tr( "Depth Enabled" ), LedCheckBox::Green ); - depthEnabled->move( 50, 80 ); + LedCheckBox * depthEnabled = new LedCheckBox( "", this, tr( "Depth Enabled" ), LedCheckBox::Green ); + depthEnabled->move( 101, 14 ); depthEnabled->setModel( & controls->m_depthEnabled ); ToolTip::add( depthEnabled, tr( "Enable bitdepth-crushing" ) ); // rate crushing knobs Knob * rate = new Knob( knobBright_26, this ); - rate->move( 100, 20 ); + rate->move( 59, 32 ); rate->setModel( & controls->m_rate ); - rate->setLabel( tr( "Rate" ) ); + rate->setLabel( tr( "FREQ" ) ); rate->setHintText( tr( "Sample rate:" ) + " ", " Hz" ); Knob * stereoDiff = new Knob( knobBright_26, this ); - stereoDiff->move( 140, 20 ); + stereoDiff->move( 72, 76 ); stereoDiff->setModel( & controls->m_stereoDiff ); - stereoDiff->setLabel( tr( "STD" ) ); + stereoDiff->setLabel( tr( "STEREO" ) ); stereoDiff->setHintText( tr( "Stereo difference:" ) + " ", "%" ); // depth crushing knob Knob * levels = new Knob( knobBright_26, this ); - levels->move( 140, 70 ); + levels->move( 92, 32 ); levels->setModel( & controls->m_levels ); - levels->setLabel( tr( "Levels" ) ); + levels->setLabel( tr( "QUANT" ) ); levels->setHintText( tr( "Levels:" ) + " ", "" ); } diff --git a/plugins/Bitcrush/artwork.png b/plugins/Bitcrush/artwork.png old mode 100644 new mode 100755 index c0c97bdd5..72d980c91 Binary files a/plugins/Bitcrush/artwork.png and b/plugins/Bitcrush/artwork.png differ diff --git a/plugins/Delay/DelayControls.cpp b/plugins/Delay/DelayControls.cpp index 97067607d..3f81eb470 100644 --- a/plugins/Delay/DelayControls.cpp +++ b/plugins/Delay/DelayControls.cpp @@ -32,7 +32,7 @@ DelayControls::DelayControls( DelayEffect* effect ): EffectControls( effect ), m_effect ( effect ), - m_delayTimeModel( 0.5, 0.01, 5.0, 0.0001, 20000.0, this, tr( "Delay Samples" )) , + m_delayTimeModel( 0.5, 0.01, 5.0, 0.0001, 5000.0, this, tr( "Delay Samples" )) , m_feedbackModel(0.0f,0.0f,1.0f,0.01f,this,tr( "Feedback" ) ), 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" ) ), diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index 327dfda03..d25f6b52b 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -36,7 +36,7 @@ StereoDelay::StereoDelay( int maxTime, int sampleRate ) m_maxLength = maxTime * sampleRate; m_length = m_maxLength; - m_index = 0; + m_writeIndex = 0; m_feedback = 0.0f; setSampleRate( sampleRate ); } @@ -57,13 +57,13 @@ StereoDelay::~StereoDelay() void StereoDelay::tick( sampleFrame frame ) { - 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 ); + m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; + int readIndex = m_writeIndex - m_length; + if (readIndex < 0 ) { readIndex += m_maxLength; } + float lOut = m_buffer[ readIndex ][ 0 ]; + float rOut = m_buffer[ readIndex ] [1 ]; + m_buffer[ m_writeIndex ][ 0 ] = frame[ 0 ] + ( lOut * m_feedback ); + m_buffer[ m_writeIndex ][ 1 ] = frame[ 1 ] + ( rOut * m_feedback ); frame[ 0 ] = lOut; frame[ 1 ] = rOut; } @@ -71,6 +71,8 @@ void StereoDelay::tick( sampleFrame frame ) + + void StereoDelay::setSampleRate( int sampleRate ) { if( m_buffer ) diff --git a/plugins/Delay/StereoDelay.h b/plugins/Delay/StereoDelay.h index 4886149dc..a0dbdc220 100644 --- a/plugins/Delay/StereoDelay.h +++ b/plugins/Delay/StereoDelay.h @@ -52,7 +52,7 @@ private: sampleFrame* m_buffer; int m_maxLength; float m_length; - int m_index; + int m_writeIndex; float m_feedback; float m_maxTime; }; diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp old mode 100644 new mode 100755 index 4c53ba36d..3144eddd0 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -49,15 +49,15 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 150, 220 ); + setFixedSize( 373, 109 ); - makeknob( cut1Knob, 33, 30, m_cut1Model, tr( "FREQ" ), tr( "Cutoff frequency" ), "Hz" ) - makeknob( res1Knob, 75, 30, m_res1Model, tr( "RESO" ), tr( "Resonance" ), "" ) - makeknob( gain1Knob, 117, 30, m_gain1Model, tr( "GAIN" ), tr( "Gain" ), "%" ) - makeknob( mixKnob, 62, 100, m_mixModel, tr( "MIX" ), tr( "Mix" ), "" ) - makeknob( cut2Knob, 33, 145, m_cut2Model, tr( "FREQ" ), tr( "Cutoff frequency" ), "Hz" ) - makeknob( res2Knob, 75, 145, m_res2Model, tr( "RESO" ), tr( "Resonance" ), "" ) - makeknob( gain2Knob, 117, 145, m_gain2Model, tr( "GAIN" ), tr( "Gain" ), "%" ) + makeknob( cut1Knob, 24, 26, m_cut1Model, tr( "FREQ" ), tr( "Cutoff frequency" ), "Hz" ) + makeknob( res1Knob, 74, 26, m_res1Model, tr( "RESO" ), tr( "Resonance" ), "" ) + makeknob( gain1Knob, 124, 26, m_gain1Model, tr( "GAIN" ), tr( "Gain" ), "%" ) + makeknob( mixKnob, 173, 37, m_mixModel, tr( "MIX" ), tr( "Mix" ), "" ) + makeknob( cut2Knob, 222, 26, m_cut2Model, tr( "FREQ" ), tr( "Cutoff frequency" ), "Hz" ) + makeknob( res2Knob, 272, 26, m_res2Model, tr( "RESO" ), tr( "Resonance" ), "" ) + makeknob( gain2Knob, 322, 26, m_gain2Model, tr( "GAIN" ), tr( "Gain" ), "%" ) gain1Knob-> setVolumeKnob( true ); gain2Knob-> setVolumeKnob( true ); @@ -67,20 +67,20 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) LedCheckBox * enabled2Toggle = new LedCheckBox( "", this, tr( "Filter 2 enabled" ), LedCheckBox::Green ); - enabled1Toggle -> move( 5, 30 ); + enabled1Toggle -> move( 12, 11 ); enabled1Toggle -> setModel( &controls -> m_enabled1Model ); ToolTip::add( enabled1Toggle, tr( "Click to enable/disable Filter 1" ) ); - enabled2Toggle -> move( 5, 145 ); + enabled2Toggle -> move( 210, 11 ); enabled2Toggle -> setModel( &controls -> m_enabled2Model ); ToolTip::add( enabled2Toggle, tr( "Click to enable/disable Filter 2" ) ); ComboBox * m_filter1ComboBox = new ComboBox( this ); - m_filter1ComboBox->setGeometry( 5, 70, 140, 22 ); + m_filter1ComboBox->setGeometry( 19, 70, 137, 22 ); m_filter1ComboBox->setFont( pointSize<8>( m_filter1ComboBox->font() ) ); m_filter1ComboBox->setModel( &controls->m_filter1Model ); ComboBox * m_filter2ComboBox = new ComboBox( this ); - m_filter2ComboBox->setGeometry( 5, 185, 140, 22 ); + m_filter2ComboBox->setGeometry( 217, 70, 137, 22 ); m_filter2ComboBox->setFont( pointSize<8>( m_filter2ComboBox->font() ) ); m_filter2ComboBox->setModel( &controls->m_filter2Model ); } diff --git a/plugins/DualFilter/artwork.png b/plugins/DualFilter/artwork.png index 88b6a1f11..00458f101 100644 Binary files a/plugins/DualFilter/artwork.png and b/plugins/DualFilter/artwork.png differ diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 66e924cae..fa664aa3b 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -70,6 +70,10 @@ EqEffect::~EqEffect() bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) { + //wet/dry controls + const float dry = dryLevel(); + const float wet = wetLevel(); + sample_t dryS[2]; // setup sample exact controls float hpRes = m_eqControls.m_hpResModel.value(); float lowShelfRes = m_eqControls.m_lowShelfResModel.value(); @@ -205,6 +209,9 @@ bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) for( fpp_t f = 0; f < frames; f++) { + //wet dry buffer + dryS[0] = buf[f][0]; + dryS[1] = buf[f][1]; if( hpActive ) { m_hp12.setParameters( sampleRate, *hpFreqPtr, *hpResPtr, 1 ); @@ -296,6 +303,10 @@ bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) } } + //apply wet / dry levels + buf[f][1] = ( dry * dryS[1] ) + ( wet * buf[f][1] ); + buf[f][0] = ( dry * dryS[0] ) + ( wet * buf[f][0] ); + //increment pointers if needed hpResPtr += hpResInc; lowShelfResPtr += lowShelfResInc; diff --git a/plugins/Flanger/FlangerControls.cpp b/plugins/Flanger/FlangerControls.cpp index 7525b8024..4ba91dba3 100644 --- a/plugins/Flanger/FlangerControls.cpp +++ b/plugins/Flanger/FlangerControls.cpp @@ -35,7 +35,7 @@ FlangerControls::FlangerControls( FlangerEffect *effect ) : EffectControls ( effect ), m_effect ( effect ), m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay Samples" ) ) , - m_lfoFrequencyModel( 0.25, 0.01, 5, 0.0001, 20000.0 ,this, tr( "Lfo Frequency" ) ), + m_lfoFrequencyModel( 0.25, 0.01, 60, 0.0001, 60000.0 ,this, tr( "Lfo Frequency" ) ), m_lfoAmountModel( 0.0, 0.0, 0.0025 , 0.0001 , this , tr( "Seconds" ) ), m_feedbackModel( 0.0 , 0.0 , 1.0 , 0.0001, this, tr( "Regen" ) ), m_whiteNoiseAmountModel( 0.0 , 0.0 , 0.05 , 0.0001, this, tr( "Noise" ) ), diff --git a/plugins/Flanger/FlangerControlsDialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp index 0b95ac781..972015b0b 100644 --- a/plugins/Flanger/FlangerControlsDialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -52,7 +52,7 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) : lfoFreqKnob->setVolumeKnob( false ); lfoFreqKnob->setModel( &controls->m_lfoFrequencyModel ); lfoFreqKnob->setLabel( tr( "RATE" ) ); - lfoFreqKnob->setHintText( tr ( "Rate:" ) , "Hz" ); + lfoFreqKnob->setHintText( tr ( "Period:" ) , " Sec" ); Knob * lfoAmtKnob = new Knob( knobBright_26, this ); lfoAmtKnob->move( 85,10 ); diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 5a287bec3..d270c0801 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -94,7 +94,7 @@ bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::mixer()->processingSampleRate(); bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); - m_lfo->setFrequency( m_flangerControls.m_lfoFrequencyModel.value() ); + m_lfo->setFrequency( 1.0/m_flangerControls.m_lfoFrequencyModel.value() ); m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); sample_t dryS[2]; @@ -107,8 +107,8 @@ bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) dryS[0] = buf[f][0]; dryS[1] = buf[f][1]; m_lfo->tick(&leftLfo, &rightLfo); - m_lDelay->setLength( ( float )length + ( amplitude * leftLfo ) ); - m_rDelay->setLength( ( float )length+ ( amplitude * rightLfo ) ); + m_lDelay->setLength( ( float )length + amplitude * (leftLfo+1.0) ); + m_rDelay->setLength( ( float )length + amplitude * (rightLfo+1.0) ); if(invertFeedback) { m_lDelay->tick( &buf[f][1] ); diff --git a/plugins/Flanger/MonoDelay.cpp b/plugins/Flanger/MonoDelay.cpp index 534f0bcf8..fc63d57d1 100644 --- a/plugins/Flanger/MonoDelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -34,7 +34,7 @@ MonoDelay::MonoDelay( int maxTime , int sampleRate ) m_maxLength = maxTime * sampleRate; m_length = m_maxLength; - m_index = 0; + m_writeIndex = 0; m_feedback = 0.0f; setSampleRate( sampleRate ); } @@ -54,11 +54,11 @@ MonoDelay::~MonoDelay() void MonoDelay::tick( sample_t* sample ) { - 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 ); + m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; + int readIndex = m_writeIndex - m_length; + if (readIndex < 0 ) { readIndex += m_maxLength; } + float out = m_buffer[ readIndex ]; + m_buffer[ m_writeIndex ] = *sample + ( out * m_feedback ); *sample = out; } diff --git a/plugins/Flanger/MonoDelay.h b/plugins/Flanger/MonoDelay.h index 8b06bff1b..52898bfe4 100644 --- a/plugins/Flanger/MonoDelay.h +++ b/plugins/Flanger/MonoDelay.h @@ -52,7 +52,7 @@ private: sample_t* m_buffer; int m_maxLength; float m_length; - int m_index; + int m_writeIndex; float m_feedback; float m_maxTime; }; diff --git a/plugins/GigPlayer/CMakeLists.txt b/plugins/GigPlayer/CMakeLists.txt index 24db813bd..88e15f87b 100644 --- a/plugins/GigPlayer/CMakeLists.txt +++ b/plugins/GigPlayer/CMakeLists.txt @@ -2,6 +2,11 @@ if(LMMS_HAVE_GIG) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS}) + # Disable C++11 on Clang until gig.h is patched + IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + REMOVE_DEFINITIONS(-std=c++0x) + ENDIF() + # Required for not crashing loading files with libgig SET(GCC_COVERAGE_COMPILE_FLAGS "-fexceptions") add_definitions(${GCC_COVERAGE_COMPILE_FLAGS}) diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 4a23e513b..c13283028 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -1066,7 +1066,6 @@ void GigInstrumentView::showFileDialog() types << tr( "GIG Files (*.gig)" ); ofd.setNameFilters( types ); - QString dir; if( k->m_filename != "" ) { QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); diff --git a/plugins/GigPlayer/PatchesDialog.h b/plugins/GigPlayer/PatchesDialog.h index c8bcce157..0836631ac 100644 --- a/plugins/GigPlayer/PatchesDialog.h +++ b/plugins/GigPlayer/PatchesDialog.h @@ -30,7 +30,6 @@ #include "LcdSpinBox.h" #include "GigPlayer.h" -#include #include #include diff --git a/plugins/ReverbSC/CMakeLists.txt b/plugins/ReverbSC/CMakeLists.txt index 85a005d3c..71706df88 100644 --- a/plugins/ReverbSC/CMakeLists.txt +++ b/plugins/ReverbSC/CMakeLists.txt @@ -7,18 +7,18 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") BUILD_PLUGIN( reverbsc - ReverbSC.cpp - ReverbSCControls.cpp - ReverbSCControlDialog.cpp + ReverbSC.cpp + ReverbSCControls.cpp + ReverbSCControlDialog.cpp base.c revsc.c - dcblock.c - MOCFILES - ReverbSC.h - ReverbSCControls.h - ReverbSCControlDialog.h base.h revsc.h dcblock.h + dcblock.c + ReverbSC.h + MOCFILES + ReverbSCControls.h + ReverbSCControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png ) diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index 1aec2b9dc..58cb0a770 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -58,8 +58,8 @@ ReverbSCEffect::ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatur sp_dcblock_create(&dcblk[0]); sp_dcblock_create(&dcblk[1]); - sp_dcblock_init(sp, dcblk[0]); - sp_dcblock_init(sp, dcblk[1]); + sp_dcblock_init(sp, dcblk[0], Engine::mixer()->currentQualitySettings().sampleRateMultiplier() ); + sp_dcblock_init(sp, dcblk[1], Engine::mixer()->currentQualitySettings().sampleRateMultiplier() ); } ReverbSCEffect::~ReverbSCEffect() @@ -125,6 +125,27 @@ bool ReverbSCEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) return isRunning(); } + +void ReverbSCEffect::changeSampleRate() +{ + // Change sr variable in Soundpipe. does not need to be destroyed + sp->sr = Engine::mixer()->processingSampleRate(); + + mutex.lock(); + sp_revsc_destroy(&revsc); + sp_dcblock_destroy(&dcblk[0]); + sp_dcblock_destroy(&dcblk[1]); + + sp_revsc_create(&revsc); + sp_revsc_init(sp, revsc); + + sp_dcblock_create(&dcblk[0]); + sp_dcblock_create(&dcblk[1]); + + sp_dcblock_init(sp, dcblk[0], Engine::mixer()->currentQualitySettings().sampleRateMultiplier() ); + sp_dcblock_init(sp, dcblk[1], Engine::mixer()->currentQualitySettings().sampleRateMultiplier() ); + mutex.unlock(); +} extern "C" { diff --git a/plugins/ReverbSC/ReverbSC.h b/plugins/ReverbSC/ReverbSC.h index 926afb6c6..ba987414b 100644 --- a/plugins/ReverbSC/ReverbSC.h +++ b/plugins/ReverbSC/ReverbSC.h @@ -48,12 +48,14 @@ public: return &m_reverbSCControls; } + void changeSampleRate(); private: ReverbSCControls m_reverbSCControls; sp_data *sp; sp_revsc *revsc; sp_dcblock *dcblk[2]; + QMutex mutex; friend class ReverbSCControls; } ; diff --git a/plugins/ReverbSC/ReverbSCControls.cpp b/plugins/ReverbSC/ReverbSCControls.cpp index 59fe6ccdd..5e52d3faf 100644 --- a/plugins/ReverbSC/ReverbSCControls.cpp +++ b/plugins/ReverbSC/ReverbSCControls.cpp @@ -38,6 +38,7 @@ ReverbSCControls::ReverbSCControls( ReverbSCEffect* effect ) : m_colorModel( 10000.0f, 100.0f, 15000.0f, 0.1f, this, tr( "Color" ) ), m_outputGainModel( 0.0f, -60.0f, 15, 0.1f, this, tr( "Output Gain" ) ) { + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changeSampleRate() )); } void ReverbSCControls::changeControl() @@ -60,3 +61,7 @@ void ReverbSCControls::saveSettings( QDomDocument& doc, QDomElement& _this ) m_outputGainModel.saveSettings( doc, _this, "output_gain" ); } +void ReverbSCControls::changeSampleRate() +{ + m_effect->changeSampleRate(); +} diff --git a/plugins/ReverbSC/ReverbSCControls.h b/plugins/ReverbSC/ReverbSCControls.h index d75a79db4..638bdade9 100644 --- a/plugins/ReverbSC/ReverbSCControls.h +++ b/plugins/ReverbSC/ReverbSCControls.h @@ -61,6 +61,7 @@ public: private slots: void changeControl(); + void changeSampleRate(); private: ReverbSCEffect* m_effect; diff --git a/plugins/ReverbSC/base.h b/plugins/ReverbSC/base.h index 6edf5a394..74c535cdc 100644 --- a/plugins/ReverbSC/base.h +++ b/plugins/ReverbSC/base.h @@ -25,7 +25,7 @@ typedef struct sp_auxdata { typedef struct sp_data { SPFLOAT *out; - int sr; + uint32_t sr; int nchan; unsigned long len; unsigned long pos; diff --git a/plugins/ReverbSC/dcblock.c b/plugins/ReverbSC/dcblock.c index 493a9ae92..f38bb73dc 100644 --- a/plugins/ReverbSC/dcblock.c +++ b/plugins/ReverbSC/dcblock.c @@ -10,6 +10,7 @@ * */ +#include #include #include "base.h" #include "dcblock.h" @@ -26,11 +27,11 @@ int sp_dcblock_destroy(sp_dcblock **p) return SP_OK; } -int sp_dcblock_init(sp_data *sp, sp_dcblock *p) +int sp_dcblock_init(sp_data *sp, sp_dcblock *p, int oversampling ) { p->outputs = 0.0; p->inputs = 0.0; - p->gain = 0.99; + p->gain = pow( 0.99, 1.0f / oversampling ); if (p->gain == 0.0 || p->gain>=1.0 || p->gain<=-1.0) p->gain = 0.99; return SP_OK; diff --git a/plugins/ReverbSC/dcblock.h b/plugins/ReverbSC/dcblock.h index 6584c400a..d98d58566 100644 --- a/plugins/ReverbSC/dcblock.h +++ b/plugins/ReverbSC/dcblock.h @@ -7,5 +7,5 @@ typedef struct { int sp_dcblock_create(sp_dcblock **p); int sp_dcblock_destroy(sp_dcblock **p); -int sp_dcblock_init(sp_data *sp, sp_dcblock *p); +int sp_dcblock_init(sp_data *sp, sp_dcblock *p, int oversampling ); int sp_dcblock_compute(sp_data *sp, sp_dcblock *p, SPFLOAT *in, SPFLOAT *out); diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 56e716d60..b9a7d9822 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -87,10 +87,10 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_managePluginButton = new PixmapButton( this, "" ); m_managePluginButton->setCheckable( false ); m_managePluginButton->setCursor( Qt::PointingHandCursor ); - m_managePluginButton->setActiveGraphic( embed::getIconPixmap( - "track_op_menu_active" ) ); - m_managePluginButton->setInactiveGraphic( embed::getIconPixmap( - "track_op_menu" ) ); + m_managePluginButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "controls_active" ) ); + m_managePluginButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "controls" ) ); connect( m_managePluginButton, SIGNAL( clicked() ), _ctl, SLOT( managePlugin() ) ); ToolTip::add( m_managePluginButton, tr( "Control VST-plugin from LMMS host" ) ); @@ -98,8 +98,8 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_managePluginButton->setWhatsThis( tr( "Click here, if you want to control VST-plugin from host." ) ); - m_managePluginButton->setMinimumWidth( 21 ); - m_managePluginButton->setMaximumWidth( 21 ); + m_managePluginButton->setMinimumWidth( 26 ); + m_managePluginButton->setMaximumWidth( 26 ); m_managePluginButton->setMinimumHeight( 21 ); m_managePluginButton->setMaximumHeight( 21 ); diff --git a/data/themes/classic/track_op_menu.png b/plugins/VstEffect/controls.png similarity index 100% rename from data/themes/classic/track_op_menu.png rename to plugins/VstEffect/controls.png diff --git a/data/themes/classic/track_op_menu_active.png b/plugins/VstEffect/controls_active.png similarity index 100% rename from data/themes/classic/track_op_menu_active.png rename to plugins/VstEffect/controls_active.png diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 191aacc9e..ada18448a 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -1081,7 +1081,6 @@ void sf2InstrumentView::showFileDialog() types << tr( "SoundFont2 Files (*.sf2)" ); ofd.setNameFilters( types ); - QString dir; if( k->m_filename != "" ) { QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); diff --git a/plugins/vestige/controls.png b/plugins/vestige/controls.png new file mode 100644 index 000000000..4e03eb351 Binary files /dev/null and b/plugins/vestige/controls.png differ diff --git a/plugins/vestige/controls_active.png b/plugins/vestige/controls_active.png new file mode 100644 index 000000000..5759cd7bb Binary files /dev/null and b/plugins/vestige/controls_active.png differ diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index c6d56d1bf..0b6ac4632 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -42,6 +42,7 @@ #include "Mixer.h" #include "GuiApplication.h" #include "PixmapButton.h" +#include "SampleBuffer.h" #include "StringPairDrag.h" #include "TextFloat.h" #include "ToolTip.h" @@ -174,17 +175,6 @@ void vestigeInstrument::setParameter( void ) void vestigeInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - if( QFileInfo( m_pluginDLL ).isAbsolute() ) - { - QString f = QString( m_pluginDLL ).replace( QDir::separator(), '/' ); - QString vd = QString( ConfigManager::inst()->vstDir() ).replace( QDir::separator(), '/' ); - QString relativePath; - if( !( relativePath = f.section( vd, 1, 1 ) ).isEmpty() ) - { - m_pluginDLL = relativePath; - } - } - _this.setAttribute( "plugin", m_pluginDLL ); m_pluginMutex.lock(); if( m_plugin != NULL ) @@ -254,8 +244,7 @@ void vestigeInstrument::loadFile( const QString & _file ) { closePlugin(); } - - m_pluginDLL = _file; + m_pluginDLL = SampleBuffer::tryToMakeRelative( _file ); TextFloat * tf = TextFloat::displayMessage( tr( "Loading plugin" ), tr( "Please wait while loading VST-plugin..." ), @@ -269,6 +258,7 @@ void vestigeInstrument::loadFile( const QString & _file ) closePlugin(); delete tf; collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( m_pluginDLL ) ); + m_pluginDLL = ""; return; } @@ -425,10 +415,10 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, m_managePluginButton->setCheckable( false ); m_managePluginButton->setCursor( Qt::PointingHandCursor ); m_managePluginButton->move( 216, 101 ); - m_managePluginButton->setActiveGraphic( embed::getIconPixmap( - "track_op_menu_active" ) ); - m_managePluginButton->setInactiveGraphic( embed::getIconPixmap( - "track_op_menu" ) ); + m_managePluginButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "controls_active" ) ); + m_managePluginButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "controls" ) ); connect( m_managePluginButton, SIGNAL( clicked() ), this, SLOT( managePlugin() ) ); ToolTip::add( m_managePluginButton, tr( "Control VST-plugin from LMMS host" ) ); @@ -614,29 +604,22 @@ void VestigeInstrumentView::openPlugin() { FileDialog ofd( NULL, tr( "Open VST-plugin" ) ); - QString dir; - if( m_vi->m_pluginDLL != "" ) - { - dir = QFileInfo( m_vi->m_pluginDLL ).absolutePath(); - } - else - { - dir = ConfigManager::inst()->vstDir(); - } - // change dir to position of previously opened file - ofd.setDirectory( dir ); - ofd.setFileMode( FileDialog::ExistingFiles ); - // set filters QStringList types; types << tr( "DLL-files (*.dll)" ) << tr( "EXE-files (*.exe)" ) ; ofd.setNameFilters( types ); + if( m_vi->m_pluginDLL != "" ) { - // select previously opened file - ofd.selectFile( QFileInfo( m_vi->m_pluginDLL ).fileName() ); + QString f = SampleBuffer::tryToMakeAbsolute( m_vi->m_pluginDLL ); + ofd.setDirectory( QFileInfo( f ).absolutePath() ); + ofd.selectFile( QFileInfo( f ).fileName() ); + } + else + { + ofd.setDirectory( ConfigManager::inst()->vstDir() ); } if ( ofd.exec () == QDialog::Accepted ) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a0b74bc2d..0bd4fe0e1 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -34,20 +34,20 @@ IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) IF(LMMS_HOST_X86_64) SET(EXTRA_FLAGS -m32) - # workaround for broken wineg++ in WINE 1.4 (shipped e.g. with Ubuntu Precise) EXEC_PROGRAM( ${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) + # workaround for broken wineg++ in WINE 1.4 (shipped e.g. with Ubuntu Precise) IF("${WINEBUILD_OUTPUT}" MATCHES ".*x86_64-linux-gnu/wine/libwinecrt0.a.*") - SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386-linux-gnu/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) + SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386-linux-gnu/wine/libwinecrt0.a -L/usr/lib/i386-linux-gnu/wine/ -luser32 -lkernel32 -lgdi32) ENDIF() # The following check works on Fedora systems IF("${WINEBUILD_OUTPUT}" MATCHES "/usr/lib/lib64/wine/libwinecrt0.a.*") SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) ENDIF() - # Development + # Wine development IF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-devel/lib64/wine/libwinecrt0.a.*") SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /opt/wine-devel/lib/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) ENDIF() - # Staging + # Wine staging IF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-staging/lib64/wine/libwinecrt0.a.*") SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /opt/wine-staging/lib/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) ENDIF() @@ -61,16 +61,15 @@ STRING(REPLACE " " ";" WINE_BUILD_FLAGS ${CMAKE_CXX_FLAGS} " " ${CMAKE_EXE_LINKE ADD_CUSTOM_COMMAND( SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" COMMAND ${WINE_CXX} - ARGS "-I\"${CMAKE_BINARY_DIR}\"" - "-I\"${CMAKE_SOURCE_DIR}/include\"" - "-I\"${CMAKE_INSTALL_PREFIX}/include/wine/windows\"" - "-I\"${CMAKE_INSTALL_PREFIX}/include\"" - "-I\"${WINE_INCLUDE_BASE_DIR}\"" - "-L\"${WINE_LIBRARY_DIR}\"" - "\"${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp\"" + ARGS -I${CMAKE_BINARY_DIR} + -I${CMAKE_SOURCE_DIR}/include + -I${WINE_INCLUDE_BASE_DIR} + -L${WINE_LIBRARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} -o ../RemoteVstPlugin + # Ensure correct file extension COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" TARGET vstbase OUTPUTS ../RemoteVstPlugin diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19106d917..3d9093c4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,10 @@ IF(NOT ("${OGGVORBIS_INCLUDE_DIR}" STREQUAL "")) INCLUDE_DIRECTORIES("${OGGVORBIS_INCLUDE_DIR}") ENDIF() +IF(NOT ("${LAME_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES("${LAME_INCLUDE_DIRS}") +ENDIF() + # Use libraries in non-standard directories (e.g., another version of Qt) IF(LMMS_BUILD_LINUX) LINK_LIBRARIES(-Wl,--enable-new-dtags) @@ -136,6 +140,7 @@ SET(LMMS_REQUIRED_LIBS ${PULSEAUDIO_LIBRARIES} ${JACK_LIBRARIES} ${OGGVORBIS_LIBRARIES} + ${LAME_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${EXTRA_LIBRARIES} @@ -191,6 +196,7 @@ IF(LMMS_BUILD_WIN32) "${MINGW_PREFIX}/bin/libvorbisfile-3.dll" "${MINGW_PREFIX}/bin/libjpeg-9.dll" "${MINGW_PREFIX}/bin/libogg-0.dll" + "${MINGW_PREFIX}/bin/libmp3lame-0.dll" "${MINGW_PREFIX}/bin/libfftw3f-3.dll" "${MINGW_PREFIX}/bin/libFLAC-8.dll" "${MINGW_PREFIX}/bin/libpng16-16.dll" diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 468f701e5..0391061b6 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -716,19 +716,19 @@ float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) float FloatModel::getRoundedValue() const { - return static_cast( static_cast( value() / step() + 0.5 ) ) * step(); + return qRound( value() / step() ) * step(); } -float FloatModel::getDigitCount() +int FloatModel::getDigitCount() const { float steptemp = step(); int digits = 0; while ( steptemp < 1 ) { - steptemp = steptemp / 0.1f; + steptemp = steptemp * 10.0f; digits++; } return digits; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c0f0e76e1..5dcfa3c42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -69,6 +69,7 @@ set(LMMS_SRCS core/audio/AudioAlsa.cpp core/audio/AudioDevice.cpp core/audio/AudioFileDevice.cpp + core/audio/AudioFileMP3.cpp core/audio/AudioFileOgg.cpp core/audio/AudioFileWave.cpp core/audio/AudioJack.cpp diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 5cdfe3d97..5ccb4d345 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -63,7 +63,12 @@ ConfigManager::ConfigManager() : // If we're in development (lmms is not installed) let's get the source and // binary directories by reading the CMake Cache - QFile cmakeCache(qApp->applicationDirPath() + "/CMakeCache.txt"); + QDir appPath = qApp->applicationDirPath(); + // If in tests, get parent directory + if (appPath.dirName() == "tests") { + appPath.cdUp(); + } + QFile cmakeCache(appPath.absoluteFilePath("CMakeCache.txt")); if (cmakeCache.exists()) { cmakeCache.open(QFile::ReadOnly); QTextStream stream(&cmakeCache); @@ -277,7 +282,8 @@ void ConfigManager::addRecentlyOpenedProject( const QString & file ) { QFileInfo recentFile( file ); if( recentFile.suffix().toLower() == "mmp" || - recentFile.suffix().toLower() == "mmpz" ) + recentFile.suffix().toLower() == "mmpz" || + recentFile.suffix().toLower() == "mpt" ) { m_recentlyOpenedProjects.removeAll( file ); if( m_recentlyOpenedProjects.size() > 50 ) @@ -313,6 +319,16 @@ const QString & ConfigManager::value( const QString & cls, +const QString & ConfigManager::value( const QString & cls, + const QString & attribute, + const QString & defaultVal ) const +{ + const QString & val = value( cls, attribute ); + return val.isEmpty() ? defaultVal : val; +} + + + void ConfigManager::setValue( const QString & cls, const QString & attribute, diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 6bf54a8bc..c1c81119f 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -113,6 +113,7 @@ void LfoController::updateValueBuffer() } m_currentPhase = absFraction( phase - m_phaseOffset ); + m_bufferLastUpdated = s_periods; } void LfoController::updatePhase() diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 5dee4ed16..3c16bce96 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -30,6 +30,7 @@ #include "AudioFileWave.h" #include "AudioFileOgg.h" +#include "AudioFileMP3.h" #ifdef LMMS_HAVE_SCHED_H #include "sched.h" @@ -48,6 +49,15 @@ const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] = &AudioFileOgg::getInst #else NULL +#endif + }, + { ProjectRenderer::MP3File, + QT_TRANSLATE_NOOP( "ProjectRenderer", "Compressed MP3-File (*.mp3)" ), + ".mp3", +#ifdef LMMS_HAVE_MP3LAME + &AudioFileMP3::getInst +#else + NULL #endif }, // ... insert your own file-encoder-infos here... may be one day the @@ -93,6 +103,8 @@ ProjectRenderer::ProjectRenderer( const Mixer::qualitySettings & qualitySettings ProjectRenderer::~ProjectRenderer() { + Engine::mixer()->restoreAudioDevice(); // also deletes audio-dev + Engine::mixer()->changeQuality( m_oldQualitySettings ); } @@ -172,10 +184,11 @@ void ProjectRenderer::run() m_progress = 0; std::pair exportEndpoints = Engine::getSong()->getExportEndpoints(); tick_t startTick = exportEndpoints.first.getTicks(); - tick_t lengthTicks = exportEndpoints.second.getTicks() - startTick; + tick_t endTick = exportEndpoints.second.getTicks(); + tick_t lengthTicks = endTick - startTick; // Continually track and emit progress percentage to listeners - while( Engine::getSong()->isExportDone() == false && + while( exportPos.getTicks() < endTick && Engine::getSong()->isExporting() == true && !m_abort ) { @@ -190,12 +203,8 @@ void ProjectRenderer::run() Engine::getSong()->stopExport(); - const QString f = m_fileDev->outputFile(); - - Engine::mixer()->restoreAudioDevice(); // also deletes audio-dev - Engine::mixer()->changeQuality( m_oldQualitySettings ); - // if the user aborted export-process, the file has to be deleted + const QString f = m_fileDev->outputFile(); if( m_abort ) { QFile( f ).remove(); diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index f5ec42cdb..cb930b087 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -1411,25 +1411,35 @@ void SampleBuffer::setReversed( bool _on ) -QString SampleBuffer::tryToMakeRelative( const QString & _file ) +QString SampleBuffer::tryToMakeRelative( const QString & file ) { - if( QFileInfo( _file ).isRelative() == false ) + if( QFileInfo( file ).isRelative() == false ) { - QString f = QString( _file ).replace( QDir::separator(), '/' ); - QString fsd = ConfigManager::inst()->factorySamplesDir(); - QString usd = ConfigManager::inst()->userSamplesDir(); - fsd.replace( QDir::separator(), '/' ); - usd.replace( QDir::separator(), '/' ); - if( f.startsWith( fsd ) ) + QString f = QString( file ).replace( QDir::separator(), '/' ); + + // First, look in factory samples + // Isolate "samples/" from "data:/samples/" + QString samplesSuffix = ConfigManager::inst()->factorySamplesDir().mid( ConfigManager::inst()->dataDir().length() ); + + // Iterate over all valid "data:/" searchPaths + for ( const QString & path : QDir::searchPaths( "data" ) ) { - return QString( f ).mid( fsd.length() ); + QString samplesPath = QString( path + samplesSuffix ).replace( QDir::separator(), '/' ); + if ( f.startsWith( samplesPath ) ) + { + return QString( f ).mid( samplesPath.length() ); + } } - else if( f.startsWith( usd ) ) + + // Next, look in user samples + QString usd = ConfigManager::inst()->userSamplesDir(); + usd.replace( QDir::separator(), '/' ); + if( f.startsWith( usd ) ) { return QString( f ).mid( usd.length() ); } } - return _file; + return file; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 1af2dc2f3..af359a881 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -464,29 +464,6 @@ void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fp } } -bool Song::isExportDone() const -{ - if ( m_renderBetweenMarkers ) - { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTicks() >= - m_playPos[Mode_PlaySong].m_timeLine->loopEnd().getTicks(); - } - - if( m_exportLoop ) - { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTicks() >= - length() * ticksPerTact(); - } - else - { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTicks() >= - ( length() + 1 ) * ticksPerTact(); - } -} - std::pair Song::getExportEndpoints() const { if ( m_renderBetweenMarkers ) @@ -1077,6 +1054,27 @@ void Song::loadProject( const QString & fileName ) } node = dataFile.content().firstChild(); + + QDomNodeList tclist=dataFile.content().elementsByTagName("trackcontainer"); + m_nLoadingTrack=0; + for( int i=0,n=tclist.count(); imainWindow(), multiExport ); epd.exec(); } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 9d8254cd9..6e406b0a9 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -86,25 +86,18 @@ void TrackContainer::loadSettings( const QDomElement & _this ) static QProgressDialog * pd = NULL; bool was_null = ( pd == NULL ); - int start_val = 0; if( !journalRestore && gui != nullptr ) { if( pd == NULL ) { pd = new QProgressDialog( tr( "Loading project..." ), tr( "Cancel" ), 0, - _this.childNodes().count(), + Engine::getSong()->getLoadingTrackCount(), gui->mainWindow() ); pd->setWindowModality( Qt::ApplicationModal ); pd->setWindowTitle( tr( "Please wait..." ) ); pd->show(); } - else - { - start_val = pd->value(); - pd->setMaximum( pd->maximum() + - _this.childNodes().count() ); - } } QDomNode node = _this.firstChild(); @@ -124,6 +117,14 @@ void TrackContainer::loadSettings( const QDomElement & _this ) if( node.isElement() && !node.toElement().attribute( "metadata" ).toInt() ) { + QString trackName = node.toElement().hasAttribute( "name" ) ? + node.toElement().attribute( "name" ) : + node.firstChild().toElement().attribute( "name" ); + if( pd != NULL ) + { + pd->setLabelText( tr("Loading Track %1 (%2/Total %3)").arg( trackName ). + arg( pd->value() + 1 ).arg( Engine::getSong()->getLoadingTrackCount() ) ); + } Track::create( node.toElement(), this ); } node = node.nextSibling(); @@ -131,7 +132,6 @@ void TrackContainer::loadSettings( const QDomElement & _this ) if( pd != NULL ) { - pd->setValue( start_val + _this.childNodes().count() ); if( was_null ) { delete pd; diff --git a/src/core/audio/AudioFileMP3.cpp b/src/core/audio/AudioFileMP3.cpp new file mode 100644 index 000000000..cce7ec8e4 --- /dev/null +++ b/src/core/audio/AudioFileMP3.cpp @@ -0,0 +1,133 @@ +/* + * AudioFileMP3.cpp - Audio-device which encodes a wave stream into + * an MP3 file. This is used for song export. + * + * Copyright (c) 2017 to present Michael Gregorius + * + * This file is part of LMMS - https://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 "AudioFileMP3.h" + +#ifdef LMMS_HAVE_MP3LAME + +#include "Mixer.h" + +#include + + +AudioFileMP3::AudioFileMP3( OutputSettings const & outputSettings, + const ch_cnt_t channels, + bool & successful, + const QString & file, + Mixer* mixer ) : + AudioFileDevice( outputSettings, channels, file, mixer ) +{ + successful = true; + // For now only accept stereo sources + successful &= channels == 2; + successful &= initEncoder(); + successful &= outputFileOpened(); +} + +AudioFileMP3::~AudioFileMP3() +{ + flushRemainingBuffers(); + tearDownEncoder(); +} + +void AudioFileMP3::writeBuffer( const surroundSampleFrame * _buf, + const fpp_t _frames, + const float _master_gain ) +{ + if (_frames < 1) + { + return; + } + + // TODO Why isn't the gain applied by the driver but inside the device? + std::vector interleavedDataBuffer(_frames * 2); + for (fpp_t i = 0; i < _frames; ++i) + { + interleavedDataBuffer[2*i] = _buf[i][0] * _master_gain; + interleavedDataBuffer[2*i + 1] = _buf[i][1] * _master_gain; + } + + size_t minimumBufferSize = 1.25 * _frames + 7200; + std::vector encodingBuffer(minimumBufferSize); + + int bytesWritten = lame_encode_buffer_interleaved_ieee_float(m_lame, &interleavedDataBuffer[0], _frames, &encodingBuffer[0], static_cast(encodingBuffer.size())); + assert (bytesWritten >= 0); + + writeData(&encodingBuffer[0], bytesWritten); +} + +void AudioFileMP3::flushRemainingBuffers() +{ + // The documentation states that flush should have at least 7200 bytes. So let's be generous. + std::vector encodingBuffer(7200 * 4); + + int bytesWritten = lame_encode_flush(m_lame, &encodingBuffer[0], static_cast(encodingBuffer.size())); + assert (bytesWritten >= 0); + + writeData(&encodingBuffer[0], bytesWritten); +} + +MPEG_mode mapToMPEG_mode(OutputSettings::StereoMode stereoMode) +{ + switch (stereoMode) + { + case OutputSettings::StereoMode_Stereo: + return STEREO; + case OutputSettings::StereoMode_JointStereo: + return JOINT_STEREO; + case OutputSettings::StereoMode_Mono: + return MONO; + default: + return NOT_SET; + } +} + +bool AudioFileMP3::initEncoder() +{ + m_lame = lame_init(); + + // Handle stereo/joint/mono settings + OutputSettings::StereoMode stereoMode = getOutputSettings().getStereoMode(); + lame_set_mode(m_lame, mapToMPEG_mode(stereoMode)); + + // Handle bit rate settings + OutputSettings::BitRateSettings bitRateSettings = getOutputSettings().getBitRateSettings(); + int bitRate = static_cast(bitRateSettings.getBitRate()); + + lame_set_VBR(m_lame, vbr_off); + lame_set_brate(m_lame, bitRate); + + // Add a comment + id3tag_set_comment(m_lame, "Created with LMMS"); + + return lame_init_params(m_lame) != -1; +} + +void AudioFileMP3::tearDownEncoder() +{ + lame_close(m_lame); +} + +#endif diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 526776a73..170f411d2 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -70,8 +70,7 @@ inline int AudioFileOgg::writePage() bool AudioFileOgg::startEncoding() { vorbis_comment vc; - const char * comments = "Cool=This song has been made using Linux " - "MultiMedia Studio"; + const char * comments = "Cool=This song has been made using LMMS"; int comment_length = strlen( comments ); char * user_comments = new char[comment_length + 1]; strcpy( user_comments, comments ); @@ -100,7 +99,7 @@ bool AudioFileOgg::startEncoding() m_rate = 48000; setSampleRate( 48000 ); } - m_serialNo = 0; // track-num? + m_comments = &vc; // comments for ogg-file // Have vorbisenc choose a mode for us @@ -135,6 +134,10 @@ bool AudioFileOgg::startEncoding() vorbis_analysis_init( &m_vd, &m_vi ); vorbis_block_init( &m_vd, &m_vb ); + // We give our ogg file a random serial number and avoid + // 0 and UINT32_MAX which can get you into trouble. + qsrand( time( 0 ) ); + m_serialNo = 0xD0000000 + qrand() % 0x0FFFFFFF; ogg_stream_init( &m_os, m_serialNo ); // Now, build the three header packets and send through to the stream diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index fb6e2901f..45e46b838 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -79,7 +79,12 @@ bool AudioFileWave::startEncoding() outputFile().toUtf8().constData(), #endif SFM_WRITE, &m_si ); + + // Prevent fold overs when encountering clipped data + sf_command(m_sf, SFC_SET_CLIPPING, NULL, SF_TRUE); + sf_set_string ( m_sf, SF_STR_SOFTWARE, "LMMS" ); + return true; } diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index 867ab0066..a17ebf719 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -160,7 +160,7 @@ int calc13octaveband31(float *absspec_buffer, float *subbands, int num_spec, flo { static const int onethirdoctavecenterfr[] = {20, 25, 31, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000}; int i, j; - float f_min, f_max, frequency, bandwith; + float f_min, f_max, frequency, bandwidth; int j_min, j_max=0; float fpower; @@ -189,13 +189,13 @@ static const int onethirdoctavecenterfr[] = {20, 25, 31, 40, 50, 63, 80, 100, 12 { subbands[i]=0; - // calculate bandwith for subband + // calculate bandwidth for subband frequency=onethirdoctavecenterfr[i]; - bandwith=(pow(2, 1.0/3.0)-1)*frequency; + bandwidth=(pow(2, 1.0/3.0)-1)*frequency; - f_min=frequency-bandwith/2.0; - f_max=frequency+bandwith/2.0; + f_min=frequency-bandwidth/2.0; + f_max=frequency+bandwidth/2.0; j_min=(int)(f_min/max_frequency*(float)num_spec); diff --git a/src/core/main.cpp b/src/core/main.cpp index 1363a1210..508822217 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -151,6 +151,7 @@ void printHelp() " [ -i ]\n" " [ --import [-e]]\n" " [ -l ]\n" + " [ -m ]\n" " [ -o ]\n" " [ -p ]\n" " [ -r ] [ options ]\n" @@ -165,7 +166,7 @@ void printHelp() "-c, --config Get the configuration from \n" "-d, --dump Dump XML of compressed file \n" "-f, --format Specify format of render-output where\n" - " Format is either 'wav' or 'ogg'.\n" + " Format is either 'wav', 'ogg' or 'mp3'.\n" " --geometry Specify the size and position of the main window\n" " geometry is .\n" "-h, --help Show this usage information and exit.\n" @@ -178,6 +179,12 @@ void printHelp() " --import [-e] Import MIDI file .\n" " If -e is specified lmms exits after importing the file.\n" "-l, --loop Render as a loop\n" + "-m, --mode Stereo mode used for MP3 export\n" + " Possible values: s, j, m\n" + " s: Stereo\n" + " j: Joint Stereo\n" + " m: Mono\n" + " Default: j\n" "-o, --output Render into \n" " For --render, provide a file path\n" " For --rendertracks, provide a directory path\n" @@ -291,7 +298,7 @@ int main( int argc, char * * argv ) { printf( "LMMS cannot be run as root.\nUse \"--allowroot\" to override.\n\n" ); return EXIT_FAILURE; - } + } #endif QCoreApplication * app = coreOnly ? @@ -299,7 +306,7 @@ int main( int argc, char * * argv ) new MainApplication( argc, argv ); Mixer::qualitySettings qs( Mixer::qualitySettings::Mode_HighQuality ); - OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::Depth_16Bit ); + OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::Depth_16Bit, OutputSettings::StereoMode_JointStereo ); ProjectRenderer::ExportFileFormats eff = ProjectRenderer::WaveFile; // second of two command-line parsing stages @@ -353,7 +360,7 @@ int main( int argc, char * * argv ) printf( "\nOption \"--allowroot\" will be ignored on this platform.\n\n" ); } #endif - + } else if( arg == "--dump" || arg == "-d" ) { @@ -430,6 +437,12 @@ int main( int argc, char * * argv ) { eff = ProjectRenderer::OggFile; } +#endif +#ifdef LMMS_HAVE_MP3LAME + else if( ext == "mp3" ) + { + eff = ProjectRenderer::MP3File; + } #endif else { @@ -485,6 +498,38 @@ int main( int argc, char * * argv ) else { printf( "\nInvalid bitrate %s.\n\n" + "Try \"%s --help\" for more information.\n\n", argv[i], argv[0] ); + return EXIT_FAILURE; + } + } + else if( arg == "--mode" || arg == "-m" ) + { + ++i; + + if( i == argc ) + { + printf( "\nNo stereo mode specified.\n\n" + "Try \"%s --help\" for more information.\n\n", argv[0] ); + return EXIT_FAILURE; + } + + QString const mode( argv[i] ); + + if( mode == "s" ) + { + os.setStereoMode(OutputSettings::StereoMode_Stereo); + } + else if( mode == "j" ) + { + os.setStereoMode(OutputSettings::StereoMode_JointStereo); + } + else if( mode == "m" ) + { + os.setStereoMode(OutputSettings::StereoMode_Mono); + } + else + { + printf( "\nInvalid stereo mode %s.\n\n" "Try \"%s --help\" for more information.\n\n", argv[i], argv[0] ); return EXIT_FAILURE; } @@ -778,10 +823,6 @@ int main( int argc, char * * argv ) " %4" " %5" " " - " " - " %6" - " %7" - " " "" "" ).arg( MainWindow::tr( "There is a recovery file present. " @@ -792,10 +833,6 @@ int main( int argc, char * * argv ) MainWindow::tr( "Recover" ), MainWindow::tr( "Recover the file. Please don't run " "multiple instances of LMMS when you do this." ), - MainWindow::tr( "Ignore" ), - MainWindow::tr( "Launch LMMS as usual but with " - "automatic backup disabled to prevent the " - "present recover file from being overwritten." ), MainWindow::tr( "Discard" ), MainWindow::tr( "Launch a default session and delete " "the restored files. This is not reversible." ) @@ -807,38 +844,32 @@ int main( int argc, char * * argv ) QPushButton * recover; QPushButton * discard; - QPushButton * ignore; QPushButton * exit; - + #if QT_VERSION >= 0x050000 - // setting all buttons to the same roles allows us + // setting all buttons to the same roles allows us // to have a custom layout discard = mb.addButton( MainWindow::tr( "Discard" ), QMessageBox::AcceptRole ); - ignore = mb.addButton( MainWindow::tr( "Ignore" ), - QMessageBox::AcceptRole ); recover = mb.addButton( MainWindow::tr( "Recover" ), QMessageBox::AcceptRole ); - # else + # else // in qt4 the button order is reversed recover = mb.addButton( MainWindow::tr( "Recover" ), QMessageBox::AcceptRole ); - ignore = mb.addButton( MainWindow::tr( "Ignore" ), - QMessageBox::AcceptRole ); discard = mb.addButton( MainWindow::tr( "Discard" ), QMessageBox::AcceptRole ); #endif - + // have a hidden exit button exit = mb.addButton( "", QMessageBox::RejectRole); exit->setVisible(false); - + // set icons recover->setIcon( embed::getIconPixmap( "recover" ) ); discard->setIcon( embed::getIconPixmap( "discard" ) ); - ignore->setIcon( embed::getIconPixmap( "ignore" ) ); mb.setDefaultButton( recover ); mb.setEscapeButton( exit ); @@ -853,13 +884,6 @@ int main( int argc, char * * argv ) fileToLoad = recoveryFile; gui->mainWindow()->setSession( MainWindow::SessionState::Recover ); } - else if( mb.clickedButton() == ignore ) - { - if( autoSaveEnabled ) - { - gui->mainWindow()->setSession( MainWindow::SessionState::Limited ); - } - } else // Exit { return 0; @@ -902,20 +926,19 @@ int main( int argc, char * * argv ) } } // If enabled, open last project if there is one. Else, create - // a new one. Also skip recently opened file if limited session to - // lower the chance of opening an already opened file. + // a new one. else if( ConfigManager::inst()-> value( "app", "openlastproject" ).toInt() && !ConfigManager::inst()-> recentlyOpenedProjects().isEmpty() && - gui->mainWindow()->getSession() != - MainWindow::SessionState::Limited ) + !recoveryFilePresent ) { QString f = ConfigManager::inst()-> recentlyOpenedProjects().first(); QFileInfo recentFile( f ); - if ( recentFile.exists() ) + if ( recentFile.exists() && + recentFile.suffix().toLower() != "mpt" ) { Engine::getSong()->loadProject( f ); } @@ -932,8 +955,7 @@ int main( int argc, char * * argv ) // Finally we start the auto save timer and also trigger the // autosave one time as recover.mmp is a signal to possible other // instances of LMMS. - if( autoSaveEnabled && - gui->mainWindow()->getSession() != MainWindow::SessionState::Limited ) + if( autoSaveEnabled ) { gui->mainWindow()->autoSaveTimerReset(); gui->mainWindow()->autoSave(); diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index 9add4e80b..8ba70bcb7 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -67,6 +67,11 @@ void MidiClient::addPort( MidiPort* port ) void MidiClient::removePort( MidiPort* port ) { + if( ! port ) + { + return; + } + QVector::Iterator it = qFind( m_midiPorts.begin(), m_midiPorts.end(), port ); if( it != m_midiPorts.end() ) diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index a9a28dc5e..43019876e 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -129,11 +129,11 @@ void MidiPort::processInEvent( const MidiEvent& event, const MidiTime& time ) { return; } - } - if( fixedInputVelocity() >= 0 && inEvent.velocity() > 0 ) - { - inEvent.setVelocity( fixedInputVelocity() ); + if( fixedInputVelocity() >= 0 && inEvent.velocity() > 0 ) + { + inEvent.setVelocity( fixedInputVelocity() ); + } } m_midiEventProcessor->processInEvent( inEvent, time ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4d40dcc08..5b4050bca 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -85,6 +85,7 @@ SET(LMMS_SRCS gui/widgets/ToolButton.cpp gui/widgets/ToolTip.cpp gui/widgets/TrackLabelButton.cpp + gui/widgets/TrackRenameLineEdit.cpp gui/widgets/VisualizationWidget.cpp PARENT_SCOPE diff --git a/src/gui/ExportProjectDialog.cpp b/src/gui/ExportProjectDialog.cpp index c4f7696b8..83c7883cc 100644 --- a/src/gui/ExportProjectDialog.cpp +++ b/src/gui/ExportProjectDialog.cpp @@ -58,7 +58,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, int cbIndex = 0; for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) { - if( ProjectRenderer::fileEncodeDevices[i].m_getDevInst != NULL ) + if( ProjectRenderer::fileEncodeDevices[i].isAvailable() ) { // get the extension of this format QString renderExt = ProjectRenderer::fileEncodeDevices[i].m_extension; @@ -128,7 +128,20 @@ void ExportProjectDialog::closeEvent( QCloseEvent * _ce ) } - +OutputSettings::StereoMode mapToStereoMode(int index) +{ + switch (index) + { + case 0: + return OutputSettings::StereoMode_Stereo; + case 1: + return OutputSettings::StereoMode_JointStereo; + case 2: + return OutputSettings::StereoMode_Mono; + default: + return OutputSettings::StereoMode_Stereo; + } +} void ExportProjectDialog::startExport() { @@ -146,7 +159,8 @@ void ExportProjectDialog::startExport() OutputSettings os = OutputSettings( samplerates[ samplerateCB->currentIndex() ], bitRateSettings, - static_cast( depthCB->currentIndex() ) ); + static_cast( depthCB->currentIndex() ), + mapToStereoMode(stereoModeComboBox->currentIndex()) ); m_renderManager = new RenderManager( qs, os, m_ft, m_fileName ); @@ -181,6 +195,8 @@ ProjectRenderer::ExportFileFormats convertIndexToExportFileFormat(int index) return ProjectRenderer::WaveFile; case 1: return ProjectRenderer::OggFile; + case 2: + return ProjectRenderer::MP3File; default: Q_ASSERT(false); break; @@ -195,10 +211,23 @@ void ExportProjectDialog::onFileFormatChanged(int index) ProjectRenderer::ExportFileFormats exportFormat = convertIndexToExportFileFormat(index); - bool bitRateControlsEnabled = exportFormat == ProjectRenderer::OggFile; + bool stereoModeVisible = exportFormat == ProjectRenderer::MP3File; + + bool sampleRateControlsVisible = exportFormat != ProjectRenderer::MP3File; + + bool bitRateControlsEnabled = + (exportFormat == ProjectRenderer::OggFile || + exportFormat == ProjectRenderer::MP3File); + bool bitDepthControlEnabled = exportFormat == ProjectRenderer::WaveFile; + bool variableBitrateVisible = exportFormat != ProjectRenderer::MP3File; + + stereoModeWidget->setVisible(stereoModeVisible); + sampleRateWidget->setVisible(sampleRateControlsVisible); + bitrateWidget->setVisible(bitRateControlsEnabled); + checkBoxVariableBitRate->setVisible(variableBitrateVisible); depthWidget->setVisible(bitDepthControlEnabled); } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 732ce2db8..8c2898da3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -664,10 +664,6 @@ void MainWindow::resetWindowTitle() { title += " - " + tr( "Recover session. Please save your work!" ); } - if( getSession() == Limited ) - { - title += " - " + tr( "Automatic backup disabled. Remember to save your work!" ); - } setWindowTitle( title + " - " + tr( "LMMS %1" ).arg( LMMS_VERSION ) ); } @@ -738,7 +734,7 @@ void MainWindow::clearKeyModifiers() -void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de, QSize const & sizeIfInvisible ) +void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de ) { // If our widget is the main content of a window (e.g. piano roll, FxMixer, etc), // we really care about the position of the *window* - not the position of the widget within its window @@ -761,7 +757,7 @@ void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de, QSize const & _de.setAttribute( "x", normalGeom.x() ); _de.setAttribute( "y", normalGeom.y() ); - QSize sizeToStore = visible ? normalGeom.size() : sizeIfInvisible; + QSize sizeToStore = normalGeom.size(); _de.setAttribute( "width", sizeToStore.width() ); _de.setAttribute( "height", sizeToStore.height() ); } @@ -773,8 +769,8 @@ void MainWindow::restoreWidgetState( QWidget * _w, const QDomElement & _de ) { QRect r( qMax( 1, _de.attribute( "x" ).toInt() ), qMax( 1, _de.attribute( "y" ).toInt() ), - qMax( 100, _de.attribute( "width" ).toInt() ), - qMax( 100, _de.attribute( "height" ).toInt() ) ); + qMax( _w->sizeHint().width(), _de.attribute( "width" ).toInt() ), + qMax( _w->minimumHeight(), _de.attribute( "height" ).toInt() ) ); if( _de.hasAttribute( "visible" ) && !r.isNull() ) { // If our widget is the main content of a window (e.g. piano roll, FxMixer, etc), @@ -880,8 +876,8 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() m_recentlyOpenedProjectsMenu->clear(); QStringList rup = ConfigManager::inst()->recentlyOpenedProjects(); -// The file history goes 30 deep but we only show the 15 -// most recent ones that we can open. +// The file history goes 50 deep but we only show the 15 +// most recent ones that we can open and omit .mpt files. int shownInMenu = 0; for( QStringList::iterator it = rup.begin(); it != rup.end(); ++it ) { @@ -889,6 +885,11 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() if ( recentFile.exists() && *it != ConfigManager::inst()->recoveryFile() ) { + if( recentFile.suffix().toLower() == "mpt" ) + { + continue; + } + m_recentlyOpenedProjectsMenu->addAction( embed::getIconPixmap( "project_file" ), *it ); #ifdef LMMS_BUILD_APPLE @@ -1374,8 +1375,8 @@ void MainWindow::closeEvent( QCloseEvent * _ce ) if( mayChangeProject(true) ) { // delete recovery file - if( ConfigManager::inst()->value( "ui", "enableautosave" ).toInt() - && getSession() != Limited ) + if( ConfigManager::inst()-> + value( "ui", "enableautosave" ).toInt() ) { sessionCleanup(); _ce->accept(); @@ -1562,8 +1563,7 @@ void MainWindow::autoSave() // from the timer where we need to do extra tests. void MainWindow::runAutoSave() { - if( ConfigManager::inst()->value( "ui", "enableautosave" ).toInt() && - getSession() != Limited ) + if( ConfigManager::inst()->value( "ui", "enableautosave" ).toInt() ) { autoSave(); autoSaveTimerReset(); // Reset timer diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index f2fb66cd3..9f135784e 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -119,8 +119,8 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : #endif m_backgroundArtwork( QDir::toNativeSeparators( ConfigManager::inst()->backgroundArtwork() ) ), m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), - m_enableAutoSave( ConfigManager::inst()->value( "ui", "enableautosave" ).toInt() ), - m_enableRunningAutoSave( ConfigManager::inst()->value( "ui", "enablerunningautosave" ).toInt() ), + m_enableAutoSave( ConfigManager::inst()->value( "ui", "enableautosave", "1" ).toInt() ), + m_enableRunningAutoSave( ConfigManager::inst()->value( "ui", "enablerunningautosave", "1" ).toInt() ), m_saveInterval( ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() < 1 ? MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES : ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() ), @@ -131,7 +131,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_syncVSTPlugins( ConfigManager::inst()->value( "ui", "syncvstplugins" ).toInt() ), m_animateAFP(ConfigManager::inst()->value( "ui", - "animateafp").toInt() ), + "animateafp", "1" ).toInt() ), m_printNoteLabels(ConfigManager::inst()->value( "ui", "printnotelabels").toInt() ), m_displayWaveform(ConfigManager::inst()->value( "ui", diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 00bbc3127..06b32ff7f 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -94,6 +94,8 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, connect( updateTimer, SIGNAL( timeout() ), this, SLOT( updatePosition() ) ); updateTimer->start( 50 ); + connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ), + this, SLOT( update() ) ); } diff --git a/src/gui/dialogs/export_project.ui b/src/gui/dialogs/export_project.ui index 2cd466ee3..838fbee1f 100644 --- a/src/gui/dialogs/export_project.ui +++ b/src/gui/dialogs/export_project.ui @@ -7,7 +7,7 @@ 0 0 715 - 447 + 491 @@ -45,39 +45,48 @@ - - - Samplerate: - - - - - - - - 44100 Hz + + + + 0 - - - - 48000 Hz - - - - - 88200 Hz - - - - - 96000 Hz - - - - - 192000 Hz - - + + + + Samplerate: + + + + + + + + 44100 Hz + + + + + 48000 Hz + + + + + 88200 Hz + + + + + 96000 Hz + + + + + 192000 Hz + + + + + @@ -124,6 +133,44 @@ + + + + + 0 + + + + + Stereo mode: + + + + + + + 1 + + + + Stereo + + + + + Joint Stereo + + + + + Mono + + + + + + + diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index e2c58f286..1e94c1b50 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -69,7 +69,7 @@ QPixmap * AutomationEditor::s_toolYFlip = NULL; QPixmap * AutomationEditor::s_toolXFlip = NULL; const QVector AutomationEditor::m_zoomXLevels = - { 8.0f, 4.0f, 2.0f, 1.0f, 0.5f, 0.25f, 0.125f }; + { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; @@ -239,7 +239,7 @@ void AutomationEditor::setCurrentPattern(AutomationPattern * new_pattern ) void AutomationEditor::saveSettings(QDomDocument & doc, QDomElement & dom_parent) { - MainWindow::saveWidgetState(parentWidget(), dom_parent, QSize( 640, 400 )); + MainWindow::saveWidgetState( parentWidget(), dom_parent ); } @@ -1233,18 +1233,20 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } } + // alternating shades for better contrast - // count the bars which disappear on left by scrolling - + float timeSignature = static_cast( Engine::getSong()->getTimeSigModel().getNumerator() ) + / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); float zoomFactor = m_zoomXLevels[m_zoomingXModel.value()]; - int barCount = m_currentPosition / MidiTime::ticksPerTact(); - int leftBars = m_currentPosition * zoomFactor / m_ppt; + //the bars which disappears at the left side by scrolling + int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerTact(); - for( int x = VALUES_WIDTH; x < width() + m_currentPosition * zoomFactor; x += m_ppt, ++barCount ) + //iterates the visible bars and draw the shading on uneven bars + for( int x = VALUES_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppt, ++barCount ) { if( ( barCount + leftBars ) % 2 != 0 ) { - p.fillRect( x - m_currentPosition * zoomFactor, TOP_MARGIN, m_ppt, + p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, TOP_MARGIN, m_ppt, height() - ( SCROLLBAR_SIZE + TOP_MARGIN ), backgroundShade() ); } } @@ -1301,7 +1303,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) //Don't bother doing/rendering anything if there is no automation points if( time_map.size() > 0 ) { - timeMap::iterator it = time_map.begin(); + timeMap::iterator it = time_map.begin(); while( it+1 != time_map.end() ) { // skip this section if it occurs completely before the @@ -1318,7 +1320,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) { break; } - + //NEEDS Change in CSS /*bool is_selected = false; // if we're in move-mode, we may only draw @@ -1358,8 +1360,8 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) for( int i = 0; i < ( it + 1 ).key() - it.key(); i++ ) { path.lineTo( QPointF( xCoordOfTick( it.key() + i ), yCoordOfLevel( values[i] ) ) ); //NEEDS Change in CSS - //drawLevelTick( p, it.key() + i, values[i], is_selected ); - + //drawLevelTick( p, it.key() + i, values[i], is_selected ); + } path.lineTo( QPointF( xCoordOfTick( ( it + 1 ).key() ), yCoordOfLevel( nextValue ) ) ); path.lineTo( QPointF( xCoordOfTick( ( it + 1 ).key() ), yCoordOfLevel( 0 ) ) ); @@ -1535,12 +1537,12 @@ void AutomationEditor::drawLevelTick(QPainter & p, int tick, float value) p.fillRect( x, y_start, rect_width, rect_height, currentColor ); } - + else { printf("not in range\n"); } - + } @@ -1598,12 +1600,12 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) { y++; } - if( we->delta() < 0 ) + else if( we->delta() < 0 ) { y--; } y = qBound( 0, y, m_zoomingYModel.size() - 1 ); - m_zoomingYModel.setValue( y ); + m_zoomingYModel.setValue( y ); } else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) { @@ -1612,7 +1614,7 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) { q--; } - if( we->delta() < 0 ) + else if( we->delta() < 0 ) { q++; } @@ -1624,13 +1626,13 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) { int x = m_zoomingXModel.value(); if( we->delta() > 0 ) - { - x--; - } - if( we->delta() < 0 ) { x++; } + else if( we->delta() < 0 ) + { + x--; + } x = qBound( 0, x, m_zoomingXModel.size() - 1 ); m_zoomingXModel.setValue( x ); } diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index 50ff0c1e3..699ff2524 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -237,7 +237,7 @@ void BBTrackContainerView::removeBBView(int bb) void BBTrackContainerView::saveSettings(QDomDocument& doc, QDomElement& element) { - MainWindow::saveWidgetState(parentWidget(), element, QSize( 640, 400 ) ); + MainWindow::saveWidgetState( parentWidget(), element ); } void BBTrackContainerView::loadSettings(const QDomElement& element) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 4ef28cfab..9b65c6847 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -143,7 +143,7 @@ PianoRoll::PianoRollKeyTypes PianoRoll::prKeyOrder[] = const int DEFAULT_PR_PPT = KEY_LINE_HEIGHT * DefaultStepsPerTact; const QVector PianoRoll::m_zoomLevels = - { 8.0f, 4.0f, 2.0f, 1.0f, 0.5f, 0.25f, 0.125f }; + { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; PianoRoll::PianoRoll() : @@ -805,7 +805,7 @@ void PianoRoll::setBackgroundShade( const QColor & c ) -void PianoRoll::drawNoteRect( QPainter & p, int x, int y, +void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int width, const Note * n, const QColor & noteCol, const QColor & selCol, const int noteOpc, const bool borders ) { @@ -997,6 +997,7 @@ void PianoRoll::shiftPos( int amount ) //shift notes pos by amount } m_pattern->rearrangeAllNotes(); + m_pattern->updateLength(); m_pattern->dataChanged(); // we modified the song @@ -1917,7 +1918,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) { // select the notes within the selection rectangle and // then destroy the selection rectangle - computeSelectedNotes( + computeSelectedNotes( me->modifiers() & Qt::ShiftModifier ); } else if( m_action == ActionMoveNote ) @@ -2459,15 +2460,10 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) note->setPos( MidiTime( pos_ticks ) ); note->setKey( key_num ); - // If dragging beat notes check if pattern should be MelodyPattern - if( note->length() < 0 ) - { - m_pattern->checkType(); - } } } } - } + } else if (m_action == ActionResizeNote) { // When resizing notes: @@ -2475,7 +2471,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // 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 (shift) { // Algorithm: @@ -2495,8 +2491,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) const Note *posteriorNote = nullptr; for (const Note *note : notes) { - if (note->selected() && (posteriorNote == nullptr || - note->oldPos().getTicks() + note->oldLength().getTicks() > + if (note->selected() && (posteriorNote == nullptr || + note->oldPos().getTicks() + note->oldLength().getTicks() > posteriorNote->oldPos().getTicks() + posteriorNote->oldLength().getTicks())) { posteriorNote = note; @@ -2516,9 +2512,9 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) if(note->selected()) { // scale relative start and end positions by scaleFactor - int newStart = stretchStartTick + scaleFactor * + int newStart = stretchStartTick + scaleFactor * (note->oldPos().getTicks() - stretchStartTick); - int newEnd = stretchStartTick + scaleFactor * + int newEnd = stretchStartTick + scaleFactor * (note->oldPos().getTicks()+note->oldLength().getTicks() - stretchStartTick); // if not holding alt, quantize the offsets if(!alt) @@ -2537,7 +2533,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) int newLength = qMax(1, newEnd-newStart); if (note == posteriorNote) { - posteriorDeltaThisFrame = (newStart+newLength) - + posteriorDeltaThisFrame = (newStart+newLength) - (note->pos().getTicks() + note->length().getTicks()); } note->setLength( MidiTime(newLength) ); @@ -2892,18 +2888,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) ++key; } + // Draw alternating shades on bars - // count the bars which disappear on left by scrolling - + float timeSignature = static_cast( Engine::getSong()->getTimeSigModel().getNumerator() ) + / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; - int barCount = m_currentPosition / MidiTime::ticksPerTact(); - int leftBars = m_currentPosition * zoomFactor / m_ppt; + //the bars which disappears at the left side by scrolling + int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerTact(); - for( int x = WHITE_KEY_WIDTH; x < width() + m_currentPosition * zoomFactor; x += m_ppt, ++barCount ) + //iterates the visible bars and draw the shading on uneven bars + for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppt, ++barCount ) { if( ( barCount + leftBars ) % 2 != 0 ) { - p.fillRect( x - m_currentPosition * zoomFactor, PR_TOP_MARGIN, m_ppt, + p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppt, height() - ( PR_BOTTOM_MARGIN + PR_TOP_MARGIN ), backgroundShade() ); } } @@ -3258,7 +3256,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { q--; } - if( we->delta() < 0 ) + else if( we->delta() < 0 ) { q++; } @@ -3272,7 +3270,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { l--; } - if( we->delta() < 0 ) + else if( we->delta() < 0 ) { l++; } @@ -3283,13 +3281,13 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { int z = m_zoomingModel.value(); if( we->delta() > 0 ) - { - z--; - } - if( we->delta() < 0 ) { z++; } + else if( we->delta() < 0 ) + { + z--; + } z = qBound( 0, z, m_zoomingModel.size() - 1 ); // update combobox with zooming-factor m_zoomingModel.setValue( z ); @@ -3920,27 +3918,32 @@ int PianoRoll::quantization() const void PianoRoll::quantizeNotes() { + if( ! hasValidPattern() ) + { + return; + } + NoteVector notes = getSelectedNotes(); - if (notes.empty()) + if( notes.empty() ) { - for (Note* n : m_pattern->notes()) + for( Note* n : m_pattern->notes() ) { - notes.push_back(n); + notes.push_back( n ); } } - for (Note* n : notes) + for( Note* n : notes ) { - if (n->length() == MidiTime(0)) + if( n->length() == MidiTime( 0 ) ) { continue; } Note copy(*n); - m_pattern->removeNote(n); - copy.quantizePos(quantization()); - m_pattern->addNote(copy); + m_pattern->removeNote( n ); + copy.quantizePos( quantization() ); + m_pattern->addNote( copy ); } update(); @@ -4389,7 +4392,7 @@ void PianoRollWindow::reset() void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de ) { - MainWindow::saveWidgetState( this, de, QSize( 640, 480 ) ); + MainWindow::saveWidgetState( this, de ); } diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 699fa5eac..cf561f179 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -71,7 +71,7 @@ void positionLine::paintEvent( QPaintEvent * pe ) } const QVector SongEditor::m_zoomLevels = - { 16.0f, 8.0f, 4.0f, 2.0f, 1.0f, 0.5f, 0.25f, 0.125f }; + { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; SongEditor::SongEditor( Song * song ) : @@ -262,7 +262,7 @@ SongEditor::~SongEditor() void SongEditor::saveSettings( QDomDocument& doc, QDomElement& element ) { - MainWindow::saveWidgetState(parentWidget(), element, QSize( 640, 400 )); + MainWindow::saveWidgetState( parentWidget(), element ); } void SongEditor::loadSettings( const QDomElement& element ) @@ -360,13 +360,13 @@ void SongEditor::wheelEvent( QWheelEvent * we ) int z = m_zoomingModel->value(); if( we->delta() > 0 ) - { - z--; - } - if( we->delta() < 0 ) { z++; } + else if( we->delta() < 0 ) + { + z--; + } z = qBound( 0, z, m_zoomingModel->size() - 1 ); // update combobox with zooming-factor m_zoomingModel->setValue( z ); @@ -586,6 +586,14 @@ void SongEditor::updatePosition( const MidiTime & t ) +void SongEditor::updatePositionLine() +{ + m_positionLine->setFixedHeight( height() ); +} + + + + void SongEditor::zoomingChanged() { setPixelsPerTact( m_zoomLevels[m_zoomingModel->value()] * DEFAULT_PIXELS_PER_TACT ); @@ -697,6 +705,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : zoomToolBar->addWidget( m_zoomingComboBox ); connect(song, SIGNAL(projectLoaded()), this, SLOT(adjustUiAfterProjectLoad())); + connect(this, SIGNAL(resized()), m_editor, SLOT(updatePositionLine())); } QSize SongEditorWindow::sizeHint() const @@ -705,6 +714,14 @@ QSize SongEditorWindow::sizeHint() const } + + +void SongEditorWindow::resizeEvent(QResizeEvent *event) +{ + emit resized(); +} + + void SongEditorWindow::play() { emit playTriggered(); diff --git a/src/gui/widgets/ControllerRackView.cpp b/src/gui/widgets/ControllerRackView.cpp index cf1cf3c83..79e1bafa0 100644 --- a/src/gui/widgets/ControllerRackView.cpp +++ b/src/gui/widgets/ControllerRackView.cpp @@ -102,7 +102,7 @@ ControllerRackView::~ControllerRackView() void ControllerRackView::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - MainWindow::saveWidgetState( this, _this, QSize( 400, 300) ); + MainWindow::saveWidgetState( this, _this ); } diff --git a/src/gui/widgets/EffectRackView.cpp b/src/gui/widgets/EffectRackView.cpp index 2fa25d575..b3f468d0e 100644 --- a/src/gui/widgets/EffectRackView.cpp +++ b/src/gui/widgets/EffectRackView.cpp @@ -39,7 +39,7 @@ EffectRackView::EffectRackView( EffectChain* model, QWidget* parent ) : ModelView( NULL, this ) { QVBoxLayout* mainLayout = new QVBoxLayout( this ); - mainLayout->setMargin( 0 ); + mainLayout->setMargin( 5 ); m_effectsGroupBox = new GroupBox( tr( "EFFECTS CHAIN" ) ); mainLayout->addWidget( m_effectsGroupBox ); diff --git a/src/gui/widgets/GroupBox.cpp b/src/gui/widgets/GroupBox.cpp index fbcf88ed7..158390bb5 100644 --- a/src/gui/widgets/GroupBox.cpp +++ b/src/gui/widgets/GroupBox.cpp @@ -41,8 +41,6 @@ GroupBox::GroupBox( const QString & _caption, QWidget * _parent ) : m_caption( _caption ), m_titleBarHeight( 11 ) { - updatePixmap(); - m_led = new PixmapButton( this, _caption ); m_led->setCheckable( true ); m_led->move( 3, 0 ); @@ -84,60 +82,22 @@ void GroupBox::mousePressEvent( QMouseEvent * _me ) -void GroupBox::resizeEvent( QResizeEvent * _ev ) +void GroupBox::paintEvent( QPaintEvent * pe ) { - updatePixmap(); - QWidget::resizeEvent( _ev ); -} + QPainter p( this ); - - -void GroupBox::updatePixmap() -{ - QColor bg_color = QApplication::palette().color( QPalette::Active, - QPalette::Background ); - QPixmap pm( size() ); - pm.fill( bg_color/*.dark( 132 )*/ ); - - QPainter p( &pm ); + // Draw background + p.fillRect( 0, 0, width() - 1, height() - 1, p.background() ); // outer rect - p.setPen( bg_color.dark( 150 ) ); + p.setPen( p.background().color().dark( 150 ) ); p.drawRect( 0, 0, width() - 1, height() - 1 ); - // brighter line at bottom/right - p.setPen( bg_color.light( 150 ) ); - p.drawLine( width() - 1, 0, width() - 1, height() - 1 ); - p.drawLine( 0, height() - 1, width() - 1, height() - 1 ); - - // draw groupbox-titlebar - QLinearGradient g( 0, 0, 0, m_titleBarHeight ); - g.setColorAt( 0, bg_color.darker( 250 ) ); - g.setColorAt( 0.1, bg_color.lighter( 120 ) ); - g.setColorAt( 1, bg_color.darker( 250 ) ); - p.fillRect( 2, 2, width() - 4, m_titleBarHeight, g ); - // draw line below titlebar - p.setPen( bg_color.dark( 400 ) ); - p.drawLine( 1, m_titleBarHeight + 1, width() - 3, m_titleBarHeight + 1 ); + p.fillRect( 1, 1, width() - 2, m_titleBarHeight + 1, p.background().color().darker( 150 ) ); - // black inner rect - p.drawRect( 1, 1, width() - 3, height() - 3 ); - - - //p.setPen( QColor( 255, 255, 255 ) ); + // draw text p.setPen( palette().color( QPalette::Active, QPalette::Text ) ); p.setFont( pointSize<8>( font() ) ); p.drawText( 22, m_titleBarHeight, m_caption ); - - QPalette pal = palette(); - pal.setBrush( backgroundRole(), QBrush( pm ) ); - setPalette( pal ); } - - - - - - - diff --git a/src/gui/widgets/InstrumentSoundShapingView.cpp b/src/gui/widgets/InstrumentSoundShapingView.cpp index 20eaaba5e..aa64b5596 100644 --- a/src/gui/widgets/InstrumentSoundShapingView.cpp +++ b/src/gui/widgets/InstrumentSoundShapingView.cpp @@ -76,7 +76,8 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : { m_envLfoViews[i] = new EnvelopeAndLfoView( m_targetsTabWidget ); m_targetsTabWidget->addTab( m_envLfoViews[i], - tr( InstrumentSoundShaping::targetNames[i][0].toUtf8().constData() ) ); + tr( InstrumentSoundShaping::targetNames[i][0].toUtf8().constData() ), + NULL ); } diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 2c1739540..73ef42758 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -631,7 +631,6 @@ void Knob::mouseMoveEvent( QMouseEvent * _me ) emit sliderMoved( model()->value() ); QCursor::setPos( mapToGlobal( m_origMousePos ) ); } - s_textFloat->setText( displayValue() ); } @@ -735,7 +734,7 @@ void Knob::setPosition( const QPoint & _p ) float newValue = value * ratio; if( qAbs( newValue ) >= step ) { - float roundedValue = static_cast( static_cast( ( oldValue - newValue ) / step + 0.5 ) ) * step; + float roundedValue = qRound( ( oldValue - value ) / step ) * step; model()->setValue( roundedValue ); m_leftOver = 0.0f; } @@ -749,7 +748,7 @@ void Knob::setPosition( const QPoint & _p ) { if( qAbs( value ) >= step ) { - float roundedValue = static_cast( static_cast( ( oldValue - value ) / step + 0.5 ) ) * step; + float roundedValue = qRound( ( oldValue - value ) / step ) * step; model()->setValue( roundedValue ); m_leftOver = 0.0f; } diff --git a/src/gui/widgets/ProjectNotes.cpp b/src/gui/widgets/ProjectNotes.cpp index 209b89887..d8d6636c4 100644 --- a/src/gui/widgets/ProjectNotes.cpp +++ b/src/gui/widgets/ProjectNotes.cpp @@ -67,7 +67,7 @@ ProjectNotes::ProjectNotes() : setupActions(); setCentralWidget( m_edit ); - setWindowTitle( tr( "Project notes" ) ); + setWindowTitle( tr( "Project Notes" ) ); setWindowIcon( embed::getIconPixmap( "project_notes" ) ); gui->mainWindow()->addWindowedWidget( this ); @@ -89,7 +89,7 @@ ProjectNotes::~ProjectNotes() void ProjectNotes::clear() { - m_edit->setHtml( tr( "Put down your project notes here." ) ); + m_edit->setHtml( tr( "Enter project notes here" ) ); m_edit->selectAll(); m_edit->setTextColor( QColor( 224, 224, 224 ) ); QTextCursor cursor = m_edit->textCursor(); @@ -438,7 +438,7 @@ void ProjectNotes::alignmentChanged( int _a ) void ProjectNotes::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - MainWindow::saveWidgetState( this, _this, QSize( 640, 400 ) ); + MainWindow::saveWidgetState( this, _this ); QDomCDATASection ds = _doc.createCDATASection( m_edit->toHtml() ); _this.appendChild( ds ); diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 8048ecc0e..6fde217c1 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -2,7 +2,7 @@ * TabWidget.cpp - tabwidget for LMMS * * Copyright (c) 2005-2014 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -27,53 +27,71 @@ #include #include +#include +#include +#include #include "gui_templates.h" +#include "embed.h" - - -TabWidget::TabWidget( const QString & _caption, QWidget * _parent ) : - QWidget( _parent ), +TabWidget::TabWidget( const QString & caption, QWidget * parent, bool usePixmap ) : + QWidget( parent ), m_activeTab( 0 ), - m_caption( _caption ), - m_tabheight( _caption.isEmpty() ? 11: 10 ) + m_caption( caption ), + m_usePixmap( usePixmap ), + m_tabText( 0, 0, 0 ), + m_tabTitleText( 0, 0, 0 ), + m_tabSelected( 0, 0, 0 ), + m_tabBackground( 0, 0, 0 ), + m_tabBorder( 0, 0, 0 ) { + + // Create taller tabbar when it's to display artwork tabs + m_tabbarHeight = usePixmap ? GRAPHIC_TAB_HEIGHT : TEXT_TAB_HEIGHT; + + m_tabheight = caption.isEmpty() ? m_tabbarHeight - 3 : m_tabbarHeight - 4; + setFont( pointSize<8>( font() ) ); setAutoFillBackground( true ); - QColor bg_color = QApplication::palette().color( QPalette::Active, - QPalette::Background ). - darker( 132 ); + QColor bg_color = QApplication::palette().color( QPalette::Active, QPalette::Background ). darker( 132 ); QPalette pal = palette(); pal.setColor( QPalette::Background, bg_color ); setPalette( pal ); + } - - TabWidget::~TabWidget() { } - - -void TabWidget::addTab( QWidget * _w, const QString & _name, int _idx ) +void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, int idx ) { setFont( pointSize<8>( font() ) ); - widgetDesc d = { _w, _name, fontMetrics().width( _name ) + 10 } ; - if( _idx < 0/* || m_widgets.contains( _idx ) == true*/ ) + + // Append tab when position is not given + if( idx < 0/* || m_widgets.contains( idx ) == true*/ ) { - while( m_widgets.contains( ++_idx ) == true ) + while( m_widgets.contains( ++idx ) == true ) { } } - m_widgets[_idx] = d; - _w->setFixedSize( width() - 4, height() - 14 ); - _w->move( 2, 13 ); - _w->hide(); + // Tab's width when it is a text tab. This isn't correct for artwork tabs, but it's fixed later during the PaintEvent + int tab_width = fontMetrics().width( name ) + 10; + + // Register new tab + widgetDesc d = { w, pixmap, name, tab_width }; + m_widgets[idx] = d; + + // Position tab's window + w->setFixedSize( width() - 4, height() - m_tabbarHeight ); + w->move( 2, m_tabbarHeight - 1 ); + w->hide(); + + // Show tab's window if it's active if( m_widgets.contains( m_activeTab ) ) { // make sure new tab doesn't overlap current widget @@ -85,15 +103,15 @@ void TabWidget::addTab( QWidget * _w, const QString & _name, int _idx ) -void TabWidget::setActiveTab( int _idx ) +void TabWidget::setActiveTab( int idx ) { - if( m_widgets.contains( _idx ) ) + if( m_widgets.contains( idx ) ) { int old_active = m_activeTab; - m_activeTab = _idx; + m_activeTab = idx; m_widgets[m_activeTab].w->raise(); m_widgets[m_activeTab].w->show(); - if( old_active != _idx && m_widgets.contains( old_active ) ) + if( old_active != idx && m_widgets.contains( old_active ) ) { m_widgets[old_active].w->hide(); } @@ -102,27 +120,74 @@ void TabWidget::setActiveTab( int _idx ) } - - -void TabWidget::mousePressEvent( QMouseEvent * _me ) +// Return the index of the tab at position "pos" +int TabWidget::findTabAtPos( const QPoint *pos ) { - if( _me->y() > 1 && _me->y() < 13 ) + + if( pos->y() > 1 && pos->y() < m_tabbarHeight - 1 ) { - int cx = ( ( m_caption == "" ) ? 4 : 14 ) + - fontMetrics().width( m_caption ); - for( widgetStack::iterator it = m_widgets.begin(); - it != m_widgets.end(); ++it ) + int cx = ( ( m_caption == "" ) ? 4 : 14 ) + fontMetrics().width( m_caption ); + + for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { - if( _me->x() >= cx && - _me->x() <= cx + ( *it ).nwidth ) + if( pos->x() >= cx && pos->x() <= cx + ( *it ).nwidth ) { - setActiveTab( it.key() ); - update(); - return; + return( it.key() ); } cx += ( *it ).nwidth; } } + + // Haven't found any tab at position "pos" + return( -1 ); +} + + +// Overload the QWidget::event handler to display tooltips (from https://doc.qt.io/qt-4.8/qt-widgets-tooltips-example.html) +bool TabWidget::event(QEvent *event) +{ + + if ( event->type() == QEvent::ToolTip ) + { + QHelpEvent *helpEvent = static_cast(event); + + int idx = findTabAtPos( & helpEvent->pos() ); + + if ( idx != -1 ) + { + // Display tab's tooltip + QToolTip::showText( helpEvent->globalPos(), m_widgets[idx].name ); + } + else + { + // The tooltip event doesn't relate to any tab, let's ignore it + QToolTip::hideText(); + event->ignore(); + } + + return true; + } + + // not a Tooltip event, let's propagate it to the other event handlers + return QWidget::event(event); +} + + +// Activate tab when clicked +void TabWidget::mousePressEvent( QMouseEvent * me ) +{ + + // Find index of tab that has been clicked + QPoint pos = me->pos(); + int idx = findTabAtPos( &pos ); + + // When found, activate tab that has been clicked + if ( idx != -1 ) + { + setActiveTab( idx ); + update(); + return; + } } @@ -133,7 +198,7 @@ void TabWidget::resizeEvent( QResizeEvent * ) for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { - ( *it ).w->setFixedSize( width() - 4, height() - 14 ); + ( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight ); } } @@ -141,66 +206,77 @@ void TabWidget::resizeEvent( QResizeEvent * ) -void TabWidget::paintEvent( QPaintEvent * _pe ) +void TabWidget::paintEvent( QPaintEvent * pe ) { - setFont( pointSize<8>( font() ) ); QPainter p( this ); + p.setFont( pointSize<7>( font() ) ); - QColor bg_color = QApplication::palette().color( QPalette::Active, - QPalette::Background ); - QLinearGradient g( 0, 0, 0, m_tabheight ); - g.setColorAt( 0, bg_color.darker( 250 ) ); - g.setColorAt( 0.1, bg_color.lighter( 120 ) ); - g.setColorAt( 1, bg_color.darker( 250 ) ); + // Draw background + QBrush bg_color = p.background(); p.fillRect( 0, 0, width() - 1, height() - 1, bg_color ); - bool big_tab_captions = ( m_caption == "" ); - - p.setPen( bg_color.darker( 150 ) ); + // Draw external borders + p.setPen( tabBorder() ); p.drawRect( 0, 0, width() - 1, height() - 1 ); - p.setPen( bg_color.light( 150 ) ); - p.drawLine( width() - 1, 0, width() - 1, height() - 1 ); - p.drawLine( 0, height() - 1, width() - 1, height() - 1 ); - - p.setPen( QColor( 0, 0, 0 ) ); - p.drawRect( 1, 1, width() - 3, height() - 3 ); - - p.fillRect( 2, 2, width() - 4, m_tabheight, g ); - p.drawLine( 2, m_tabheight + 2, width() - 3, m_tabheight + 2); + // Draw tabs' bar background + p.fillRect( 1, 1, width() - 2, m_tabheight + 2, tabBackground() ); + // Draw title, if any if( ! m_caption.isEmpty() ) { - p.setPen( QColor( 255, 255, 255 ) ); + p.setFont( pointSize<8>( p.font() ) ); + p.setPen( tabTitleText() ); p.drawText( 5, 11, m_caption ); } // Calculate the tabs' x (tabs are painted next to the caption) int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + fontMetrics().width( m_caption ); - QColor cap_col( 160, 160, 160 ); - if( big_tab_captions ) + // Compute tabs' width depending on the number of tabs (only applicable for artwork tabs) + widgetStack::iterator first = m_widgets.begin(); + widgetStack::iterator last = m_widgets.end(); + int tab_width = width(); + if ( first != last ) { - p.setFont( pointSize<8>( p.font() ) ); - cap_col = QColor( 224, 224, 224 ); + tab_width = ( width() - tab_x_offset ) / std::distance( first, last ); } - else - { - p.setFont( pointSize<7>( p.font() ) ); - } - p.setPen( cap_col ); - - for( widgetStack::iterator it = m_widgets.begin(); - it != m_widgets.end(); ++it ) + // Draw all tabs + p.setPen( tabText() ); + for( widgetStack::iterator it = first ; it != last ; ++it ) { - if( it.key() == m_activeTab ) + // Draw a text tab or a artwork tab. + if( m_usePixmap ) { - p.setPen( QColor( 32, 48, 64 ) ); - p.fillRect( tab_x_offset, 2, ( *it ).nwidth - 6, 10, cap_col ); + // Fixes tab's width, because original size is only correct for text tabs + ( *it ).nwidth = tab_width; + + // Get artwork + QPixmap artwork( embed::getIconPixmap( ( *it ).pixmap ) ); + + // Highlight active tab + if( it.key() == m_activeTab ) + { + p.fillRect( tab_x_offset, 0, ( *it ).nwidth, m_tabbarHeight - 1, tabSelected() ); + } + + // Draw artwork + p.drawPixmap(tab_x_offset + ( ( *it ).nwidth - artwork.width() ) / 2, 1, artwork ); } - p.drawText( tab_x_offset + 3, m_tabheight, ( *it ).name ); - p.setPen( cap_col ); + else + { + // Highlight tab when active + if( it.key() == m_activeTab ) + { + p.fillRect( tab_x_offset, 2, ( *it ).nwidth - 6, m_tabbarHeight - 4, tabSelected() ); + } + + // Draw text + p.drawText( tab_x_offset + 3, m_tabheight + 1, ( *it ).name ); + } + + // Next tab's horizontal position tab_x_offset += ( *it ).nwidth; } } @@ -208,13 +284,16 @@ void TabWidget::paintEvent( QPaintEvent * _pe ) -void TabWidget::wheelEvent( QWheelEvent * _we ) +// Switch between tabs with mouse wheel +void TabWidget::wheelEvent( QWheelEvent * we ) { - if (_we->y() > m_tabheight) + if( we->y() > m_tabheight ) + { return; + } - _we->accept(); - int dir = ( _we->delta() < 0 ) ? 1 : -1; + we->accept(); + int dir = ( we->delta() < 0 ) ? 1 : -1; int tab = m_activeTab; while( tab > -1 && static_cast( tab ) < m_widgets.count() ) { @@ -227,9 +306,62 @@ void TabWidget::wheelEvent( QWheelEvent * _we ) setActiveTab( tab ); } +// Return the color to be used to draw a TabWidget's title text (if any) +QColor TabWidget::tabTitleText() const +{ + return m_tabTitleText; +} +// Set the color to be used to draw a TabWidget's title text (if any) +void TabWidget::setTabTitleText( const QColor & c ) +{ + m_tabTitleText = c; +} +// Return the color to be used to draw a TabWidget's text (if any) +QColor TabWidget::tabText() const +{ + return m_tabText; +} +// Set the color to be used to draw a TabWidget's text (if any) +void TabWidget::setTabText( const QColor & c ) +{ + m_tabText = c; +} +// Return the color to be used to highlight a TabWidget'selected tab (if any) +QColor TabWidget::tabSelected() const +{ + return m_tabSelected; +} +// Set the color to be used to highlight a TabWidget'selected tab (if any) +void TabWidget::setTabSelected( const QColor & c ) +{ + m_tabSelected = c; +} +// Return the color to be used for the TabWidget's background +QColor TabWidget::tabBackground() const +{ + return m_tabBackground; +} + +// Set the color to be used for the TabWidget's background +void TabWidget::setTabBackground( const QColor & c ) +{ + m_tabBackground = c; +} + +// Return the color to be used for the TabWidget's borders +QColor TabWidget::tabBorder() const +{ + return m_tabBorder; +} + +// Set the color to be used for the TabWidget's borders +void TabWidget::setTabBorder( const QColor & c ) +{ + m_tabBorder = c; +} diff --git a/src/gui/widgets/TrackLabelButton.cpp b/src/gui/widgets/TrackLabelButton.cpp index 231509e74..db310a05e 100644 --- a/src/gui/widgets/TrackLabelButton.cpp +++ b/src/gui/widgets/TrackLabelButton.cpp @@ -36,6 +36,7 @@ #include "InstrumentTrack.h" #include "RenameDialog.h" #include "Song.h" +#include "TrackRenameLineEdit.h" @@ -48,7 +49,7 @@ TrackLabelButton::TrackLabelButton( TrackView * _tv, QWidget * _parent ) : setAcceptDrops( true ); setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); - m_renameLineEdit = new QLineEdit( this ); + m_renameLineEdit = new TrackRenameLineEdit( this ); m_renameLineEdit->hide(); if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() ) @@ -83,8 +84,8 @@ void TrackLabelButton::rename() if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() ) { QString txt = m_trackView->getTrack()->name(); - RenameDialog rename_dlg( txt ); - rename_dlg.exec(); + RenameDialog renameDlg( txt ); + renameDlg.exec(); if( txt != text() ) { m_trackView->getTrack()->setName( txt ); diff --git a/src/gui/widgets/TrackRenameLineEdit.cpp b/src/gui/widgets/TrackRenameLineEdit.cpp new file mode 100644 index 000000000..b68af3141 --- /dev/null +++ b/src/gui/widgets/TrackRenameLineEdit.cpp @@ -0,0 +1,61 @@ +/* + * TrackRenameLineEdit.cpp - implementation of class TrackRenameLineEdit, which + * represents the text field that appears when one + * double-clicks a track's label to rename it + * + * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2017 Alexandre Almeida + * + * This file is part of LMMS - https://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 "TrackRenameLineEdit.h" + +#include + + + +TrackRenameLineEdit::TrackRenameLineEdit( QWidget * parent ) : + QLineEdit( parent ) +{ +} + + + + +void TrackRenameLineEdit::show() +{ + m_oldName = text(); + QLineEdit::show(); +} + + + + +void TrackRenameLineEdit::keyPressEvent( QKeyEvent * ke ) +{ + if( ke->key() == Qt::Key_Escape ) + { + setText( m_oldName ); + hide(); + } + + QLineEdit::keyPressEvent( ke ); +} diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 727a3e46a..0de64a29f 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -11,6 +11,7 @@ #cmakedefine LMMS_HAVE_ALSA #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK +#cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS #cmakedefine LMMS_HAVE_SNDIO diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 80b5427b1..b847c85e6 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1411,8 +1411,8 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : generalSettingsLayout->addLayout( basicControlsLayout ); - m_tabWidget = new TabWidget( "", this ); - m_tabWidget->setFixedHeight( INSTRUMENT_HEIGHT + 10 ); + m_tabWidget = new TabWidget( "", this, true ); + m_tabWidget->setFixedHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 ); // create tab-widgets @@ -1439,11 +1439,11 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_miscView = new InstrumentMiscView( m_track, m_tabWidget ); - m_tabWidget->addTab( m_ssView, tr( "ENV/LFO" ), 1 ); - m_tabWidget->addTab( instrumentFunctions, tr( "FUNC" ), 2 ); - m_tabWidget->addTab( m_effectView, tr( "FX" ), 3 ); - m_tabWidget->addTab( m_midiView, tr( "MIDI" ), 4 ); - m_tabWidget->addTab( m_miscView, tr( "MISC" ), 5 ); + m_tabWidget->addTab( m_ssView, tr( "Envelope, filter & LFO" ), "env_lfo_tab", 1 ); + m_tabWidget->addTab( instrumentFunctions, tr( "Chord stacking & arpeggio" ), "func_tab", 2 ); + m_tabWidget->addTab( m_effectView, tr( "Effects" ), "fx_tab", 3 ); + m_tabWidget->addTab( m_midiView, tr( "MIDI settings" ), "midi_tab", 4 ); + m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 ); // setup piano-widget m_pianoView = new PianoView( this ); @@ -1617,7 +1617,7 @@ void InstrumentTrackWindow::updateInstrumentView() if( m_track->m_instrument != NULL ) { m_instrumentView = m_track->m_instrument->createView( m_tabWidget ); - m_tabWidget->addTab( m_instrumentView, tr( "PLUGIN" ), 0 ); + m_tabWidget->addTab( m_instrumentView, tr( "Plugin" ), "plugin_tab", 0 ); m_tabWidget->setActiveTab( 0 ); m_ssView->setFunctionsHidden( m_track->m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 720a2bf6a..788dfd9ec 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -361,9 +361,7 @@ void Pattern::checkType() NoteVector::Iterator it = m_notes.begin(); while( it != m_notes.end() ) { - if( ( *it )->length() > 0 || - ( *it )->pos() % ( MidiTime::ticksPerTact() / - MidiTime::stepsPerTact() ) ) + if( ( *it )->length() > 0 ) { setType( MelodyPattern ); return; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 2152c0cc1..45f587607 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -71,6 +71,8 @@ SampleTCO::SampleTCO( Track * _track ) : { connect( timeLine, SIGNAL( positionMarkerMoved() ), this, SLOT( playbackPositionChanged() ) ); } + //playbutton clicked or space key / on Export Song set isPlaying to false + connect( Engine::getSong(), SIGNAL( playbackStateChanged() ), this, SLOT( playbackPositionChanged() ) ); //care about loops connect( Engine::getSong(), SIGNAL( updateSampleTracks() ), this, SLOT( playbackPositionChanged() ) ); //care about mute TCOs @@ -79,11 +81,6 @@ SampleTCO::SampleTCO( Track * _track ) : connect( getTrack()->getMutedModel(), SIGNAL( dataChanged() ),this, SLOT( playbackPositionChanged() ) ); //care about TCO position connect( this, SIGNAL( positionChanged() ), this, SLOT( updateTrackTcos() ) ); - //playbutton clicked or space key - if( gui ) - { - connect( gui->songEditor(), SIGNAL( playTriggered() ), this, SLOT( playbackPositionChanged() ) ); - } switch( getTrack()->trackContainer()->type() ) { @@ -148,7 +145,7 @@ void SampleTCO::setSampleBuffer( SampleBuffer* sb ) void SampleTCO::setSampleFile( const QString & _sf ) { m_sampleBuffer->setAudioFile( _sf ); - updateLength(); + changeLength( (int) ( m_sampleBuffer->frames() / Engine::framesPerTick() ) ); emit sampleChanged(); emit playbackPositionChanged(); @@ -169,7 +166,8 @@ void SampleTCO::toggleRecord() void SampleTCO::playbackPositionChanged() { Engine::mixer()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle ); - m_isPlaying = false; + SampleTrack * st = dynamic_cast( getTrack() ); + st->setPlayingTcos( false ); } @@ -199,7 +197,7 @@ void SampleTCO::setIsPlaying(bool isPlaying) void SampleTCO::updateLength() { - changeLength( sampleLength() ); + emit sampleChanged(); } @@ -505,12 +503,6 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) // disable antialiasing for borders, since its not needed p.setRenderHint( QPainter::Antialiasing, false ); - if( r.width() < width() - 1 ) - { - p.drawLine( r.x(), r.y() + r.height() / 2, - rect().right() - TCO_BORDER_WIDTH, r.y() + r.height() / 2 ); - } - // inner border p.setPen( c.lighter( 160 ) ); p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, @@ -716,12 +708,21 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) void SampleTrack::updateTcos() +{ + Engine::mixer()->removePlayHandlesOfTypes( this, PlayHandle::TypeSamplePlayHandle ); + setPlayingTcos( false ); +} + + + + +void SampleTrack::setPlayingTcos( bool isPlaying ) { for( int i = 0; i < numOfTCOs(); ++i ) { TrackContentObject * tco = getTCO( i ); SampleTCO * sTco = dynamic_cast( tco ); - sTco->playbackPositionChanged(); + sTco->setIsPlaying( isPlaying ); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2367ea4bb..8cbe23858 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,7 @@ ADD_EXECUTABLE(tests $ src/core/ProjectVersionTest.cpp + src/core/RelativePathsTest.cpp src/tracks/AutomationTrackTest.cpp ) diff --git a/tests/src/core/RelativePathsTest.cpp b/tests/src/core/RelativePathsTest.cpp new file mode 100644 index 000000000..555fa39b5 --- /dev/null +++ b/tests/src/core/RelativePathsTest.cpp @@ -0,0 +1,48 @@ +/* + * RelativePathsTest.cpp + * + * Copyright (c) 2017 Tres Finocchiaro + * + * This file is part of LMMS - https://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 "QTestSuite.h" + +#include "ConfigManager.h" +#include "SampleBuffer.h" + +#include + +class RelativePathsTest : QTestSuite +{ + Q_OBJECT +private slots: + void RelativePathComparisonTests() + { + QFileInfo fi(ConfigManager::inst()->factorySamplesDir() + "/drums/kick01.ogg"); + QVERIFY(fi.exists()); + + QString absPath = fi.absoluteFilePath(); + QString relPath = "drums/kick01.ogg"; + QCOMPARE(SampleBuffer::tryToMakeRelative(absPath), relPath); + QCOMPARE(SampleBuffer::tryToMakeAbsolute(relPath), absPath); + } +} RelativePathTests; + +#include "RelativePathsTest.moc"