Merge branch 'stable-1.2'
# Conflicts: # .gitmodules # .travis.yml # .travis/linux..before_install.sh # .travis/linux..install.sh # CMakeLists.txt # cmake/linux/package_linux.sh.in # cmake/modules/BuildPlugin.cmake # include/AutomatableModel.h # plugins/MidiImport/MidiImport.cpp # plugins/carlapatchbay/CMakeLists.txt # plugins/carlarack/CMakeLists.txt # src/core/Song.cpp # src/core/Track.cpp # src/gui/editors/SongEditor.cpp # tests/src/core/AutomatableModelTest.cpp
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -37,3 +37,6 @@
|
||||
[submodule "src/3rdparty/ringbuffer"]
|
||||
path = src/3rdparty/ringbuffer
|
||||
url = https://github.com/JohannesLorenz/ringbuffer.git
|
||||
[submodule "plugins/carlabase/carla"]
|
||||
path = plugins/carlabase/carla
|
||||
url = https://github.com/falktx/carla
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: required
|
||||
cache:
|
||||
directories:
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
sudo add-apt-repository ppa:beineri/opt-qt592-trusty -y
|
||||
sudo add-apt-repository ppa:andrewrk/libgroove -y
|
||||
sudo sed -e "s/trusty/precise/" -i \
|
||||
/etc/apt/sources.list.d/andrewrk-libgroove-trusty.list
|
||||
sudo add-apt-repository ppa:beineri/opt-qt592-xenial -y
|
||||
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update -qq || true
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev
|
||||
libasound2-dev libjack-jackd2-dev libsdl-dev libsamplerate0-dev libstk0-dev stk
|
||||
libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev
|
||||
libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev fluid
|
||||
libgig-dev libsoundio-dev qt59base qt59translations qt59tools"
|
||||
|
||||
# swh build dependencies
|
||||
@@ -18,9 +18,3 @@ PACKAGES="$PACKAGES $SWH_PACKAGES $VST_PACKAGES libjack-jackd2-0"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
sudo apt-get install -y $PACKAGES
|
||||
|
||||
# kxstudio repo offers Carla; avoid package conflicts (wine, etc) by running last
|
||||
sudo add-apt-repository -y ppa:kxstudio-debian/libs
|
||||
sudo add-apt-repository -y ppa:kxstudio-debian/apps
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y carla
|
||||
|
||||
@@ -229,7 +229,8 @@ IF(WANT_CARLA)
|
||||
SET(LMMS_HAVE_CARLA TRUE)
|
||||
SET(STATUS_CARLA "OK")
|
||||
ELSE(CARLA_FOUND)
|
||||
SET(STATUS_CARLA "not found, please install the latest carla")
|
||||
SET(LMMS_HAVE_WEAKCARLA TRUE)
|
||||
SET(STATUS_CARLA "OK (weak linking enabled)")
|
||||
ENDIF(CARLA_FOUND)
|
||||
ENDIF(WANT_CARLA)
|
||||
|
||||
@@ -499,7 +500,7 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
|
||||
# Due to a regression in gcc-4.8.X, we need to disable array-bounds check
|
||||
IF (CMAKE_COMPILER_IS_GNUCXX AND ((CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "4.8.0") OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.8.0") OR LMMS_BUILD_WIN32))
|
||||
SET(WERROR_FLAGS "${WERROR_FLAGS} -Wno-array-bounds")
|
||||
SET(WERROR_FLAGS "${WERROR_FLAGS} -Wno-array-bounds -Wno-attributes")
|
||||
ENDIF()
|
||||
ELSEIF(MSVC)
|
||||
# Remove any existing /W flags
|
||||
|
||||
24
cmake/linux/launch_lmms.sh
Normal file
24
cmake/linux/launch_lmms.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
export PATH="$PATH:/sbin"
|
||||
if command -v carla > /dev/null 2>&1; then
|
||||
CARLAPATH="$(command -v carla)"
|
||||
CARLAPREFIX="${CARLAPATH%/bin*}"
|
||||
echo "Carla appears to be installed on this system at $CARLAPREFIX/lib[64]/carla so we'll use it."
|
||||
export LD_LIBRARY_PATH=$CARLAPREFIX/lib/carla:$CARLAPREFIX/lib64/carla:$LD_LIBRARY_PATH
|
||||
else
|
||||
echo "Carla does not appear to be installed. That's OK, please ignore any related library errors."
|
||||
fi
|
||||
export LD_LIBRARY_PATH=$DIR/usr/lib/:$DIR/usr/lib/lmms:$LD_LIBRARY_PATH
|
||||
# Prevent segfault on VirualBox
|
||||
if lsmod |grep vboxguest > /dev/null 2>&1; then
|
||||
echo "VirtualBox detected. Forcing libgl software rendering."
|
||||
export LIBGL_ALWAYS_SOFTWARE=1;
|
||||
fi
|
||||
if ldconfig -p | grep libjack.so.0 > /dev/null 2>&1; then
|
||||
echo "Jack appears to be installed on this system, so we'll use it."
|
||||
else
|
||||
echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead."
|
||||
export LD_LIBRARY_PATH=$DIR/usr/lib/lmms/optional:$LD_LIBRARY_PATH
|
||||
fi
|
||||
QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms.real "$@"
|
||||
@@ -99,33 +99,8 @@ cp -R /usr/share/stk/rawwaves/ "${APPDIR}usr/share/stk/"
|
||||
|
||||
# Create a wrapper script which calls the lmms executable
|
||||
mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real"
|
||||
# shellcheck disable=SC1083
|
||||
cat >"${APPDIR}usr/bin/lmms" <<EOL
|
||||
#!/usr/bin/env bash
|
||||
DIR="\$( cd "\$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
|
||||
export PATH="\$PATH:/sbin"
|
||||
if which carla > /dev/null 2>&1; then
|
||||
CARLAPATH="\$(which carla)"
|
||||
CARLAPREFIX="\${CARLAPATH%/bin*}"
|
||||
echo "Carla appears to be installed on this system at \$CARLAPREFIX/lib[64]/carla so we'll use it."
|
||||
export LD_LIBRARY_PATH=\$CARLAPREFIX/lib/carla:\$CARLAPREFIX/lib64/carla:\$LD_LIBRARY_PATH
|
||||
else
|
||||
echo "Carla does not appear to be installed. That's OK, please ignore any related library errors."
|
||||
fi
|
||||
export LD_LIBRARY_PATH=\$DIR/usr/lib/:\$DIR/usr/lib/lmms:\$LD_LIBRARY_PATH
|
||||
# Prevent segfault on VirualBox
|
||||
if lsmod |grep vboxguest > /dev/null 2>&1; then
|
||||
echo "VirtualBox detected. Forcing libgl software rendering."
|
||||
export LIBGL_ALWAYS_SOFTWARE=1;
|
||||
fi
|
||||
if ldconfig -p | grep libjack.so.0 > /dev/null 2>&1; then
|
||||
echo "Jack appears to be installed on this system, so we'll use it."
|
||||
else
|
||||
echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead."
|
||||
export LD_LIBRARY_PATH=\$DIR/usr/lib/lmms/optional:\$LD_LIBRARY_PATH
|
||||
fi
|
||||
QT_X11_NO_NATIVE_MENUBAR=1 \$DIR/usr/bin/lmms.real "\$@"
|
||||
EOL
|
||||
|
||||
cp "@CMAKE_CURRENT_SOURCE_DIR@/launch_lmms.sh" "${APPDIR}usr/bin/lmms"
|
||||
|
||||
chmod +x "${APPDIR}usr/bin/lmms"
|
||||
|
||||
@@ -133,7 +108,7 @@ chmod +x "${APPDIR}usr/bin/lmms"
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
# Ensure linuxdeployqt can find shared objects
|
||||
export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":"${APPDIR}usr/lib/lmms/optional":$LD_LIBRARY_PATH
|
||||
|
||||
# Handle wine linking
|
||||
if [ -d "@WINE_32_LIBRARY_DIR@" ]; then
|
||||
@@ -153,8 +128,8 @@ VSTBIN32="${APPDIR}usr/bin/RemoteVstPlugin32.exe.so"
|
||||
VSTBIN64="${APPDIR}usr/bin/RemoteVstPlugin64.exe.so"
|
||||
|
||||
mv "$ZYNLIB" "$ZYNBIN"
|
||||
mv "$VSTLIB32" "$VSTBIN32"
|
||||
mv "$VSTLIB64" "$VSTBIN64"
|
||||
mv "$VSTLIB32" "$VSTBIN32" || true
|
||||
mv "$VSTLIB64" "$VSTBIN64" || true
|
||||
|
||||
# Patch the desktop file
|
||||
sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE"
|
||||
@@ -182,14 +157,15 @@ success "Bundled and relinked dependencies"
|
||||
|
||||
# Link to original location so lmms can find them
|
||||
ln -sr "$ZYNBIN" "$ZYNLIB"
|
||||
ln -sr "$VSTBIN32" "$VSTLIB32"
|
||||
ln -sr "$VSTBIN64" "$VSTLIB64"
|
||||
ln -sr "$VSTBIN32" "$VSTLIB32" || true
|
||||
ln -sr "$VSTBIN64" "$VSTLIB64" || true
|
||||
|
||||
# Remove wine library conflict
|
||||
rm -f "${APPDIR}/usr/lib/libwine.so.1"
|
||||
|
||||
# Use system-provided carla
|
||||
rm -f "${APPDIR}usr/lib/"libcarla*.so
|
||||
rm -f "${APPDIR}usr/lib/lmms/optional/"libcarla*.so
|
||||
|
||||
# Remove bundled jack in LD_LIBRARY_PATH if exists
|
||||
if [ -e "${APPDIR}/usr/lib/libjack.so.0" ]; then
|
||||
|
||||
Binary file not shown.
1
debian/control
vendored
1
debian/control
vendored
@@ -28,6 +28,7 @@ Build-Depends:
|
||||
libsoundio-dev,
|
||||
libstk0-dev,
|
||||
libvorbis-dev,
|
||||
libx11-xcb-dev,
|
||||
libxcb-keysyms1-dev,
|
||||
libxcb-util0-dev,
|
||||
libxml-perl,
|
||||
|
||||
@@ -236,6 +236,7 @@ public:
|
||||
m_centerValue = centerVal;
|
||||
}
|
||||
|
||||
//! link @p m1 and @p m2, let @p m1 take the values of @p m2
|
||||
static void linkModels( AutomatableModel* m1, AutomatableModel* m2 );
|
||||
static void unlinkModels( AutomatableModel* m1, AutomatableModel* m2 );
|
||||
|
||||
@@ -359,7 +360,7 @@ private:
|
||||
template<class T> void roundAt( T &value, const T &where ) const;
|
||||
|
||||
|
||||
ScaleType m_scaleType; //! scale type, linear by default
|
||||
ScaleType m_scaleType; //!< scale type, linear by default
|
||||
float m_value;
|
||||
float m_initValue;
|
||||
float m_minValue;
|
||||
|
||||
@@ -48,6 +48,14 @@ class FileBrowser : public SideBarWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
Create a file browser side bar widget
|
||||
@param directories '*'-separated list of directories to search for.
|
||||
If a directory of factory files should be in the list it
|
||||
must be the last one (for the factory files delimiter to work)
|
||||
@param filter Filter as used in QDir::match
|
||||
@param recurse *to be documented*
|
||||
*/
|
||||
FileBrowser( const QString & directories, const QString & filter,
|
||||
const QString & title, const QPixmap & pm,
|
||||
QWidget * parent, bool dirs_as_items = false, bool recurse = false );
|
||||
@@ -69,8 +77,8 @@ private:
|
||||
|
||||
QLineEdit * m_filterEdit;
|
||||
|
||||
QString m_directories;
|
||||
QString m_filter;
|
||||
QString m_directories; //!< Directories to search, split with '*'
|
||||
QString m_filter; //!< Filter as used in QDir::match()
|
||||
|
||||
bool m_dirsAsItems;
|
||||
bool m_recurse;
|
||||
@@ -163,7 +171,14 @@ private:
|
||||
static QPixmap * s_folderOpenedPixmap;
|
||||
static QPixmap * s_folderLockedPixmap;
|
||||
|
||||
//! Directories that lead here
|
||||
//! Initially, this is just set to the current path of a directory
|
||||
//! If, however, you have e.g. 'TripleOscillator/xyz' in two of the
|
||||
//! file browser's search directories 'a' and 'b', this will have two
|
||||
//! entries 'a/TripleOscillator' and 'b/TripleOscillator'
|
||||
//! and 'xyz' in the tree widget
|
||||
QStringList m_directories;
|
||||
//! Filter as used in QDir::match()
|
||||
QString m_filter;
|
||||
|
||||
int m_dirCount;
|
||||
|
||||
@@ -100,7 +100,7 @@ private:
|
||||
int m_numDigits;
|
||||
int m_marginWidth;
|
||||
|
||||
void initUi( const QString& name, const QString &style = QString("19green") ); //!< to be called by ctors
|
||||
void initUi( const QString& name, const QString &style ); //!< to be called by ctors
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -125,21 +125,13 @@ public:
|
||||
|
||||
void clearKeyModifiers();
|
||||
|
||||
bool isCtrlPressed()
|
||||
{
|
||||
return m_keyMods.m_ctrl;
|
||||
}
|
||||
|
||||
// TODO Remove this function, since m_shift can get stuck down.
|
||||
// [[deprecated]]
|
||||
bool isShiftPressed()
|
||||
{
|
||||
return m_keyMods.m_shift;
|
||||
}
|
||||
|
||||
bool isAltPressed()
|
||||
{
|
||||
return m_keyMods.m_alt;
|
||||
}
|
||||
|
||||
static void saveWidgetState( QWidget * _w, QDomElement & _de );
|
||||
static void restoreWidgetState( QWidget * _w, const QDomElement & _de );
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ const int DEFAULT_TRACK_HEIGHT = 32;
|
||||
|
||||
const int TCO_BORDER_WIDTH = 2;
|
||||
|
||||
char const *const FILENAME_FILTER = "[\\0000-\x1f\"*/:<>?\\\\|\x7f]";
|
||||
|
||||
|
||||
class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject
|
||||
{
|
||||
|
||||
@@ -202,13 +202,19 @@ bool HydrogenImport::readSong()
|
||||
else
|
||||
{
|
||||
unsigned nLayer = 0;
|
||||
QDomNode layerNode = instrumentNode.firstChildElement( "layer" );
|
||||
QDomNode instrumentComponentNode = instrumentNode.firstChildElement("instrumentComponent");
|
||||
if (instrumentComponentNode.isNull())
|
||||
{
|
||||
instrumentComponentNode = instrumentNode;
|
||||
}
|
||||
|
||||
QDomNode layerNode = instrumentComponentNode.firstChildElement( "layer" );
|
||||
while ( ! layerNode.isNull() )
|
||||
{
|
||||
if ( nLayer >= MAX_LAYERS )
|
||||
{
|
||||
printf( "nLayer >= MAX_LAYERS" );
|
||||
continue;
|
||||
printf("nLayer >= MAX_LAYERS\n");
|
||||
break;
|
||||
}
|
||||
QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" );
|
||||
QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" );
|
||||
|
||||
@@ -217,8 +217,7 @@ public:
|
||||
p( NULL ),
|
||||
it_inst( NULL ),
|
||||
isSF2( false ),
|
||||
hasNotes( false ),
|
||||
lastEnd( 0 )
|
||||
hasNotes( false )
|
||||
{ }
|
||||
|
||||
InstrumentTrack * it;
|
||||
@@ -226,7 +225,6 @@ public:
|
||||
Instrument * it_inst;
|
||||
bool isSF2;
|
||||
bool hasNotes;
|
||||
MidiTime lastEnd;
|
||||
QString trackName;
|
||||
|
||||
smfMidiChannel * create( TrackContainer* tc, QString tn )
|
||||
@@ -257,9 +255,11 @@ public:
|
||||
if( trackName != "") {
|
||||
it->setName( tn );
|
||||
}
|
||||
lastEnd = 0;
|
||||
// General MIDI default
|
||||
it->pitchRangeModel()->setInitValue( 2 );
|
||||
|
||||
// Create a default pattern
|
||||
p = dynamic_cast<Pattern*>(it->createTCO(0));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -267,16 +267,37 @@ public:
|
||||
|
||||
void addNote( Note & n )
|
||||
{
|
||||
if( !p || n.pos() > lastEnd + DefaultTicksPerBar )
|
||||
if (!p)
|
||||
{
|
||||
MidiTime pPos = MidiTime( n.pos().getBar(), 0 );
|
||||
p = dynamic_cast<Pattern*>( it->createTCO( 0 ) );
|
||||
p->movePosition( pPos );
|
||||
p = dynamic_cast<Pattern*>(it->createTCO(0));
|
||||
}
|
||||
p->addNote(n, false);
|
||||
hasNotes = true;
|
||||
lastEnd = n.pos() + n.length();
|
||||
n.setPos( n.pos( p->startPosition() ) );
|
||||
p->addNote( n, false );
|
||||
}
|
||||
|
||||
void splitPatterns()
|
||||
{
|
||||
Pattern * newPattern = nullptr;
|
||||
MidiTime lastEnd(0);
|
||||
|
||||
p->rearrangeAllNotes();
|
||||
for (auto n : p->notes())
|
||||
{
|
||||
if (!newPattern || n->pos() > lastEnd + DefaultTicksPerBar)
|
||||
{
|
||||
MidiTime pPos = MidiTime(n->pos().getBar(), 0);
|
||||
newPattern = dynamic_cast<Pattern*>(it->createTCO(0));
|
||||
newPattern->movePosition(pPos);
|
||||
}
|
||||
lastEnd = n->pos() + n->length();
|
||||
|
||||
Note newNote(*n);
|
||||
newNote.setPos(n->pos(newPattern->startPosition()));
|
||||
newPattern->addNote(newNote, false);
|
||||
}
|
||||
|
||||
delete p;
|
||||
p = nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -539,7 +560,11 @@ bool MidiImport::readSMF( TrackContainer* tc )
|
||||
|
||||
for( int c=0; c < 256; ++c )
|
||||
{
|
||||
if( !chs[c].hasNotes && chs[c].it )
|
||||
if (chs[c].hasNotes)
|
||||
{
|
||||
chs[c].splitPatterns();
|
||||
}
|
||||
else if (chs[c].it)
|
||||
{
|
||||
printf(" Should remove empty track\n");
|
||||
// must delete trackView first - but where is it?
|
||||
|
||||
@@ -6,7 +6,30 @@ IF(NOT CMAKE_VERSION VERSION_LESS 3.9)
|
||||
CMAKE_POLICY(SET CMP0068 OLD)
|
||||
ENDIF()
|
||||
|
||||
if(LMMS_HAVE_CARLA)
|
||||
# If Carla was not provided by the system, make a dummy library instead
|
||||
if(LMMS_HAVE_WEAKCARLA)
|
||||
# Mimic the autoconf header
|
||||
FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/autoconf)
|
||||
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/autoconf/config.h "")
|
||||
SET(CARLA_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/includes
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend
|
||||
${CMAKE_CURRENT_BINARY_DIR}/autoconf
|
||||
)
|
||||
ADD_LIBRARY(carla_native-plugin SHARED DummyCarla.cpp)
|
||||
TARGET_INCLUDE_DIRECTORIES(carla_native-plugin PUBLIC ${CARLA_INCLUDE_DIRS})
|
||||
INSTALL(TARGETS carla_native-plugin
|
||||
LIBRARY DESTINATION "${PLUGIN_DIR}/optional"
|
||||
RUNTIME DESTINATION "${PLUGIN_DIR}/optional"
|
||||
)
|
||||
SET(CARLA_LIBRARIES carla_native-plugin)
|
||||
# Set parent scope variables so carlarack and carlapatchbay can see them
|
||||
SET(CARLA_LIBRARIES ${CARLA_LIBRARIES} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS})
|
||||
LINK_DIRECTORIES(${CARLA_LIBRARY_DIRS})
|
||||
@@ -17,4 +40,7 @@ if(LMMS_HAVE_CARLA)
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH_USE_LINK_PATH TRUE
|
||||
INSTALL_RPATH "${CARLA_RPATH}")
|
||||
endif(LMMS_HAVE_CARLA)
|
||||
IF(LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEPENDENCIES(carlabase carla_native-plugin)
|
||||
ENDIF()
|
||||
endif()
|
||||
|
||||
12
plugins/carlabase/DummyCarla.cpp
Normal file
12
plugins/carlabase/DummyCarla.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// A dummy Carla interface
|
||||
#include "CarlaNativePlugin.h"
|
||||
|
||||
const char* carla_get_library_filename() { return nullptr; }
|
||||
const char* carla_get_library_folder() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; }
|
||||
const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; }
|
||||
CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; }
|
||||
1
plugins/carlabase/carla
Submodule
1
plugins/carlabase/carla
Submodule
Submodule plugins/carlabase/carla added at 4ac8ff2ef4
@@ -438,7 +438,8 @@ PluginView* CarlaInstrument::instantiateView(QWidget* parent)
|
||||
// Disable plugin focus per https://bugreports.qt.io/browse/QTBUG-30181
|
||||
#ifndef CARLA_OS_MAC
|
||||
if (QWidget* const window = parent->window())
|
||||
fHost.uiParentId = window->winId();
|
||||
// TODO: Remove cast; Only needed for Qt4
|
||||
fHost.uiParentId = (uintptr_t)window->winId();
|
||||
else
|
||||
#endif
|
||||
fHost.uiParentId = 0;
|
||||
|
||||
BIN
plugins/carlabase/logo.png
Normal file
BIN
plugins/carlabase/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
@@ -1,4 +1,4 @@
|
||||
if(LMMS_HAVE_CARLA)
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEFINITIONS(-DCARLA_PLUGIN_PATCHBAY -DCARLA_PLUGIN_SYNTH)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase")
|
||||
@@ -6,4 +6,4 @@ if(LMMS_HAVE_CARLA)
|
||||
${CARLA_LIBRARY_DIRS})
|
||||
LINK_LIBRARIES(carlabase)
|
||||
BUILD_PLUGIN(carlapatchbay carlapatchbay.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif(LMMS_HAVE_CARLA)
|
||||
endif()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
if(LMMS_HAVE_CARLA)
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEFINITIONS(-DCARLA_PLUGIN_RACK -DCARLA_PLUGIN_SYNTH)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase")
|
||||
@@ -6,4 +6,4 @@ if(LMMS_HAVE_CARLA)
|
||||
${CARLA_LIBRARY_DIRS})
|
||||
LINK_LIBRARIES(carlabase)
|
||||
BUILD_PLUGIN(carlarack carlarack.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif(LMMS_HAVE_CARLA)
|
||||
endif()
|
||||
|
||||
@@ -338,6 +338,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
Engine::mixer()->processingSampleRate() );
|
||||
}
|
||||
m.unlock();
|
||||
static_cast<malletsSynth *>(_n->m_pluginData)->setPresetIndex(p);
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
@@ -345,6 +346,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
|
||||
malletsSynth * ps = static_cast<malletsSynth *>( _n->m_pluginData );
|
||||
ps->setFrequency( freq );
|
||||
p = ps->presetIndex();
|
||||
|
||||
sample_t add_scale = 0.0f;
|
||||
if( p == 10 && m_isOldVersionModel.value() == true )
|
||||
@@ -355,9 +357,9 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
for( fpp_t frame = offset; frame < frames + offset; ++frame )
|
||||
{
|
||||
_working_buffer[frame][0] = ps->nextSampleLeft() *
|
||||
( m_scalers[m_presetsModel.value()] + add_scale );
|
||||
( m_scalers[p] + add_scale );
|
||||
_working_buffer[frame][1] = ps->nextSampleRight() *
|
||||
( m_scalers[m_presetsModel.value()] + add_scale );
|
||||
( m_scalers[p] + add_scale );
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
@@ -579,7 +581,6 @@ void malletsInstrumentView::modelChanged()
|
||||
void malletsInstrumentView::changePreset()
|
||||
{
|
||||
malletsInstrument * inst = castModel<malletsInstrument>();
|
||||
inst->instrumentTrack()->silenceAllNotes();
|
||||
int _preset = inst->m_presetsModel.value();
|
||||
|
||||
if( _preset < 9 )
|
||||
@@ -614,7 +615,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch,
|
||||
const StkFloat _control11,
|
||||
const int _control16,
|
||||
const uint8_t _delay,
|
||||
const sample_rate_t _sample_rate )
|
||||
const sample_rate_t _sample_rate ) :
|
||||
m_presetIndex(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -664,7 +666,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch,
|
||||
const StkFloat _control11,
|
||||
const StkFloat _control128,
|
||||
const uint8_t _delay,
|
||||
const sample_rate_t _sample_rate )
|
||||
const sample_rate_t _sample_rate ) :
|
||||
m_presetIndex(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -712,7 +715,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch,
|
||||
const StkFloat _control64,
|
||||
const StkFloat _control128,
|
||||
const uint8_t _delay,
|
||||
const sample_rate_t _sample_rate )
|
||||
const sample_rate_t _sample_rate ) :
|
||||
m_presetIndex(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -120,8 +120,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
inline int presetIndex()
|
||||
{
|
||||
return m_presetIndex;
|
||||
}
|
||||
|
||||
inline void setPresetIndex(int presetIndex)
|
||||
{
|
||||
m_presetIndex = presetIndex;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
int m_presetIndex;
|
||||
Instrmnt * m_voice;
|
||||
|
||||
StkFloat * m_delay;
|
||||
|
||||
@@ -1085,7 +1085,7 @@ void RemoteVstPlugin::getParameterDump()
|
||||
|
||||
for( int i = 0; i < m_plugin->numParams; ++i )
|
||||
{
|
||||
char paramName[32];
|
||||
char paramName[256];
|
||||
memset( paramName, 0, sizeof( paramName ) );
|
||||
pluginDispatch( effGetParamName, i, 0, paramName );
|
||||
paramName[sizeof(paramName)-1] = 0;
|
||||
@@ -1299,7 +1299,7 @@ void RemoteVstPlugin::savePreset( const std::string & _file )
|
||||
if (!isPreset &&!chunky) uIntToFile = (unsigned int) m_plugin->numPrograms;
|
||||
pBank->numPrograms = endian_swap( uIntToFile );
|
||||
|
||||
FILE * stream = F_OPEN_UTF8( _file, "w" );
|
||||
FILE * stream = F_OPEN_UTF8( _file, "wb" );
|
||||
if (!stream)
|
||||
{
|
||||
fprintf( stderr,
|
||||
@@ -1357,7 +1357,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file )
|
||||
unsigned int * pLen = new unsigned int[ 1 ];
|
||||
unsigned int len = 0;
|
||||
sBank * pBank = (sBank*) new char[ sizeof( sBank ) ];
|
||||
FILE * stream = F_OPEN_UTF8( _file, "r" );
|
||||
FILE * stream = F_OPEN_UTF8( _file, "rb" );
|
||||
if (!stream)
|
||||
{
|
||||
fprintf( stderr,
|
||||
|
||||
@@ -499,8 +499,23 @@ void AutomatableModel::unlinkModel( AutomatableModel* model )
|
||||
|
||||
void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
|
||||
{
|
||||
if (!model1->m_linkedModels.contains( model2 ) && model1 != model2)
|
||||
{
|
||||
// copy data
|
||||
model1->m_value = model2->m_value;
|
||||
if (model1->valueBuffer() && model2->valueBuffer())
|
||||
{
|
||||
std::copy_n(model2->valueBuffer()->data(),
|
||||
model1->valueBuffer()->length(),
|
||||
model1->valueBuffer()->data());
|
||||
}
|
||||
// send dataChanged() before linking (because linking will
|
||||
// connect the two dataChanged() signals)
|
||||
emit model1->dataChanged();
|
||||
// finally: link the models
|
||||
model1->linkModel( model2 );
|
||||
model2->linkModel( model1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -537,6 +537,15 @@ void NotePlayHandle::processMidiTime( const MidiTime& time )
|
||||
|
||||
void NotePlayHandle::resize( const bpm_t _new_tempo )
|
||||
{
|
||||
if (origin() == OriginMidiInput ||
|
||||
(origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput))
|
||||
{
|
||||
// Don't resize notes from MIDI input - they should continue to play
|
||||
// until the key is released, and their large duration can cause
|
||||
// overflows in this method.
|
||||
return;
|
||||
}
|
||||
|
||||
double completed = m_totalFramesPlayed / (double) m_frames;
|
||||
double new_frames = m_origFrames * m_origTempo / (double) _new_tempo;
|
||||
m_frames = (f_cnt_t)new_frames;
|
||||
|
||||
@@ -102,14 +102,14 @@ RemotePlugin::RemotePlugin() :
|
||||
|
||||
m_socketFile = QDir::tempPath() + QDir::separator() +
|
||||
QUuid::createUuid().toString();
|
||||
const char * path = m_socketFile.toUtf8().constData();
|
||||
size_t length = strlen( path );
|
||||
auto path = m_socketFile.toUtf8();
|
||||
size_t length = path.length();
|
||||
if ( length >= sizeof sa.sun_path )
|
||||
{
|
||||
length = sizeof sa.sun_path - 1;
|
||||
qWarning( "Socket path too long." );
|
||||
}
|
||||
memcpy( sa.sun_path, path, length );
|
||||
memcpy(sa.sun_path, path.constData(), length );
|
||||
sa.sun_path[length] = '\0';
|
||||
|
||||
m_server = socket( PF_LOCAL, SOCK_STREAM, 0 );
|
||||
@@ -117,7 +117,7 @@ RemotePlugin::RemotePlugin() :
|
||||
{
|
||||
qWarning( "Unable to start the server." );
|
||||
}
|
||||
remove( path );
|
||||
remove(path.constData());
|
||||
int ret = bind( m_server, (struct sockaddr *) &sa, sizeof sa );
|
||||
if ( ret == -1 || listen( m_server, 1 ) == -1 )
|
||||
{
|
||||
|
||||
@@ -182,7 +182,7 @@ QString RenderManager::pathForTrack(const Track *track, int num)
|
||||
{
|
||||
QString extension = ProjectRenderer::getFileExtensionFromFormat( m_format );
|
||||
QString name = track->name();
|
||||
name = name.remove(QRegExp("[^a-zA-Z]"));
|
||||
name = name.remove(QRegExp(FILENAME_FILTER));
|
||||
name = QString( "%1_%2%3" ).arg( num ).arg( name ).arg( extension );
|
||||
return QDir(m_outputPath).filePath(name);
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ void FileBrowser::addItems(const QString & path )
|
||||
return;
|
||||
}
|
||||
|
||||
// try to add all directories from file system alphabetically into the tree
|
||||
QDir cdir( path );
|
||||
QStringList files = cdir.entryList( QDir::Dirs, QDir::Name );
|
||||
for( QStringList::const_iterator it = files.constBegin();
|
||||
@@ -240,6 +241,7 @@ void FileBrowser::addItems(const QString & path )
|
||||
m_fileBrowserTreeWidget->topLevelItem( i ) );
|
||||
if( d == NULL || cur_file < d->text( 0 ) )
|
||||
{
|
||||
// insert before item, we're done
|
||||
Directory *dd = new Directory( cur_file, path,
|
||||
m_filter );
|
||||
m_fileBrowserTreeWidget->insertTopLevelItem( i,dd );
|
||||
@@ -249,6 +251,11 @@ void FileBrowser::addItems(const QString & path )
|
||||
}
|
||||
else if( cur_file == d->text( 0 ) )
|
||||
{
|
||||
// imagine we have subdirs named "TripleOscillator/xyz" in
|
||||
// two directories from m_directories
|
||||
// then only add one tree widget for both
|
||||
// so we don't add a new Directory - we just
|
||||
// add the path to the current directory
|
||||
d->addDirectory( path );
|
||||
d->update();
|
||||
orphan = false;
|
||||
@@ -257,6 +264,8 @@ void FileBrowser::addItems(const QString & path )
|
||||
}
|
||||
if( orphan )
|
||||
{
|
||||
// it has not yet been added yet, so it's (lexically)
|
||||
// larger than all other dirs => append it at the bottom
|
||||
Directory *d = new Directory( cur_file,
|
||||
path, m_filter );
|
||||
d->update();
|
||||
@@ -768,21 +777,29 @@ void Directory::update( void )
|
||||
if( !childCount() )
|
||||
{
|
||||
m_dirCount = 0;
|
||||
// for all paths leading here, add their items
|
||||
for( QStringList::iterator it = m_directories.begin();
|
||||
it != m_directories.end(); ++it )
|
||||
{
|
||||
int top_index = childCount();
|
||||
int filesBeforeAdd = childCount() - m_dirCount;
|
||||
if( addItems( fullName( *it ) ) &&
|
||||
( *it ).contains(
|
||||
ConfigManager::inst()->dataDir() ) )
|
||||
{
|
||||
QTreeWidgetItem * sep = new QTreeWidgetItem;
|
||||
sep->setText( 0,
|
||||
FileBrowserTreeWidget::tr(
|
||||
"--- Factory files ---" ) );
|
||||
sep->setIcon( 0, embed::getIconPixmap(
|
||||
"factory_files" ) );
|
||||
insertChild( m_dirCount + top_index, sep );
|
||||
// factory file directory is added
|
||||
// note: those are always added last
|
||||
int filesNow = childCount() - m_dirCount;
|
||||
if(filesNow > filesBeforeAdd) // any file appended?
|
||||
{
|
||||
QTreeWidgetItem * sep = new QTreeWidgetItem;
|
||||
sep->setText( 0,
|
||||
FileBrowserTreeWidget::tr(
|
||||
"--- Factory files ---" ) );
|
||||
sep->setIcon( 0, embed::getIconPixmap(
|
||||
"factory_files" ) );
|
||||
// add delimeter after last file before appending our files
|
||||
insertChild( filesBeforeAdd + m_dirCount, sep );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -803,6 +820,7 @@ bool Directory::addItems(const QString & path )
|
||||
|
||||
bool added_something = false;
|
||||
|
||||
// try to add all directories from file system alphabetically into the tree
|
||||
QStringList files = thisDir.entryList( QDir::Dirs, QDir::Name );
|
||||
for( QStringList::const_iterator it = files.constBegin();
|
||||
it != files.constEnd(); ++it )
|
||||
@@ -817,6 +835,7 @@ bool Directory::addItems(const QString & path )
|
||||
child( i ) );
|
||||
if( d == NULL || cur_file < d->text( 0 ) )
|
||||
{
|
||||
// insert before item, we're done
|
||||
insertChild( i, new Directory( cur_file,
|
||||
path, m_filter ) );
|
||||
orphan = false;
|
||||
@@ -825,6 +844,12 @@ bool Directory::addItems(const QString & path )
|
||||
}
|
||||
else if( cur_file == d->text( 0 ) )
|
||||
{
|
||||
// imagine we have top-level subdirs named "TripleOscillator" in
|
||||
// two directories from FileBrowser::m_directories
|
||||
// and imagine both have a sub folder named "xyz"
|
||||
// then only add one tree widget for both
|
||||
// so we don't add a new Directory - we just
|
||||
// add the path to the current directory
|
||||
d->addDirectory( path );
|
||||
orphan = false;
|
||||
break;
|
||||
@@ -832,6 +857,8 @@ bool Directory::addItems(const QString & path )
|
||||
}
|
||||
if( orphan )
|
||||
{
|
||||
// it has not yet been added yet, so it's (lexically)
|
||||
// larger than all other dirs => append it at the bottom
|
||||
addChild( new Directory( cur_file, path,
|
||||
m_filter ) );
|
||||
m_dirCount++;
|
||||
|
||||
@@ -1305,6 +1305,7 @@ void MainWindow::sessionCleanup()
|
||||
|
||||
void MainWindow::focusOutEvent( QFocusEvent * _fe )
|
||||
{
|
||||
// TODO Remove this function, since it is apparently never actually called!
|
||||
// when loosing focus we do not receive key-(release!)-events anymore,
|
||||
// so we might miss release-events of one the modifiers we're watching!
|
||||
clearKeyModifiers();
|
||||
|
||||
@@ -2382,7 +2382,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
m_pattern->instrumentTrack()->processInEvent( evt );
|
||||
}
|
||||
}
|
||||
else if( n->isPlaying() )
|
||||
else if( n->isPlaying() && !isSelection() )
|
||||
{
|
||||
// mouse not over this note, stop playing it.
|
||||
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( n->key() );
|
||||
|
||||
@@ -473,12 +473,13 @@ void SongEditor::toggleProportionalSnap()
|
||||
|
||||
void SongEditor::keyPressEvent( QKeyEvent * ke )
|
||||
{
|
||||
if( ke->modifiers() & Qt::ShiftModifier &&
|
||||
bool isShiftPressed = ke->modifiers() & Qt::ShiftModifier;
|
||||
if( isShiftPressed &&
|
||||
( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) )
|
||||
{
|
||||
m_song->insertBar();
|
||||
}
|
||||
else if( ke->modifiers() & Qt::ShiftModifier &&
|
||||
else if( isShiftPressed &&
|
||||
( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) )
|
||||
{
|
||||
m_song->removeBar();
|
||||
@@ -517,7 +518,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke )
|
||||
}
|
||||
else if( ke->key() == Qt::Key_A && ke->modifiers() & Qt::ControlModifier )
|
||||
{
|
||||
selectAllTcos( !(ke->modifiers() & Qt::ShiftModifier) );
|
||||
selectAllTcos( !isShiftPressed );
|
||||
}
|
||||
else if( ke->key() == Qt::Key_Escape )
|
||||
{
|
||||
|
||||
@@ -52,35 +52,28 @@ TextFloat * Knob::s_textFloat = NULL;
|
||||
|
||||
|
||||
|
||||
//! @todo: in C++11, we can use delegating ctors
|
||||
#define DEFAULT_KNOB_INITIALIZER_LIST \
|
||||
QWidget( _parent ), \
|
||||
FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ), \
|
||||
m_label( "" ), \
|
||||
m_knobPixmap( NULL ), \
|
||||
m_volumeKnob( false ), \
|
||||
m_volumeRatio( 100.0, 0.0, 1000000.0 ), \
|
||||
m_buttonPressed( false ), \
|
||||
m_angle( -10 ), \
|
||||
m_lineWidth( 0 ), \
|
||||
m_textColor( 255, 255, 255 )
|
||||
|
||||
Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) :
|
||||
DEFAULT_KNOB_INITIALIZER_LIST,
|
||||
QWidget( _parent ),
|
||||
FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ),
|
||||
m_label( "" ),
|
||||
m_knobPixmap( NULL ),
|
||||
m_volumeKnob( false ),
|
||||
m_volumeRatio( 100.0, 0.0, 1000000.0 ),
|
||||
m_buttonPressed( false ),
|
||||
m_angle( -10 ),
|
||||
m_lineWidth( 0 ),
|
||||
m_textColor( 255, 255, 255 ),
|
||||
m_knobNum( _knob_num )
|
||||
{
|
||||
initUi( _name );
|
||||
}
|
||||
|
||||
Knob::Knob( QWidget * _parent, const QString & _name ) :
|
||||
DEFAULT_KNOB_INITIALIZER_LIST,
|
||||
m_knobNum( knobBright_26 )
|
||||
Knob( knobBright_26, _parent, _name )
|
||||
{
|
||||
initUi( _name );
|
||||
}
|
||||
|
||||
#undef DEFAULT_KNOB_INITIALIZER_LIST
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -606,7 +599,7 @@ void Knob::mousePressEvent( QMouseEvent * _me )
|
||||
m_buttonPressed = true;
|
||||
}
|
||||
else if( _me->button() == Qt::LeftButton &&
|
||||
gui->mainWindow()->isShiftPressed() == true )
|
||||
(_me->modifiers() & Qt::ShiftModifier) )
|
||||
{
|
||||
new StringPairDrag( "float_value",
|
||||
QString::number( model()->value() ),
|
||||
@@ -808,9 +801,9 @@ void Knob::enterValue()
|
||||
|
||||
void Knob::friendlyUpdate()
|
||||
{
|
||||
if( model()->controllerConnection() == NULL ||
|
||||
if (model() && (model()->controllerConnection() == NULL ||
|
||||
model()->controllerConnection()->getController()->frequentUpdates() == false ||
|
||||
Controller::runningFrames() % (256*4) == 0 )
|
||||
Controller::runningFrames() % (256*4) == 0))
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void LcdSpinBox::mouseMoveEvent( QMouseEvent* event )
|
||||
if( m_mouseMoving )
|
||||
{
|
||||
int dy = event->globalY() - m_origMousePos.y();
|
||||
if( gui->mainWindow()->isShiftPressed() )
|
||||
if( event->modifiers() & Qt::ShiftModifier )
|
||||
dy = qBound( -4, dy/4, 4 );
|
||||
if( dy > 1 || dy < -1 )
|
||||
{
|
||||
|
||||
@@ -39,42 +39,32 @@
|
||||
|
||||
|
||||
|
||||
//! @todo: in C++11, we can use delegating ctors
|
||||
#define DEFAULT_LCDWIDGET_INITIALIZER_LIST \
|
||||
QWidget( parent ), \
|
||||
m_label(), \
|
||||
m_textColor( 255, 255, 255 ), \
|
||||
m_textShadowColor( 64, 64, 64 )
|
||||
|
||||
LcdWidget::LcdWidget( QWidget* parent, const QString& name ) :
|
||||
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
|
||||
m_numDigits( 1 )
|
||||
LcdWidget( 1, parent, name )
|
||||
{
|
||||
initUi( name );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) :
|
||||
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
|
||||
m_numDigits( numDigits )
|
||||
LcdWidget( numDigits, QString("19green"), parent, name )
|
||||
{
|
||||
initUi( name );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) :
|
||||
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
|
||||
QWidget( parent ),
|
||||
m_label(),
|
||||
m_textColor( 255, 255, 255 ),
|
||||
m_textShadowColor( 64, 64, 64 ),
|
||||
m_numDigits( numDigits )
|
||||
{
|
||||
initUi( name, style );
|
||||
}
|
||||
|
||||
#undef DEFAULT_LCDWIDGET_INITIALIZER_LIST
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,13 +39,9 @@ static const QString names[LedCheckBox::NumColors] =
|
||||
|
||||
|
||||
|
||||
//! @todo: in C++11, we can use delegating ctors
|
||||
#define DEFAULT_LEDCHECKBOX_INITIALIZER_LIST \
|
||||
AutomatableButton( _parent, _name )
|
||||
|
||||
LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
|
||||
const QString & _name, LedColors _color ) :
|
||||
DEFAULT_LEDCHECKBOX_INITIALIZER_LIST,
|
||||
AutomatableButton( _parent, _name ),
|
||||
m_text( _text )
|
||||
{
|
||||
initUi( _color );
|
||||
@@ -56,13 +52,10 @@ LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
|
||||
|
||||
LedCheckBox::LedCheckBox( QWidget * _parent,
|
||||
const QString & _name, LedColors _color ) :
|
||||
DEFAULT_LEDCHECKBOX_INITIALIZER_LIST
|
||||
LedCheckBox( QString(), _parent, _name, _color )
|
||||
{
|
||||
initUi( _color );
|
||||
}
|
||||
|
||||
#undef DEFAULT_LEDCHECKBOX_INITIALIZER_LIST
|
||||
|
||||
|
||||
|
||||
LedCheckBox::~LedCheckBox()
|
||||
|
||||
@@ -447,7 +447,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH )
|
||||
}
|
||||
m_midiNotesMutex.unlock();
|
||||
|
||||
lock();
|
||||
Engine::mixer()->requestChangeInModel();
|
||||
// invalidate all NotePlayHandles and PresetPreviewHandles linked to this track
|
||||
m_processHandles.clear();
|
||||
|
||||
@@ -457,7 +457,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH )
|
||||
flags |= PlayHandle::TypeInstrumentPlayHandle;
|
||||
}
|
||||
Engine::mixer()->removePlayHandlesOfTypes( this, flags );
|
||||
unlock();
|
||||
Engine::mixer()->doneChangeInModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -546,11 +546,13 @@ void InstrumentTrack::setName( const QString & _new_name )
|
||||
|
||||
void InstrumentTrack::updateBaseNote()
|
||||
{
|
||||
Engine::mixer()->requestChangeInModel();
|
||||
for( NotePlayHandleList::Iterator it = m_processHandles.begin();
|
||||
it != m_processHandles.end(); ++it )
|
||||
{
|
||||
( *it )->setFrequencyUpdate();
|
||||
}
|
||||
Engine::mixer()->doneChangeInModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -1599,7 +1601,7 @@ void InstrumentTrackWindow::saveSettingsBtnClicked()
|
||||
sfd.setDirectory( presetRoot + m_track->instrumentName() );
|
||||
sfd.setFileMode( FileDialog::AnyFile );
|
||||
QString fname = m_track->name();
|
||||
sfd.selectFile( fname.remove(QRegExp("[^a-zA-Z0-9_\\-\\d\\s]")) );
|
||||
sfd.selectFile(fname.remove(QRegExp(FILENAME_FILTER)));
|
||||
sfd.setDefaultSuffix( "xpf");
|
||||
|
||||
if( sfd.exec() == QDialog::Accepted &&
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* AutomatableModelTest.cpp
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
* Copyright (c) 2019-2020 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -31,7 +31,14 @@ class AutomatableModelTest : QTestSuite
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
bool m1Changed, m2Changed;
|
||||
void resetChanged() { m1Changed = m2Changed = false; }
|
||||
|
||||
private slots: // helper slots
|
||||
void onM1Changed(Model* ) { m1Changed = true; }
|
||||
void onM2Changed(Model* ) { m2Changed = true; }
|
||||
|
||||
private slots: // tests
|
||||
//! Test that upcast and exact casts work,
|
||||
//! but no downcast or any other casts
|
||||
void CastTests()
|
||||
@@ -50,6 +57,45 @@ private slots:
|
||||
QCOMPARE(&intModel, imPtr->dynamicCast<IntModel>()); // same class
|
||||
QVERIFY(nullptr == imPtr->dynamicCast<ComboBoxModel>()); // child class
|
||||
}
|
||||
|
||||
void LinkTests()
|
||||
{
|
||||
BoolModel m1(false), m2(false);
|
||||
|
||||
QObject::connect(&m1, SIGNAL(dataChanged(Model*)),
|
||||
this, SLOT(onM1Changed(Model*)));
|
||||
QObject::connect(&m2, SIGNAL(dataChanged(Model*)),
|
||||
this, SLOT(onM2Changed(Model*)));
|
||||
|
||||
resetChanged();
|
||||
AutomatableModel::linkModels(&m1, &m1);
|
||||
QVERIFY(!m1Changed); // cannot link to itself
|
||||
QVERIFY(!m2Changed);
|
||||
|
||||
resetChanged();
|
||||
AutomatableModel::linkModels(&m1, &m2);
|
||||
QVERIFY(m1Changed); // since m1 takes the value of m2
|
||||
QVERIFY(!m2Changed); // the second model is the source
|
||||
|
||||
resetChanged();
|
||||
AutomatableModel::linkModels(&m1, &m2);
|
||||
QVERIFY(!m1Changed); // it's already linked
|
||||
QVERIFY(!m2Changed);
|
||||
|
||||
resetChanged();
|
||||
BoolModel m3(false);
|
||||
m1.setValue(1.f);
|
||||
m2.setValue(1.f);
|
||||
AutomatableModel::linkModels(&m1, &m2);
|
||||
QVERIFY(m1.value());
|
||||
QVERIFY(m2.value());
|
||||
QVERIFY(!m3.value());
|
||||
AutomatableModel::linkModels(&m2, &m3); // drag m3, drop on m2
|
||||
// m2 should take m3's (0) value
|
||||
// due to a bug(?), this does not happen
|
||||
QVERIFY(m2.value());
|
||||
QVERIFY(!m3.value());
|
||||
}
|
||||
} AutomatableModelTests;
|
||||
|
||||
#include "AutomatableModelTest.moc"
|
||||
|
||||
Reference in New Issue
Block a user