diff options
Diffstat (limited to 'muse2')
36 files changed, 4805 insertions, 15 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 632b022e..378faa9f 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,9 @@ +01.11.2010 + - Fixed all MESS plugins: compile with -fvisibility=hidden, to avoid namespace conflicts. + In particular, simplesynth was causing conflict with variable 'plugins' causing it to + be overwritten. + - So, this fixes LADSPA plugins not appearing in plugin list. + - Added simpledrums from muse_qt4_evolution, is called 'simpledrums2' and replaces the original. 31.10.2010 - Fixed Toolbar1 class. 'Snap', 'Quantize' combo boxes should work now. Thanks to Luis G. for help. (Tim) 30.10.2010 diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index e07b6c6e..f9087051 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -1395,7 +1395,7 @@ MusE::MusE(int argc, char** argv) : QMainWindow() } initMidiSynth(); - + populateAddTrack(addTrack); transport = new Transport(this, "transport"); @@ -3460,6 +3460,7 @@ int main(int argc, char* argv[]) initMetronome(); //QApplication::clipboard()->setSelectionMode(false); ddskrjo + QApplication::addLibraryPath(museGlobalLib + "/qtplugins"); if (debugMsg) { QStringList list = app.libraryPaths(); @@ -3474,6 +3475,7 @@ int main(int argc, char* argv[]) muse = new MusE(argc, &argv[optind]); app.setMuse(muse); muse->setIcon(*museIcon); + // Added by Tim. p3.3.22 if (!debugMode) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 9bcf11bf..61e49401 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -972,6 +972,9 @@ static void loadPluginLib(QFileInfo* fi) //LADSPA_Properties properties = descr->LADSPA_Plugin->Properties; //bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties); //plugins.add(fi, descr, !inPlaceBroken); + if(debugMsg) + fprintf(stderr, "loadPluginLib: adding dssi effect plugin:%s name:%s label:%s\n", fi->filePath().latin1(), descr->LADSPA_Plugin->Name, descr->LADSPA_Plugin->Label); + plugins.add(fi, descr->LADSPA_Plugin, true); } } @@ -1014,6 +1017,8 @@ static void loadPluginLib(QFileInfo* fi) //LADSPA_Properties properties = descr->Properties; //bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties); //plugins.add(fi, ladspa, descr, !inPlaceBroken); + if(debugMsg) + fprintf(stderr, "loadPluginLib: adding ladspa plugin:%s name:%s label:%s\n", fi->filePath().latin1(), descr->Name, descr->Label); plugins.add(fi, descr); } } @@ -1082,7 +1087,7 @@ void initPlugins() p = ladspaPath; if(debugMsg) - fprintf(stderr, "loadPluginLib: ladspa path:%s\n", ladspaPath); + fprintf(stderr, "loadPluginDir: ladspa path:%s\n", ladspaPath); while (*p != '\0') { const char* pe = p; @@ -1095,7 +1100,7 @@ void initPlugins() strncpy(buffer, p, n); buffer[n] = '\0'; if(debugMsg) - fprintf(stderr, "loadPluginLib: loading ladspa dir:%s\n", buffer); + fprintf(stderr, "loadPluginDir: loading ladspa dir:%s\n", buffer); loadPluginDir(QString(buffer)); delete[] buffer; diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 4d377fb4..74a27290 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -36,6 +36,9 @@ #include "midictrl.h" //#include "stringparam.h" +// REMOVE Tim. +#include "plugin.h" + std::vector<Synth*> synthis; // array of available synthis extern void connectNodes(AudioTrack*, AudioTrack*); @@ -509,6 +512,8 @@ void initMidiSynth() const char* path = fi->filePath().latin1(); // load Synti dll + //printf("initMidiSynth: dlopen file:%s name:%s desc:%s\n", fi->filePath().latin1(), QString(descr->name), QString(descr->description), QString(""), QString(descr->version))); + printf("initMidiSynth: dlopen file:%s\n", fi->filePath().latin1()); void* handle = dlopen(path, RTLD_NOW); if (handle == 0) { fprintf(stderr, "initMidiSynth: MESS dlopen(%s) failed: %s\n", path, dlerror()); diff --git a/muse2/synti/CMakeLists.txt b/muse2/synti/CMakeLists.txt index b2bc87ea..1a7fd322 100644 --- a/muse2/synti/CMakeLists.txt +++ b/muse2/synti/CMakeLists.txt @@ -28,7 +28,8 @@ include_directories( ) # set (SubDirs libsynti s1 organ deicsonze deicsonze2 simpledrums vam) -set (SubDirs libsynti s1 organ deicsonze simpledrums vam) +# set (SubDirs libsynti s1 organ deicsonze simpledrums vam) +set (SubDirs libsynti s1 organ deicsonze simpledrums2 vam) if (HAVE_FLUIDSYNTH) set (SubDirs ${SubDirs} fluid fluidsynth ) diff --git a/muse2/synti/deicsonze/CMakeLists.txt b/muse2/synti/deicsonze/CMakeLists.txt index a39b0e14..c9da5854 100644 --- a/muse2/synti/deicsonze/CMakeLists.txt +++ b/muse2/synti/deicsonze/CMakeLists.txt @@ -47,7 +47,7 @@ add_library ( deicsonze SHARED set_target_properties ( deicsonze PROPERTIES PREFIX "" #COMPILE_FLAGS "-O2 -include ${PROJECT_BINARY_DIR}/all-pic.h" - COMPILE_FLAGS "-DINSTPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -DINSTPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -include ${PROJECT_BINARY_DIR}/all-pic.h" ) target_link_libraries(deicsonze diff --git a/muse2/synti/deicsonze/deicsonze.cpp b/muse2/synti/deicsonze/deicsonze.cpp index 3dc5fafd..4eea4102 100644 --- a/muse2/synti/deicsonze/deicsonze.cpp +++ b/muse2/synti/deicsonze/deicsonze.cpp @@ -1660,7 +1660,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/deicsonze2/CMakeLists.txt b/muse2/synti/deicsonze2/CMakeLists.txt index 430953ca..12f88212 100644 --- a/muse2/synti/deicsonze2/CMakeLists.txt +++ b/muse2/synti/deicsonze2/CMakeLists.txt @@ -46,7 +46,7 @@ target_link_libraries( deicsonze synti ) set_target_properties ( deicsonze PROPERTIES PREFIX "" #COMPILE_FLAGS "-O2 -include ${PROJECT_BINARY_DIR}/all-pic.h" - COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" ) target_link_libraries(deicsonze diff --git a/muse2/synti/deicsonze2/deicsonze.cpp b/muse2/synti/deicsonze2/deicsonze.cpp index 28b9183e..74c1f56e 100644 --- a/muse2/synti/deicsonze2/deicsonze.cpp +++ b/muse2/synti/deicsonze2/deicsonze.cpp @@ -4350,7 +4350,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/fluid/CMakeLists.txt b/muse2/synti/fluid/CMakeLists.txt index 38184550..0007537f 100644 --- a/muse2/synti/fluid/CMakeLists.txt +++ b/muse2/synti/fluid/CMakeLists.txt @@ -38,7 +38,7 @@ add_library ( fluid SHARED # set_target_properties ( fluid PROPERTIES PREFIX "" - COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" LINK_FLAGS "${FLUIDSYN_LDFLAGS}" # "-lfluidsynth" ) target_link_libraries(fluid diff --git a/muse2/synti/fluid/fluid.cpp b/muse2/synti/fluid/fluid.cpp index a03be4de..13cfbef3 100644 --- a/muse2/synti/fluid/fluid.cpp +++ b/muse2/synti/fluid/fluid.cpp @@ -61,6 +61,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/fluidsynth/CMakeLists.txt b/muse2/synti/fluidsynth/CMakeLists.txt index cdb298e2..0c3e1b97 100644 --- a/muse2/synti/fluidsynth/CMakeLists.txt +++ b/muse2/synti/fluidsynth/CMakeLists.txt @@ -38,7 +38,7 @@ add_library ( fluidsynth SHARED # set_target_properties ( fluidsynth PROPERTIES PREFIX "" - COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" LINK_FLAGS "${FLUIDSYN_LDFLAGS}" # "-lfluidsynth" ) diff --git a/muse2/synti/fluidsynth/fluidsynti.cpp b/muse2/synti/fluidsynth/fluidsynti.cpp index bf27a61c..bf12b7f8 100644 --- a/muse2/synti/fluidsynth/fluidsynti.cpp +++ b/muse2/synti/fluidsynth/fluidsynti.cpp @@ -1310,6 +1310,12 @@ extern "C" MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/organ/CMakeLists.txt b/muse2/synti/organ/CMakeLists.txt index 8dd67aa8..40db20c1 100644 --- a/muse2/synti/organ/CMakeLists.txt +++ b/muse2/synti/organ/CMakeLists.txt @@ -40,7 +40,7 @@ add_library ( organ SHARED # set_target_properties ( organ PROPERTIES PREFIX "" - COMPILE_FLAGS "-O2 -include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -O2 -include ${PROJECT_BINARY_DIR}/all-pic.h" ) target_link_libraries(organ diff --git a/muse2/synti/organ/organ.cpp b/muse2/synti/organ/organ.cpp index a1788a6c..2dede8de 100644 --- a/muse2/synti/organ/organ.cpp +++ b/muse2/synti/organ/organ.cpp @@ -704,7 +704,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; - + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/s1/CMakeLists.txt b/muse2/synti/s1/CMakeLists.txt index 890c6b12..7dffe905 100644 --- a/muse2/synti/s1/CMakeLists.txt +++ b/muse2/synti/s1/CMakeLists.txt @@ -25,7 +25,10 @@ add_library ( s1 SHARED s1.cpp ) # tell cmake to name target s1.so instead of # libs1.so # -set_target_properties ( s1 PROPERTIES PREFIX "" ) +set_target_properties ( s1 + PROPERTIES PREFIX "" + COMPILE_FLAGS "-fvisibility=hidden" + ) target_link_libraries(s1 synti diff --git a/muse2/synti/s1/s1.cpp b/muse2/synti/s1/s1.cpp index c73615bd..8520a742 100644 --- a/muse2/synti/s1/s1.cpp +++ b/muse2/synti/s1/s1.cpp @@ -221,7 +221,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate }; - + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/simpledrums/CMakeLists.txt b/muse2/synti/simpledrums/CMakeLists.txt index 696389c6..6b015f2b 100644 --- a/muse2/synti/simpledrums/CMakeLists.txt +++ b/muse2/synti/simpledrums/CMakeLists.txt @@ -40,7 +40,7 @@ add_library ( simpledrums SHARED # set_target_properties ( simpledrums PROPERTIES PREFIX "" - COMPILE_FLAGS "-O6 -include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-O6 -fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" ) target_link_libraries(simpledrums diff --git a/muse2/synti/simpledrums/simpledrums.cpp b/muse2/synti/simpledrums/simpledrums.cpp index 5a0431a0..8bca91a4 100644 --- a/muse2/synti/simpledrums/simpledrums.cpp +++ b/muse2/synti/simpledrums/simpledrums.cpp @@ -1531,6 +1531,12 @@ extern "C" MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } diff --git a/muse2/synti/simpledrums2/CMakeLists.txt b/muse2/synti/simpledrums2/CMakeLists.txt new file mode 100644 index 00000000..3c7ba3c7 --- /dev/null +++ b/muse2/synti/simpledrums2/CMakeLists.txt @@ -0,0 +1,52 @@ +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2006 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2. +# +# 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; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#============================================================================= + +QT4_WRAP_CPP ( simpledrums_mocs simpledrumsgui.h ssplugingui.h ) +QT4_WRAP_UI ( simpledrums_uis simpledrumsguibase.ui sspluginchooserbase.ui ) + +add_library ( simpledrums SHARED + simpledrums.cpp + simpledrumsgui.cpp + simpledrums.h + ssplugin.cpp + ssplugingui.cpp + ssplugin.h + common.h + ${simpledrums_mocs} + ${simpledrums_uis} + ) + +# - tell cmake to name target simpledrums.so instead of +# libsimpledrums.so +# - use precompiled header files +# +set_target_properties ( simpledrums + PROPERTIES PREFIX "" + COMPILE_FLAGS "-O6 -fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" + ) + +target_link_libraries(simpledrums + synti +# awl + ${QT_LIBRARIES} + ) + +install_targets ( /${CMAKE_INSTALL_LIBDIR}/${MusE_INSTALL_NAME}/synthi/ simpledrums ) + diff --git a/muse2/synti/simpledrums2/COPYING b/muse2/synti/simpledrums2/COPYING new file mode 100644 index 00000000..5c3cefc2 --- /dev/null +++ b/muse2/synti/simpledrums2/COPYING @@ -0,0 +1,3 @@ +COPYING +--------------------------------------- +This software is licensed under GNU GPL. diff --git a/muse2/synti/simpledrums2/README b/muse2/synti/simpledrums2/README new file mode 100644 index 00000000..468640b1 --- /dev/null +++ b/muse2/synti/simpledrums2/README @@ -0,0 +1,43 @@ +-------------------------------------- +Simpledrums v 0.2, by Mathias Lundgren +-------------------------------------- + +Simpledrums is a simple MESS-synth sampler (MusE Experimental Soft +Synth) aiming at becoming a simple, tightly integrated sampler for +MusE, specifically aimed at drumsamples. + +Features: +- 16 channels/samples (1 sample/channel) +- Simple controls for each individual channel: volume, balance, noteoff-ignore, channel on/off +- Main volume +- 4 LADSPA send-effects can be used, 4 effect taps for each individual channel +- All channel parameters are controllable via the GUI, or by MusE:s controller handling (controller pane in pianoroll/drumeditor) +- All effect parameters can be controlled via the GUI, or by Sysex messages (f.ex. turn effect on/off, modify effect parameters) +- Complete synth state (fx-parameters, samples etc) is saved together with MusE project, and restored later when loaded +- Samples automatically resampled when loaded (if needed) + +That's all folks! + +------------- +Known issues: +------------- +- Not the prettiest gui in the world +- All samples are read directly into memory (no caching) +- Some obscure LADSPA-effects make SimpleSynth segfault +- More... + +------------- +Future plans: +------------- +- Fix all the known issues! ;-) +- Sample loops +- Sample offset variation w respect to note velocity +- Treble/eq-controller for each individual channel +- Treble level variation w respect to note velocity +- More... + +Mathias Lundgren, (lunar_shuttle@users.sourceforge.net), 2004 +Plugin management code based on Werner Schweers plugin management handling for MusE + +(C) Copyright Mathias Lundgren, Werner Schweer 2000-2004 +Licensed under the GNU General Public License diff --git a/muse2/synti/simpledrums2/ReleaseNotes.txt b/muse2/synti/simpledrums2/ReleaseNotes.txt new file mode 100644 index 00000000..e0d633d8 --- /dev/null +++ b/muse2/synti/simpledrums2/ReleaseNotes.txt @@ -0,0 +1,18 @@ +RELEASE NOTES: +-------------- +????-??-?? ver 0.2 +- Support for 4 LADSPA sendeffects added +- Resampling of samples when loading (libsamplerate) +- Synth state is saved to/restored from project file +- Channel settings: balance, volume, effect tap for each sendeffect +- Effect settings: all LADSPA parameters controllable and saved to MusE project, effect master volume, effect on/off +- Support for mono + stereo samples +- Support for stereo + mono LADSPA effects +- Bugfixes, GUI-improvements etc... + +2004-11-09 ver 0.1 +- Simpledrums initial release +- 16 channels (1 sample for each channel) with parameters: volume, balance, noteoff-ignore + +(C) Copyright Mathias Lundgren, Werner Schweer 2000-2004 +Licensed under the GNU General Public License diff --git a/muse2/synti/simpledrums2/common.h b/muse2/synti/simpledrums2/common.h new file mode 100644 index 00000000..e4763540 --- /dev/null +++ b/muse2/synti/simpledrums2/common.h @@ -0,0 +1,110 @@ +// +// C++ Interface: common +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef __MUSE_TESTO_COMMON_H__ +#define __MUSE_TESTO_COMMON_H__ + +#include "muse/midictrl.h" + +#define SS_VERSIONSTRING "1.0" + +#define SS_DEBUG 0 +#define SS_DEBUG_INIT 0 +#define SS_TRACE_FUNC 0 +#define SS_DEBUG_MIDI 0 +#define SS_DEBUG_LADSPA 0 +#define SS_DEBUG_STATE 0 + +#define SS_DBG(string) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string); +#define SS_DBG2(string1, string2) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); +#define SS_DBG_I(string1, int) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s: %d\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, int); + +#define SS_TRACE_IN if (SS_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__); +#define SS_TRACE_OUT if (SS_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__); +#define SS_ERROR(string) fprintf(stderr, "SimpleDrums error: %s\n", string) +#define SS_DBG_LADSPA(string1) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); +#define SS_DBG_LADSPA2(string1, string2) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); + +#define SS_SYSEX_INIT_DATA_VERSION 1 + +#define SS_NR_OF_CHANNELS 16 +#define SS_AUDIO_CHANNELS 2 +#define SS_NR_OF_SENDEFFECTS 4 + +// Controller-related: +#define SS_CHANNEL_CTRL_VOLUME 0 +#define SS_CHANNEL_CTRL_PAN 1 +#define SS_CHANNEL_CTRL_NOFF 2 +#define SS_CHANNEL_CTRL_ONOFF 3 +#define SS_CHANNEL_SENDFX1 4 +#define SS_CHANNEL_SENDFX2 5 +#define SS_CHANNEL_SENDFX3 6 +#define SS_CHANNEL_SENDFX4 7 + +#define SS_PLUGIN_RETURN 0 +#define SS_PLUGIN_ONOFF 1 + +#define SS_NR_OF_MASTER_CONTROLLERS 1 +#define SS_NR_OF_CHANNEL_CONTROLLERS 8 +#define SS_NR_OF_PLUGIN_CONTROLLERS 2 + +#define SS_NR_OF_CONTROLLERS (SS_NR_OF_MASTER_CONTROLLERS + (SS_NR_OF_CHANNELS * SS_NR_OF_CHANNEL_CONTROLLERS) + (SS_NR_OF_PLUGIN_CONTROLLERS*SS_NR_OF_SENDEFFECTS)) +#define SS_FIRST_MASTER_CONTROLLER CTRL_NRPN14_OFFSET +#define SS_FIRST_CHANNEL_CONTROLLER (SS_FIRST_MASTER_CONTROLLER + SS_NR_OF_MASTER_CONTROLLERS) +#define SS_LAST_MASTER_CONTROLLER (SS_FIRST_CHANNEL_CONTROLLER - 1) +#define SS_LAST_CHANNEL_CONTROLLER (SS_FIRST_CHANNEL_CONTROLLER -1 + (SS_NR_OF_CHANNEL_CONTROLLERS * SS_NR_OF_CHANNELS)) + +#define SS_FIRST_PLUGIN_CONTROLLER (SS_LAST_CHANNEL_CONTROLLER + 1) +#define SS_LAST_PLUGIN_CONTROLLER (SS_FIRST_PLUGIN_CONTROLLER -1 + SS_NR_OF_SENDEFFECTS*SS_NR_OF_PLUGIN_CONTROLLERS) + +#define SS_MASTER_CTRL_VOLUME SS_FIRST_MASTER_CONTROLLER + +#define SS_CHANNEL_VOLUME_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_VOLUME) +#define SS_CHANNEL_PAN_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_PAN) +#define SS_CHANNEL_NOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_NOFF) +#define SS_CHANNEL_ONOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_ONOFF) +#define SS_CHANNEL_SENDFX_CONTROLLER(int1,int2) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int1) + SS_CHANNEL_SENDFX1 + int2) + +#define SS_PLUGIN_RETURNLEVEL_CONTROLLER(int) (SS_FIRST_PLUGIN_CONTROLLER + (int * SS_NR_OF_PLUGIN_CONTROLLERS)) +#define SS_PLUGIN_ONOFF_CONTROLLER(int) (SS_FIRST_PLUGIN_CONTROLLER + (int * SS_NR_OF_PLUGIN_CONTROLLERS) + 1) + +#define SS_LOWEST_NOTE 36 +#define SS_HIGHEST_NOTE (SS_LOWEST_NOTE + SS_NR_OF_CHANNELS) + +#define SS_PLUGIN_PARAM_MIN 0 +#define SS_PLUGIN_PARAM_MAX 127 + +typedef unsigned char byte; + +enum { + SS_SYSEX_LOAD_SAMPLE = 0, // gui -> synth: tell synth to load sample + SS_SYSEX_INIT_DATA, // synth reinitialization, the position of this (1) in the enum must not be changed since this value is written into proj file + SS_SYSEX_LOAD_SAMPLE_OK, // synth -> gui: tell gui sample loaded OK + SS_SYSEX_LOAD_SAMPLE_ERROR, // synth -> gui: tell gui sample ! loaded OK + SS_SYSEX_CLEAR_SAMPLE, // gui -> synth: tell synth to clear sample + SS_SYSEX_CLEAR_SAMPLE_OK, // synth->gui: confirm sample cleared OK + SS_SYSEX_LOAD_SENDEFFECT, // gui -> synth: tell synth to load laspa-effect + SS_SYSEX_LOAD_SENDEFFECT_OK,// synth->gui: plugin loaded ok + SS_SYSEX_LOAD_SENDEFFECT_ERROR, // synth->gui: plugin _not_ loaded ok + SS_SYSEX_CLEAR_SENDEFFECT, // gui->synth: clear plugin + SS_SYSEX_CLEAR_SENDEFFECT_OK,// synth->gui: plugin cleared + SS_SYSEX_SET_PLUGIN_PARAMETER, //gui->synth: set plugin parameter + SS_SYSEX_SET_PLUGIN_PARAMETER_OK, // synth->gui: set plugin parameter (update gui) + SS_SYSEX_ERRORMSG, // synth -> gui: general error message from synth + SS_SYSEX_GET_INIT_DATA, // gui->synth: request init data + SS_SYSEX_SEND_INIT_DATA // synth->gui: give gui init data + }; + +extern int SS_samplerate; +extern float SS_map_pluginparam2logdomain(int pluginparam_val); +extern int SS_map_logdomain2pluginparam(float pluginparam_log); +#endif + diff --git a/muse2/synti/simpledrums2/simpledrums.cpp b/muse2/synti/simpledrums2/simpledrums.cpp new file mode 100644 index 00000000..242fd8dd --- /dev/null +++ b/muse2/synti/simpledrums2/simpledrums.cpp @@ -0,0 +1,1726 @@ +// +// C++ Implementation: simplesynth +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "muse/midictrl.h" +#include "muse/midi.h" +#include "libsynti/mpevent.h" +#include "simpledrums.h" +// #include <qstring.h> +#include <samplerate.h> + +const char* SimpleSynth::synth_state_descr[] = + { + "SS_INITIALIZING", + "SS_LOADING_SAMPLE", + "SS_CLEARING_SAMPLE", + "SS_RUNNING" + }; + +const char* SimpleSynth::channel_state_descr[] = + { + "SS_CHANNEL_INACTIVE", + "SS_SAMPLE_PLAYING" + }; + +#define SWITCH_SYNTH_STATE(state)\ +synth_state = state; \ +if (SS_DEBUG_STATE) \ + fprintf (stderr, "SS STATE: %s\n", SimpleSynth::synth_state_descr[state]); + +#define SWITCH_CHAN_STATE(ch, s)\ +channels[ch].state = s; \ +if (SS_DEBUG_STATE) \ + fprintf (stderr, "SS CHAN %d STATE: %s\n", ch, SimpleSynth::channel_state_descr[s]); + +#define SS_CHANNEL_VOLUME_QUOT 100.0 +#define SS_MASTER_VOLUME_QUOT 100.0 +int SS_samplerate; + +#define SS_LOG_MAX 0 +#define SS_LOG_MIN -10 +#define SS_LOG_OFFSET SS_LOG_MIN + + +// +// Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain) +// +float SS_map_pluginparam2logdomain(int pluginparam_val) + { + float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX; + float scaled = (float) pluginparam_val * scale; + float mapped = scaled + SS_LOG_OFFSET; + return mapped; + } +// +// Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] (from log-> [0,127]) +// (inverse func to the above) +int SS_map_logdomain2pluginparam(float pluginparam_log) + { + float mapped = pluginparam_log - SS_LOG_OFFSET; + float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN); + int scaled = (int) round(mapped * scale); + return scaled; + } + +//--------------------------------------------------------- +// SimpleSynth +//--------------------------------------------------------- +SimpleSynth::SimpleSynth(int sr) + : Mess(SS_AUDIO_CHANNELS) + { + SS_TRACE_IN + SS_samplerate = sr; + SS_initPlugins(); + + simplesynth_ptr = this; + master_vol = 100.0 / SS_MASTER_VOLUME_QUOT; + master_vol_ctrlval = 100; + + //initialize + for (int i=0; i<SS_NR_OF_CHANNELS; i++) { + channels[i].sample = 0; + channels[i].playoffset = 0; + channels[i].noteoff_ignore = false; + channels[i].volume = (double) (100.0/SS_CHANNEL_VOLUME_QUOT ); + channels[i].volume_ctrlval = 100; + channels[i].pan = 64; + channels[i].balanceFactorL = 1.0; + channels[i].balanceFactorR = 1.0; + SWITCH_CHAN_STATE(i, SS_CHANNEL_INACTIVE); + channels[i].channel_on = false; + for (int j=0; j<SS_NR_OF_SENDEFFECTS; j++) { + channels[i].sendfxlevel[j] = 0.0; + } + } + + //Process buffer: + processBuffer[0] = new double[SS_PROCESS_BUFFER_SIZE]; //left + processBuffer[1] = new double[SS_PROCESS_BUFFER_SIZE]; //right + + //Send effects + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + sendFxLineOut[i][0] = new float[SS_SENDFX_BUFFER_SIZE]; //left out + sendFxLineOut[i][1] = new float[SS_SENDFX_BUFFER_SIZE]; //right out + sendFxReturn[i][0] = new float[SS_SENDFX_BUFFER_SIZE]; //left in + sendFxReturn[i][1] = new float[SS_SENDFX_BUFFER_SIZE]; //right in + } + + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + sendEffects[i].state = SS_SENDFX_OFF; + sendEffects[i].plugin = 0; + sendEffects[i].retgain = 1.0; + sendEffects[i].retgain_ctrlval = 100; + sendEffects[i].nrofparameters = 0; + } + + //Build controller list: + controllers[0].name = "Master volume"; + controllers[0].num = CTRL_NRPN14_OFFSET; + controllers[0].min = 0; + controllers[0].max = 127; + + int i=1; + for (int ch=0; ch<SS_NR_OF_CHANNELS; ch++) { + QString c1 = "Channel " + QString::number(ch + 1) + " volume"; + QString c2 = "Channel " + QString::number(ch + 1) + " pan"; + QString c3 = "Channel " + QString::number(ch + 1) + " noteoff ignore"; + QString c4 = "Channel " + QString::number(ch + 1) + " on/off"; + QString c5 = "Channel " + QString::number(ch + 1) + " fx send 1"; + QString c6 = "Channel " + QString::number(ch + 1) + " fx send 2"; + QString c7 = "Channel " + QString::number(ch + 1) + " fx send 3"; + QString c8 = "Channel " + QString::number(ch + 1) + " fx send 4"; + controllers[i].name = c1.toLatin1().data(); + controllers[i].num = CTRL_NRPN14_OFFSET+i; + controllers[i].min = 0; + controllers[i].max = 127; + + controllers[i+1].name = c2.toLatin1().data(); + controllers[i+1].num = CTRL_NRPN14_OFFSET+i+1; + controllers[i+1].min = 0; + controllers[i+1].max = 127; + + controllers[i+2].name = c3.toLatin1().data(); + controllers[i+2].num = CTRL_NRPN14_OFFSET+i+2; + controllers[i+2].min = 0; + controllers[i+2].max = 1; + + controllers[i+3].name = c4.toLatin1().data(); + controllers[i+3].num = CTRL_NRPN14_OFFSET+i+3; + controllers[i+3].min = 0; + controllers[i+3].max = 1; + + controllers[i+4].name = c5.toLatin1().data(); + controllers[i+4].num = CTRL_NRPN14_OFFSET+i+4; + + controllers[i+5].name = c6.toLatin1().data(); + controllers[i+5].num = CTRL_NRPN14_OFFSET+i+5; + + controllers[i+6].name = c7.toLatin1().data(); + controllers[i+6].num = CTRL_NRPN14_OFFSET+i+6; + + controllers[i+7].name = c8.toLatin1().data(); + controllers[i+7].num = CTRL_NRPN14_OFFSET+i+7; + + controllers[i+4].min = controllers[i+5].min = controllers[i+6].min = controllers[i+7].min = 0; + controllers[i+4].max = controllers[i+5].max = controllers[i+6].max = controllers[i+7].max = 127; + + i+=8; + } + + for (int sfx=0; sfx<SS_NR_OF_SENDEFFECTS; sfx++) { + QString c1 = "Sendfx " + QString::number(sfx) + " ret gain"; + QString c2 = "Sendfx " + QString::number(sfx) + " on/off"; + controllers[i].name = c1.toLatin1().data(); + controllers[i].num = CTRL_NRPN14_OFFSET+i; + controllers[i].min = 0; + controllers[i].max = 127; + + controllers[i+1].name = c2.toLatin1().data(); + controllers[i+1].num = CTRL_NRPN14_OFFSET+i+1; + controllers[i+1].min = 0; + controllers[i+1].max = 1; + i+=2; + } + + pthread_mutex_init(&SS_LoaderMutex, NULL); + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// ~SimpleSynth +//--------------------------------------------------------- +SimpleSynth::~SimpleSynth() + { + SS_TRACE_IN + + // Cleanup channels and samples: + SS_DBG("Cleaning up sample data"); + for (int i=0; i<SS_NR_OF_CHANNELS; i++) { + if (channels[i].sample) { + delete[] channels[i].sample->data; + delete channels[i].sample; + } + } + simplesynth_ptr = NULL; + + SS_DBG("Deleting pluginlist"); + //Cleanup plugins: + for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) { + delete (*i); + } + plugins.clear(); + + SS_DBG("Deleting sendfx buffers"); + //Delete sendfx buffers: + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + delete[] sendFxLineOut[i][0]; + delete[] sendFxLineOut[i][1]; + delete[] sendFxReturn[i][0]; + delete[] sendFxReturn[i][1]; + } + + //processBuffer: + SS_DBG("Deleting process buffer"); + delete[] processBuffer[0]; + delete[] processBuffer[1]; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// guiVisible +/*! + \fn SimpleSynth::guiVisible + \brief Tells if the gui is hidden or shown + \return true/false if gui is shown/hidden + */ +//--------------------------------------------------------- +bool SimpleSynth::guiVisible() const + { + SS_TRACE_IN + bool v = gui->isVisible(); + SS_TRACE_OUT + return v; + } + +//--------------------------------------------------------- +// hasGui +/*! + \fn SimpleSynth::hasGui + \brief Tells if the synth has a gui or not + \return true if synth has gui, false it synth has no gui + */ +//--------------------------------------------------------- +bool SimpleSynth::hasGui() const + { + SS_TRACE_IN + SS_TRACE_OUT + return true; + } + +//--------------------------------------------------------- +// playNote +/*! + \fn SimpleSynth::playNote + \brief Triggers a note on (noteoffs are noteons with velo=0) + \param channel midi channel + \param pitch note pitch + \param velo note velocity + \return false for ok, true for not ok (not sure these are handled differently, but...) + */ +//--------------------------------------------------------- +bool SimpleSynth::playNote(int /*channel*/, int pitch, int velo) + { + SS_TRACE_IN + //Don't bother about channel, we're processing every playnote! + if ((pitch >= SS_LOWEST_NOTE) && (pitch <= SS_HIGHEST_NOTE)) { + bool noteOff = (velo == 0 ? 1 : 0); + int ch = pitch - SS_LOWEST_NOTE; + if(!noteOff) { + if (channels[ch].sample) { + //Turn on the white stuff: + channels[ch].playoffset = 0; + SWITCH_CHAN_STATE(ch , SS_SAMPLE_PLAYING); + channels[ch].cur_velo = (double) velo / 127.0; + channels[ch].gain_factor = channels[ch].cur_velo * channels[ch].volume; + if (SS_DEBUG_MIDI) { + printf("Playing note %d on channel %d\n", pitch, ch); + } + } + } + else { + //Note off: + if (channels[ch].noteoff_ignore) { + if (SS_DEBUG_MIDI) { + printf("Note off on channel %d\n", ch); + } + SWITCH_CHAN_STATE(ch , SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + channels[ch].cur_velo = 0; + } + } + } + SS_TRACE_OUT + return false; + } + +//--------------------------------------------------------- +// processEvent +/*! + \fn SimpleSynth::processEvent + \brief All events from sequencer first shows up here and are forwarded to their correct functions + \param event The event sent from sequencer + \return false for ok, true for not ok + */ +//--------------------------------------------------------- +bool SimpleSynth::processEvent(const MidiPlayEvent& ev) + { + SS_TRACE_IN + switch(ev.type()) { + case ME_CONTROLLER: + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::processEvent - Controller. Chan: %x dataA: %x dataB: %x\n", ev.channel(), ev.dataA(), ev.dataB()); + for (int i=0; i< ev.len(); i++) + printf("%x ", ev.data()[i]); + } + setController(ev.channel(), ev.dataA(), ev.dataB(), false); + return true; + case ME_NOTEON: + return playNote(ev.channel(), ev.dataA(), ev.dataB()); + case ME_NOTEOFF: + return playNote(ev.channel(), ev.dataA(), 0); + case ME_SYSEX: + //Debug print + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::processEvent - Sysex received\n"); + for (int i=0; i< ev.len(); i++) + printf("%x ", ev.data()[i]); + printf("\n"); + } + return sysex(ev.len(), ev.data()); + } + return false; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// setController +/*! + \fn SimpleSynth::setController + \brief Called from sequencer indirectly via SimpleSynth::processEvent + \brief when the synth is supposed to set a controller value + \param channel channel nr + \param id controller id + \param val value of controller + \return false for ok, true for not ok + */ +//--------------------------------------------------------- +bool SimpleSynth::setController(int channel, int id, int val) + { + SS_TRACE_IN + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::setController - received controller on channel %d, id %d value %d\n", channel, id, val); + } + + // Channel controllers: + if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { + // Find out which channel we're dealing with: + id-= SS_FIRST_CHANNEL_CONTROLLER; + int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); + id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); + + switch (id) { + case SS_CHANNEL_CTRL_VOLUME: + if (SS_DEBUG_MIDI) + printf("Received channel ctrl volume %d for channel %d\n", val, ch); + channels[ch].volume_ctrlval = val; + updateVolume(ch, val); + break; + case SS_CHANNEL_CTRL_NOFF: + if (SS_DEBUG_MIDI) + printf("Received ctrl noff %d for channel %d\n", val, ch); + channels[ch].noteoff_ignore = val; + break; + case SS_CHANNEL_CTRL_PAN: + { + if (SS_DEBUG_MIDI) + printf("Received ctrl pan %d for channel %d\n", val, ch); + channels[ch].pan = val; + updateBalance(ch, val); + break; + } + case SS_CHANNEL_CTRL_ONOFF: + { + if (SS_DEBUG_MIDI) + printf("Received ctrl onoff %d for channel %d\n", val, ch); + + if (val == false && channels[ch].channel_on == true) { + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].channel_on = val; + } + else if (val == true && channels[ch].channel_on == false) { // if it actually _was_ off: + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + channels[ch].channel_on = val; + } + break; + } + case SS_CHANNEL_SENDFX1: + case SS_CHANNEL_SENDFX2: + case SS_CHANNEL_SENDFX3: + case SS_CHANNEL_SENDFX4: + { + int fxid = id - SS_CHANNEL_SENDFX1; + channels[ch].sendfxlevel[fxid] = (double)val/127.0; + break; + } + + default: + if (SS_DEBUG_MIDI) + printf("Unknown controller received for channel %d. id=%d\n", ch, id); + break; + } + } + // Master controllers: + else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { + if (SS_DEBUG_MIDI) + printf("Mastervol controller received: %d\n", id); + master_vol_ctrlval = val; + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + } + // Emmm, this one should've been there in the beginning + else if (id == CTRL_VOLUME) { + if (SS_DEBUG_MIDI) { + printf("Ctrl volume received: vol: %d\n", val); + } + master_vol_ctrlval = val; + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + //This one can't be from the gui, update gui: + guiUpdateMasterVol(val); + } + // Plugin controllers: + else if (id >= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { + + int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; + int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; + + // Plugin return-gain: + if (cmd == SS_PLUGIN_RETURN) { + if (SS_DEBUG_MIDI) + printf("Ctrl fx retgain received: fxid: %d val: %d\n", fxid, val); + sendEffects[fxid].retgain_ctrlval = val; + sendEffects[fxid].retgain = (double) val / 75.0; + } + // Plugin on/off: + else if (cmd == SS_PLUGIN_ONOFF) { + if (SS_DEBUG_MIDI) + printf("Ctrl fx onoff received: fxid: %d val: %d\n", fxid, val); + sendEffects[fxid].state = (SS_SendFXState) val; + } + } + else { + if (SS_DEBUG_MIDI) + printf("Unknown controller received: %d\n", id); + } + SS_TRACE_OUT + return false; + } + +//--------------------------------------------------------- +/*! + \fn SimpleSynth::setController + */ +//--------------------------------------------------------- +bool SimpleSynth::setController(int channel, int id, int val, bool /*fromGui*/) + { + SS_TRACE_IN + bool ret = setController(channel, id, val); //Perhaps TODO... Separate events from the gui + SS_TRACE_OUT + return ret; + } +//--------------------------------------------------------- +// sysex +/*! + \fn SimpleSynth::sysex + \brief Called from sequencer indirectly via SimpleSynth::processEvent + \param len length of the sysex data + \param data the sysex data + \return false for ok, true for not ok +*/ +//--------------------------------------------------------- +bool SimpleSynth::sysex(int /*len*/, const unsigned char* data) + { + SS_TRACE_IN + int cmd = data[0]; + switch (cmd) { + case SS_SYSEX_LOAD_SAMPLE: + { + int channel = data[1]; + //int l = data[2]; + const char* filename = (const char*)(data+3); + if (SS_DEBUG_MIDI) { + printf("Sysex cmd: load sample, filename %s, on channel: %d\n", filename, channel); + } + loadSample(channel, filename); + break; + } + case SS_SYSEX_CLEAR_SAMPLE: + { + int ch = data[1]; + clearSample(ch); + break; + } + + case SS_SYSEX_INIT_DATA: + { + parseInitData(data); + break; + } + + case SS_SYSEX_LOAD_SENDEFFECT: + { + int fxid = data[1]; + QString lib = (const char*) (data + 2); + QString label = (const char*) (data + lib.length() + 3); + if (SS_DEBUG_MIDI) { + printf("Sysex cmd load effect: %d %s %s\n", fxid, lib.toLatin1().data(), label.toLatin1().data()); + } + initSendEffect(fxid, lib, label); + break; + } + + case SS_SYSEX_CLEAR_SENDEFFECT: + { + int fxid = data[1]; + if (SS_DEBUG_MIDI) { + printf("Sysex cmd clear effect: %d\n", fxid); + } + sendEffects[fxid].state = SS_SENDFX_OFF; + cleanupPlugin(fxid); + sendEffects[fxid].plugin = 0; + break; + } + + case SS_SYSEX_SET_PLUGIN_PARAMETER: + { + int fxid = data[1]; + int parameter = data[2]; + int val = data[3]; + // Write it to the plugin: + float floatval = sendEffects[fxid].plugin->convertGuiControlValue(parameter, val); + setFxParameter(fxid, parameter, floatval); + break; + } + + case SS_SYSEX_GET_INIT_DATA: + { + int initdata_len = 0; + const byte* tmp_initdata = NULL; + byte* event_data = NULL; + + getInitData(&initdata_len, &tmp_initdata); + int totlen = initdata_len + 1; + + event_data = new byte[initdata_len + 1]; + event_data[0] = SS_SYSEX_SEND_INIT_DATA; + memcpy(event_data + 1, tmp_initdata, initdata_len); + delete[] tmp_initdata; + tmp_initdata = NULL; + + MidiPlayEvent ev(0, 0, ME_SYSEX, event_data, totlen); + gui->writeEvent(ev); + delete[] event_data; + + break; + } + + default: + if (SS_DEBUG_MIDI) + printf("Unknown sysex cmd received: %d\n", cmd); + break; + } + SS_TRACE_OUT + return false; + } + +//--------------------------------------------------------- +// getPatchName +/*! + \fn SimpleSynth::getPatchName + \brief Called from host to get names of patches + \param index - which patchnr we're about to deliver + \return const char* with patchname + */ +//--------------------------------------------------------- +const char* SimpleSynth::getPatchName(int /*index*/, int, int) const + { + SS_TRACE_IN + SS_TRACE_OUT + return 0; + } + +//--------------------------------------------------------- +// getPatchInfo +/*! + \fn SimpleSynth::getPatchInfo + \brief Called from host to get info about patches + \param index - which patchnr we're about to deliver + \param patch - if this one is 0, this is the first call, otherwise keep deliver the host patches... or something + \return MidiPatch with patch info for host + */ +//--------------------------------------------------------- +const MidiPatch* SimpleSynth::getPatchInfo(int index, const MidiPatch* patch) const + { + SS_TRACE_IN + index = 0; patch = 0; + SS_TRACE_OUT + return 0; + } + +//--------------------------------------------------------- +// getControllerInfo +/*! + \fn SimpleSynth::getControllerInfo + \brief Called from host to collect info about which controllers the synth supports + \param index current controller number + \param name pointer where name is stored + \param controller int pointer where muse controller number is stored + \param min int pointer where controller min value is stored + \param max int pointer where controller max value is stored + \return 0 when done, otherwise return next desired controller index + */ +//--------------------------------------------------------- +int SimpleSynth::getControllerInfo(int index, const char** name, int* controller, int* min, int* max) + { + SS_TRACE_IN + if (index >= SS_NR_OF_CONTROLLERS) { + SS_TRACE_OUT + return 0; + } + + *name = controllers[index].name.c_str(); + *controller = controllers[index].num; + *min = controllers[index].min; + *max = controllers[index].max; + + if (SS_DEBUG_MIDI) { + printf("setting controller info: index %d name %s controller %d min %d max %d\n", index, *name, *controller, *min, *max); + } + SS_TRACE_OUT + return (index +1); + } + +//--------------------------------------------------------- +// process +/*! + \fn SimpleSynth::process + \brief Realtime function where the processing actually occurs + \param channels - audio data + \param offset - sample offset + \param len - nr of samples to process + */ +//--------------------------------------------------------- +void SimpleSynth::process(float** out, int offset, int len) + { + //Process messages from the gui + while (gui->fifoSize()) { + MidiPlayEvent ev = gui->readEvent(); + if (ev.type() == ME_SYSEX) { + sysex(ev.len(), ev.data()); + sendEvent(ev); + } + else if (ev.type() == ME_CONTROLLER) { + setController(ev.channel(), ev.dataA(), ev.dataB(), true); + sendEvent(ev); + } + else { + if (SS_DEBUG) + printf("SimpleSynth::process(): unknown event, type: %d\n", ev.type()); + } + } + + if (synth_state == SS_RUNNING) { + + //Temporary mix-doubles + double out1, out2; + //double ltemp, rtemp; + float* data; + // Velocity factor: + double gain_factor; + + + // Clear send-channels. Skips if fx not turned on + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + if (sendEffects[i].state == SS_SENDFX_ON) { + memset(sendFxLineOut[i][0], 0, SS_SENDFX_BUFFER_SIZE * sizeof(float)); + memset(sendFxLineOut[i][1], 0, SS_SENDFX_BUFFER_SIZE * sizeof(float)); + } + } + + + memset(out[0] + offset, 0, len * sizeof(float)); + memset(out[1] + offset, 0, len * sizeof(float)); + + //Process 1 channel at a time + for (int ch=0; ch < SS_NR_OF_CHANNELS; ch++) { + // If channels is turned off, skip: + if (channels[ch].channel_on == false) + continue; + + //If sample isn't playing, skip: + if (channels[ch].state == SS_SAMPLE_PLAYING) { + memset(processBuffer[0], 0, SS_PROCESS_BUFFER_SIZE * sizeof(double)); + memset(processBuffer[1], 0, SS_PROCESS_BUFFER_SIZE * sizeof(double)); + + for (int i=0; i<len; i++) { + // Current channel sample data: + data = channels[ch].sample->data; + gain_factor = channels[ch].gain_factor; + // Current velocity factor: + + if (channels[ch].sample->channels == 2) { + // + // Stereo sample: + // + // Add from sample: + out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); + out2 = (double) (data[channels[ch].playoffset + 1] * gain_factor * channels[ch].balanceFactorR); + channels[ch].playoffset += 2; + } + else { + // + // Mono sample: + // + out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); + out2 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorR); + channels[ch].playoffset++; + } + + processBuffer[0][i] = out1; + processBuffer[1][i] = out2; + + // If send-effects tap is on, tap signal to respective lineout channel + for (int j=0; j<SS_NR_OF_SENDEFFECTS; j++) { + if (channels[ch].sendfxlevel[j] != 0.0) { + //If the effect has 2 inputs (stereo in): + if (sendEffects[j].inputs == 2) { + sendFxLineOut[j][0][i]+= (out1 * channels[ch].sendfxlevel[j]); + sendFxLineOut[j][1][i]+= (out2 * channels[ch].sendfxlevel[j]); + } + //If the effect is mono (1 input), only use first fxLineOut + else if (sendEffects[j].inputs == 1) { + sendFxLineOut[j][0][i]+= ((out1 + out2) * channels[ch].sendfxlevel[j] / 2.0); + } + //Effects with 0 or >2 inputs are ignored + } + } + + // + // If we've reached the last sample, set state to inactive + // + if (channels[ch].playoffset >= channels[ch].sample->samples) { + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + break; + } + } + // Add contribution for this channel, for this frame, to final result: + for (int i=0; i<len; i++) { + out[0][i+offset]+=processBuffer[0][i]; + out[1][i+offset]+=processBuffer[1][i]; + } + } + } + // Do something funny with the sendies: + for (int j=0; j<SS_NR_OF_SENDEFFECTS; j++) { + if (sendEffects[j].state == SS_SENDFX_ON) { + sendEffects[j].plugin->process(len); + for (int i=0; i<len; i++) { + //Effect has mono output: + if (sendEffects[j].outputs == 1) { + //Add the result to both channels: + out[0][i+offset]+=((sendEffects[j].retgain * sendFxReturn[j][0][i]) / 2.0); + out[1][i+offset]+=((sendEffects[j].retgain * sendFxReturn[j][0][i]) / 2.0); + } + else if (sendEffects[j].outputs == 2) { + // Effect has stereo output + out[0][i+offset]+=(sendEffects[j].retgain * sendFxReturn[j][0][i]); + out[1][i+offset]+=(sendEffects[j].retgain * sendFxReturn[j][1][i]); + } + } + } + } + // Finally master gain: + for (int i=0; i<len; i++) { + out[0][i+offset] = (out[0][i+offset] * master_vol); + out[1][i+offset] = (out[1][i+offset] * master_vol); + } + } + } + +//--------------------------------------------------------- +// showGui +/*! + \fn SimpleSynth::showGui + \brief Displays or hides the gui window + \param val true or false = gui shown or hidden + */ +//--------------------------------------------------------- +void SimpleSynth::showGui(bool val) + { + SS_TRACE_IN + gui->setShown(val); + SS_TRACE_OUT + } + +//--------------------------------------------------------- +/*! + \fn SimpleSynth::init + \brief Initializes the SimpleSynth + \param name string set to caption in the gui dialog + \return true if successful, false if unsuccessful + */ +//--------------------------------------------------------- +bool SimpleSynth::init(const char* name) + { + SS_TRACE_IN + SWITCH_SYNTH_STATE(SS_INITIALIZING); + gui = new SimpleSynthGui(); + gui->show(); + gui->setWindowTitle(name); + SWITCH_SYNTH_STATE(SS_RUNNING); + SS_TRACE_OUT + return true; + } + +//--------------------------------------------------------- +/*! + \fn SimpleSynth::getInitData + \brief Data for reinitialization of SimpleSynth when loading project + \param n - number of chars used in the data + \param data - data that is sent as a sysex to the synth on reload of project + */ +//--------------------------------------------------------- +void SimpleSynth::getInitData(int* n, const unsigned char** data) + { + SS_TRACE_IN + // Calculate length of data + // For each channel, we need to store volume, pan, noff, onoff + int len = SS_NR_OF_CHANNEL_CONTROLLERS * SS_NR_OF_CHANNELS; + // Sampledata: filenames len + for (int i=0; i<SS_NR_OF_CHANNELS; i++) { + if (channels[i].sample) { + int filenamelen = strlen(channels[i].sample->filename.c_str()) + 2; + len+=filenamelen; + } + else + len++; //Add place for SS_NO_SAMPLE + } + len+=3; // 1 place for SS_SYSEX_INIT_DATA, 1 byte for master vol, 1 byte for version data + + // Effect data length + len++; //Add place for SS_SYSEX_INIT_DATA_VERSION, as control + + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + Plugin* plugin = sendEffects[i].plugin; + if (plugin) { + int namelen = plugin->lib().size() + 2; + int labelnamelen = plugin->label().size() + 2; + len+=(namelen + labelnamelen); + + len+=3; //1 byte for nr of parameters, 1 byte for return gain, 1 byte for effect on/off + len+=sendEffects[i].nrofparameters; // 1 byte for each parameter value + } + else { + len++; //place for SS_NO_PLUGIN + } + } + + // First, SS_SYSEX_INIT_DATA + byte* buffer = new byte[len]; + memset(buffer, 0, len); + buffer[0] = SS_SYSEX_INIT_DATA; + buffer[1] = SS_SYSEX_INIT_DATA_VERSION; + if (SS_DEBUG_INIT) { + printf("Length of init data: %d\n", len); + printf("buffer[0] - SS_SYSEX_INIT_DATA: %d\n", SS_SYSEX_INIT_DATA); + printf("buffer[1] - SS_SYSEX_INIT_DATA_VERSION: %d\n", SS_SYSEX_INIT_DATA_VERSION); + } + int i = 2; + // All channels: + // 0 - volume ctrlval (0-127) + // 1 - pan (0-127) + // 2 - noff ignore (0-1) + // 3 - channel on/off (0-1) + // 4 - 7 - sendfx 1-4 (0-127) + // 8 - len of filename, n + // 9 - 9+n - filename + for (int ch=0; ch<SS_NR_OF_CHANNELS; ch++) { + buffer[i] = (byte) channels[ch].volume_ctrlval; + buffer[i+1] = (byte) channels[ch].pan; + buffer[i+2] = (byte) channels[ch].noteoff_ignore; + buffer[i+3] = (byte) channels[ch].channel_on; + buffer[i+4] = (byte) round(channels[ch].sendfxlevel[0] * 127.0); + buffer[i+5] = (byte) round(channels[ch].sendfxlevel[1] * 127.0); + buffer[i+6] = (byte) round(channels[ch].sendfxlevel[2] * 127.0); + buffer[i+7] = (byte) round(channels[ch].sendfxlevel[3] * 127.0); + + if (SS_DEBUG_INIT) { + printf("Channel %d:\n", ch); + printf("buffer[%d] - channels[ch].volume_ctrlval = \t%d\n", i, channels[ch].volume_ctrlval); + printf("buffer[%d] - channels[ch].pan = \t\t%d\n", i+1, channels[ch].pan); + printf("buffer[%d] - channels[ch].noteoff_ignore = \t%d\n", i+2, channels[ch].noteoff_ignore ); + printf("buffer[%d] - channels[ch].channel_on = \t%d\n", i+3, channels[ch].channel_on); + for (int j= i+4; j < i+8; j++) { + printf("buffer[%d] - channels[ch].sendfxlevel[%d]= \t%d\n", j, j-i-4, (int)round(channels[ch].sendfxlevel[j-i-4] * 127.0)); + } + } + if (channels[ch].sample) { + int filenamelen = strlen(channels[ch].sample->filename.c_str()) + 1; + buffer[i+8] = (byte) filenamelen; + memcpy((buffer+(i+9)), channels[ch].sample->filename.c_str(), filenamelen); + if (SS_DEBUG_INIT) { + printf("buffer[%d] - filenamelen: %d\n", i+8, filenamelen); + printf("buffer[%d] - buffer[%d] - filename: ", (i+9), (i+9) + filenamelen - 1); + for (int j = i+9; j< i+9+filenamelen; j++) { + printf("%c",buffer[j]); + } + printf("\n"); + } + i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1 + filenamelen); + } + else { + buffer[i+8] = SS_NO_SAMPLE; + if (SS_DEBUG_INIT) { + printf("buffer[%d]: SS_NO_SAMPLE: - %d\n", i+8, SS_NO_SAMPLE); + } + i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1); + } + } + if (SS_DEBUG_INIT) { + printf("buffer[%d]: Master vol: - %d\n", i, master_vol_ctrlval); + } + buffer[i] = master_vol_ctrlval; + *(data) = buffer; *n = len; + i++; + + //Send effects: + buffer[i] = SS_SYSEX_INIT_DATA_VERSION; //Just for check + if (SS_DEBUG_INIT) { + printf("buffer[%d]: Control value, SS_SYSEX_INIT_DATA_VERSION\n", i); + } + + i++; + for (int j=0; j<SS_NR_OF_SENDEFFECTS; j++) { + if (sendEffects[j].plugin) { + int labelnamelen = sendEffects[j].plugin->label().size() + 1; + buffer[i] = labelnamelen; + memcpy((buffer+i+1), sendEffects[j].plugin->label().toLatin1().data(), labelnamelen); + if (SS_DEBUG_INIT) { + printf("buffer[%d] - labelnamelen: %d\n", i, labelnamelen); + printf("buffer[%d] - buffer[%d] - filename: ", (i+1), (i+1) + labelnamelen - 1); + for (int k = i+1; k < i+1+labelnamelen; k++) { + printf("%c",buffer[k]); + } + printf("\n"); + } + + i+=(labelnamelen + 1); + + int namelen = sendEffects[j].plugin->lib().size() + 1; + buffer[i] = namelen; + memcpy((buffer+i+1), sendEffects[j].plugin->lib().toLatin1().data(), namelen); + if (SS_DEBUG_INIT) { + printf("buffer[%d] - libnamelen : %d\n", i, namelen); + printf("buffer[%d] - buffer[%d] - filename: ", (i+1), (i+1) + namelen - 1); + for (int k = i+1; k < i+1+namelen; k++) { + printf("%c",buffer[k]); + } + printf("\n"); + } + + i+=(namelen + 1); + + buffer[i]=sendEffects[j].nrofparameters; + if (SS_DEBUG_INIT) { + printf("buffer[%d]: sendEffects[%d].nrofparameters=%d\n", i, j, buffer[i]); + } + i++; + + buffer[i]=sendEffects[j].retgain_ctrlval; + if (SS_DEBUG_INIT) { + printf("buffer[%d]: sendEffects[%d].retgain_ctrlval=%d\n", i, j, buffer[i]); + } + i++; + + for (int k=0; k<sendEffects[j].nrofparameters; k++) { + //TODO: Convert to 127-scale + buffer[i] = sendEffects[j].plugin->getGuiControlValue(k); + if (SS_DEBUG_INIT) { + printf("buffer[%d]: sendEffects[%d].parameterval[%d]=%d\n", i, j, k, buffer[i]); + } + i++; + } + } + // No plugin loaded: + else { + buffer[i] = SS_NO_PLUGIN; + if (SS_DEBUG_INIT) { + printf("buffer[%d]: SS_NO_PLUGIN\n", i); + } + i++; + } + } + + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::parseInitData() + */ +void SimpleSynth::parseInitData(const unsigned char* data) + { + SS_TRACE_IN + //int len = strlen((const char*)data); + if (SS_DEBUG_INIT) { + printf("buffer[1], SS_SYSEX_INIT_DATA_VERSION=%d\n", *(data+1)); + } + const byte* ptr = data+2; + for (int ch=0; ch<SS_NR_OF_CHANNELS; ch++) { + channels[ch].volume_ctrlval = (byte) *(ptr); + + if (SS_DEBUG_INIT) { + printf("Channel %d:\n", ch); + printf("buffer[%ld] - channels[ch].volume_ctrlval = \t%d\n", ptr-data, *ptr); + printf("buffer[%ld] - channels[ch].pan = \t\t%d\n", ptr-data+1, *(ptr+1)); + printf("buffer[%ld] - channels[ch].noteoff_ignore = \t%d\n", ptr-data+2, *(ptr+2)); + printf("buffer[%ld] - channels[ch].channel_on = \t%d\n", ptr-data+3, *(ptr+3)); + } + updateVolume(ch, *(ptr)); + guiUpdateVolume(ch, *(ptr)); + + channels[ch].pan = *(ptr+1); + updateBalance(ch, *(ptr+1)); + guiUpdateBalance(ch, *(ptr+1)); + + channels[ch].noteoff_ignore = *(ptr+2); + guiUpdateNoff(ch, *(ptr+2)); + + channels[ch].channel_on = *(ptr+3); + guiUpdateChoff(ch, *(ptr+3)); + + ptr+=4; + + for (int i=0; i<4; i++) { + channels[ch].sendfxlevel[i] = (float) (*(ptr)/127.0); + guiUpdateSendFxLevel(ch, i, *(ptr)); + ptr++; + } + + bool hasSample = *(ptr); + ptr++; + + channels[ch].sample = 0; + channels[ch].playoffset = 0; + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + if (SS_DEBUG_INIT) { + printf("parseInitData: channel %d, volume: %f pan: %d bfL %f bfR %f chON %d s1: %f s2: %f s3: %f s4: %f\n", + ch, + channels[ch].volume, + channels[ch].pan, + channels[ch].balanceFactorL, + channels[ch].balanceFactorR, + channels[ch].channel_on, + channels[ch].sendfxlevel[0], + channels[ch].sendfxlevel[1], + channels[ch].sendfxlevel[2], + channels[ch].sendfxlevel[3] + ); + } + if (hasSample) { + std::string filenametmp = (const char*) ptr; + ptr+= strlen(filenametmp.c_str()) + 1; + //printf("We should load %s\n", filenametmp.c_str()); + loadSample(ch, filenametmp.c_str()); + } + else { + //Clear sample + clearSample(ch); + guiNotifySampleCleared(ch); + } + } + //Master vol: + master_vol_ctrlval = *(ptr); + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + guiUpdateMasterVol(master_vol_ctrlval); + if (SS_DEBUG_INIT) { + printf("Master vol: %d\n", master_vol_ctrlval); + } + ptr++; + + // Effects: + if (*(ptr) != SS_SYSEX_INIT_DATA_VERSION) { + fprintf(stderr, "Error loading init data - control byte not found. Skipping...\n"); + SS_TRACE_OUT + return; + } + ptr++; + + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + if (SS_DEBUG_INIT) + printf("buffer[%ld] - sendeffect[%d], labelnamelen=%d\n", ptr-data, i, *ptr); + int labelnamelen = *(ptr); + + if (labelnamelen != SS_NO_PLUGIN) { + ptr++; + std::string labelnametmp = (const char*) ptr; + ptr+= labelnamelen; + + //int libnamelen = *(ptr); + ptr++; + std::string libnametmp = (const char*) ptr; + ptr+= strlen(libnametmp.c_str()) + 1; + + + initSendEffect(i, libnametmp.c_str(), labelnametmp.c_str()); + //initSendEffect(0, "cmt", "freeverb3"); + + byte params = *(ptr); + byte retgain = *(ptr+1); + ptr+=2; + + sendEffects[i].nrofparameters = params; + + sendEffects[i].retgain_ctrlval = retgain; + sendEffects[i].retgain = retgain; + sendEffects[i].retgain = (double) retgain/ 75.0; + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_PLUGIN_RETURNLEVEL_CONTROLLER(i), retgain); + gui->writeEvent(ev); + + for (int j=0; j<params; j++) { + if (SS_DEBUG_INIT) + printf("buffer[%ld] - sendeffect[%d], parameter[%d]=%d\n", ptr-data, i, j, *ptr); + setFxParameter(i, j, sendEffects[i].plugin->convertGuiControlValue(j, *(ptr))); + ptr++; + } + } + else { + if (sendEffects[i].plugin) + cleanupPlugin(i); + ptr++; + } + } + + SS_TRACE_OUT + } + +/*! + \fn SimpleSynth::loadSample(int chno, const char* filename) + */ +bool SimpleSynth::loadSample(int chno, const char* filename) + { + SS_TRACE_IN + SS_Channel* ch = &channels[chno]; + + // Thread stuff: + SS_SampleLoader* loader = new SS_SampleLoader; + loader->channel = ch; + loader->filename = std::string(filename); + loader->ch_no = chno; + if (SS_DEBUG) { + printf("Loader filename is: %s\n", filename); + } + pthread_t sampleThread; + pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); + pthread_attr_init(attributes); + pthread_attr_setdetachstate(attributes, PTHREAD_CREATE_DETACHED); + if (pthread_create(&sampleThread, attributes, ::loadSampleThread, (void*) loader)) { + perror("creating thread failed:"); + pthread_attr_destroy(attributes); + delete loader; + return false; + } + + pthread_attr_destroy(attributes); + SS_TRACE_OUT + return true; + } + +/*! + \fn loadSampleThread(void* p) + \brief Since process needs to respond withing a certain time, loading of samples need to be done in a separate thread + */ +static void* loadSampleThread(void* p) + { + SS_TRACE_IN + pthread_mutex_lock(&SS_LoaderMutex); + + // Crit section: + SS_State prevState = synth_state; + SWITCH_SYNTH_STATE(SS_LOADING_SAMPLE); + SS_SampleLoader* loader = (SS_SampleLoader*) p; + SS_Channel* ch = loader->channel; + int ch_no = loader->ch_no; + + if (ch->sample) { + delete[] ch->sample->data; + delete ch->sample; + } + ch->sample = new SS_Sample; + SS_Sample* smp = ch->sample; + + SNDFILE* sf; + const char* filename = loader->filename.c_str(); + SF_INFO sfi; + + if (SS_DEBUG) + printf("loadSampleThread: filename = %s\n", filename); + + sf = sf_open(filename, SFM_READ, &sfi); + if (sf == 0) { + fprintf(stderr,"Error opening file: %s\n", filename); + SWITCH_SYNTH_STATE(prevState); + simplesynth_ptr->guiSendSampleLoaded(false, loader->ch_no, filename); + delete ch->sample; ch->sample = 0; + delete loader; + pthread_mutex_unlock(&SS_LoaderMutex); + SS_TRACE_OUT + pthread_exit(0); + } + + //Print some info: + if (SS_DEBUG) { + printf("Sample info:\n"); + printf("Frames: \t%ld\n", (long) sfi.frames); + printf("Channels: \t%d\n", sfi.channels); + printf("Samplerate: \t%d\n", sfi.samplerate); + } + + // + // Allocate and read the thingie + // + + // If current samplerate is the same as MusE's: + if (SS_samplerate == sfi.samplerate) { + smp->data = new float[sfi.channels * sfi.frames]; + sf_count_t n = sf_readf_float(sf, smp->data, sfi.frames); + smp->frames = sfi.frames; + smp->samples = (n * sfi.channels); + smp->channels = sfi.channels; + if (SS_DEBUG) { + printf("%ld frames read\n", (long) n); + } + } + else // otherwise, resample: + { + smp->channels = sfi.channels; + // Get new nr of frames: + double srcratio = (double) SS_samplerate/ (double) sfi.samplerate; + smp->frames = (long) floor(((double) sfi.frames * srcratio)); + smp->frames = (sfi.channels == 1 ? smp->frames * 2 : smp->frames ); // Double nr of new frames if mono->stereo + smp->samples = smp->frames * smp->channels; + + if (SS_DEBUG) { + printf("Resampling from %ld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); + printf("Nr of new samples: %ld\n", smp->samples); + } + + // Read to temporary: + float temp[sfi.frames * sfi.channels]; + int frames_read = sf_readf_float(sf, temp, sfi.frames); + if (frames_read != sfi.frames) { + fprintf(stderr,"Error reading sample %s\n", filename); + simplesynth_ptr->guiSendSampleLoaded(false, loader->ch_no, filename); + sf_close(sf); + SWITCH_SYNTH_STATE(prevState); + delete ch->sample; ch->sample = 0; + delete loader; + pthread_mutex_unlock(&SS_LoaderMutex); + pthread_exit(0); + SS_TRACE_OUT + } + + // Allocate mem for the new one + smp->data = new float[smp->frames * smp->channels]; + memset(smp->data, 0, sizeof(float)* smp->frames * smp->channels); + + // libsamplerate & co (secret rabbits in the code!) + SRC_DATA srcdata; + srcdata.data_in = temp; + srcdata.data_out = smp->data; + srcdata.input_frames = sfi.frames; + srcdata.output_frames = smp->frames; + srcdata.src_ratio = (double) SS_samplerate / (double) sfi.samplerate; + + if (SS_DEBUG) { + printf("Converting sample....\n"); + } + + if (src_simple(&srcdata, SRC_SINC_BEST_QUALITY, sfi.channels)) { + SS_ERROR("Error when resampling, ignoring current sample"); + //TODO: deallocate and stuff + } + else if (SS_DEBUG) { + printf("Sample converted. %ld input frames used, %ld output frames generated\n", + srcdata.input_frames_used, + srcdata.output_frames_gen); + } + } + //Just close the dam thing + sf_close(sf); + SWITCH_SYNTH_STATE(prevState); + ch->sample->filename = loader->filename; + simplesynth_ptr->guiSendSampleLoaded(true, ch_no, filename); + delete loader; + pthread_mutex_unlock(&SS_LoaderMutex); + SS_TRACE_OUT + pthread_exit(0); + } + + +//static Mess* instantiate(int sr, const char* name) +static Mess* instantiate(int sr, QWidget*, QString* projectPathPtr, const char* name) + { + printf("SimpleSynth sampleRate %d\n", sr); + SimpleSynth* synth = new SimpleSynth(sr); + if (!synth->init(name)) { + delete synth; + synth = 0; + } + return synth; + } + + +/*! + \fn SimpleSynth::updateBalance(int pan) + */ +void SimpleSynth::updateBalance(int ch, int val) + { + SS_TRACE_IN + channels[ch].pan = val; + + // Balance: + channels[ch].balanceFactorL = 1.0; + channels[ch].balanceFactorR = 1.0; + double offset = 0; + int dev = val - 64; + offset = (double) dev / 64.0; + if (offset < 0) { + channels[ch].balanceFactorR = 1.0 + offset; + } + else { + channels[ch].balanceFactorL = 1.0 - offset; + } + + if (SS_DEBUG_MIDI) + printf("balanceFactorL %f balanceFactorR %f\n", channels[ch].balanceFactorL, channels[ch].balanceFactorR); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::updateVolume(int invol_ctrlval) + */ +void SimpleSynth::updateVolume(int ch, int invol_ctrlval) + { + SS_TRACE_IN + channels[ch].volume = (double)invol_ctrlval/ (double) SS_CHANNEL_VOLUME_QUOT; + channels[ch].volume_ctrlval = invol_ctrlval; + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiUpdateBalance(int ch, int bal) + */ +void SimpleSynth::guiUpdateBalance(int ch, int bal) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, ch, ME_CONTROLLER, SS_CHANNEL_PAN_CONTROLLER(ch), bal); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiUpdateVolume(int ch, int val) + */ +void SimpleSynth::guiUpdateVolume(int ch, int val) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_CHANNEL_VOLUME_CONTROLLER(ch), val); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiUpdateNoff(bool b) + */ +void SimpleSynth::guiUpdateNoff(int ch, bool b) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_CHANNEL_NOFF_CONTROLLER(ch), b); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiUpdateChoff(int ch, bool b) + */ +void SimpleSynth::guiUpdateChoff(int ch, bool b) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_CHANNEL_ONOFF_CONTROLLER(ch), b); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiUpdateMasterVol(int val) + */ +void SimpleSynth::guiUpdateMasterVol(int val) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_MASTER_CTRL_VOLUME, val); + gui->writeEvent(ev); + SS_TRACE_OUT + } + +/*! + \fn SimpleSynth::guiUpdateSendFxLevel(int fxid, int level) + */ +void SimpleSynth::guiUpdateSendFxLevel(int channel, int fxid, int level) + { + SS_TRACE_IN + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, SS_CHANNEL_SENDFX_CONTROLLER(channel, fxid), level); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiSendSampleLoaded(int ch, const char* filename) + */ +void SimpleSynth::guiSendSampleLoaded(bool success, int ch, const char* filename) + { + SS_TRACE_IN + int len = strlen(filename) + 3; //2 + filenamelen + 1; + byte out[len]; + + if (success) { + out[0] = SS_SYSEX_LOAD_SAMPLE_OK; + } + else { + out[0] = SS_SYSEX_LOAD_SAMPLE_ERROR; + } + out[1] = ch; + memcpy(out+2, filename, strlen(filename)+1); + MidiPlayEvent ev(0, 0, ME_SYSEX, out, len); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiSendError(const char* errorstring) + */ +void SimpleSynth::guiSendError(const char* errorstring) + { + SS_TRACE_IN + byte out[strlen(errorstring)+2]; + out[0] = SS_SYSEX_ERRORMSG; + memcpy(out+1, errorstring, strlen(errorstring) +1); + SS_TRACE_OUT + } + +extern "C" + { + static MESS descriptor = { + "SimpleSynth", + "Mathias Lundgren (lunar_shuttle@users.sf.net)", + "0.1", //Version string + MESS_MAJOR_VERSION, MESS_MINOR_VERSION, + instantiate, + }; + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) + const MESS* mess_descriptor() { return &descriptor; } + } + + +/*! + \fn SimpleSynth::initSendEffect(int sendeffectid, QString lib, QString name) + */ +bool SimpleSynth::initSendEffect(int id, QString lib, QString name) + { + SS_TRACE_IN + bool success = false; + if (sendEffects[id].plugin) { + //Cleanup if one was already there: + cleanupPlugin(id); + } + sendEffects[id].plugin = (LadspaPlugin*) plugins.find(lib, name); + LadspaPlugin* plugin = sendEffects[id].plugin; + if (plugin) { //We found one + + sendEffects[id].inputs = plugin->inports(); + sendEffects[id].outputs = plugin->outports(); + + if (plugin->instantiate()) { + SS_DBG2("Plugin instantiated", name.toLatin1().data()); + SS_DBG_I("Parameters", plugin->parameter()); + SS_DBG_I("No of inputs", plugin->inports()); + SS_DBG_I("No of outputs",plugin->outports()); + SS_DBG_I("Inplace-capable", plugin->inPlaceCapable()); + + // Connect inputs/outputs: + // If single output/input, only use first channel in sendFxLineOut/sendFxReturn + SS_DBG("Connecting ports..."); + plugin->connectInport(0, sendFxLineOut[id][0]); + if (plugin->inports() == 2) + plugin->connectInport(1, sendFxLineOut[id][1]); + else if (plugin->inports() > 2) { + fprintf(stderr, "Plugin has more than 2 inputs, not supported\n"); + } + + plugin->connectOutport(0, sendFxReturn[id][0]); + if (plugin->outports() == 2) + plugin->connectOutport(1, sendFxReturn[id][1]); + else if (plugin->outports() > 2) { + fprintf(stderr, "Plugin has more than 2 outputs, not supported\n"); + } + SS_DBG("Ports connected"); + if (plugin->start()) { + sendEffects[id].state = SS_SENDFX_ON; + success = true; + + int n = plugin->parameter(); + sendEffects[id].nrofparameters = n; + + // This is not nice, but freeverb doesn't want to play until some values are set: + if (name == "freeverb3") { + setFxParameter(id, 2, 0.5); + setFxParameter(id, 3, 0.5); + setFxParameter(id, 4, 0.5); + guiUpdateFxParameter(id, 2, 0.5); + guiUpdateFxParameter(id, 3, 0.5); + guiUpdateFxParameter(id, 4, 0.5); + } + } + //TODO: cleanup if failed + } + } + //Notify gui + int len = 3; + byte out[len]; + out[0] = SS_SYSEX_LOAD_SENDEFFECT_OK; + out[1] = id; + int j=0; + for (iPlugin i = plugins.begin(); i!=plugins.end(); i++, j++) { + if ((*i)->lib() == plugin->lib() && (*i)->label() == plugin->label()) { + out[2] = j; + MidiPlayEvent ev(0, 0, ME_SYSEX, out, len); + gui->writeEvent(ev); + } + } + + if (!success) { + QString errorString = "Error loading plugin \"" + plugin->label() + "\""; + guiSendError(errorString.toLatin1().data()); + } + return success; + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::setSendFxLevel(int channel, int effectid, double val) + */ +void SimpleSynth::setSendFxLevel(int channel, int effectid, double val) + { + SS_TRACE_IN + channels[channel].sendfxlevel[effectid] = val; + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::cleanupPlugin(int id) + */ +void SimpleSynth::cleanupPlugin(int id) + { + SS_TRACE_IN + LadspaPlugin* plugin = sendEffects[id].plugin; + plugin->stop(); + SS_DBG2("Stopped fx", plugin->label().toLatin1().data()); + sendEffects[id].nrofparameters = 0; + sendEffects[id].state = SS_SENDFX_OFF; + sendEffects[id].plugin = 0; + + byte d[2]; + d[0] = SS_SYSEX_CLEAR_SENDEFFECT_OK; + d[1] = id; + MidiPlayEvent ev(0, 0, ME_SYSEX, d, 2); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::setFxParameter(int fxid, int param, float val) + \brief Set fx-parameter on plugin and notify gui + */ +void SimpleSynth::setFxParameter(int fxid, int param, float val) + { + SS_TRACE_IN + LadspaPlugin* plugin = sendEffects[fxid].plugin; + if (SS_DEBUG_LADSPA) { + printf("Setting fx parameter: %f\n", val); + } + plugin->setParam(param, val); + //sendEffects[fxid].parameter[param] = val; + //guiUpdateFxParameter(fxid, param, val); + SS_TRACE_OUT + } + + + +/*! + \fn SimpleSynth::guiUpdateFxParameter(int fxid, int param, float val) + \brief Notify gui of changed fx-parameter + */ +void SimpleSynth::guiUpdateFxParameter(int fxid, int param, float val) + { + SS_TRACE_IN + LadspaPlugin* plugin = sendEffects[fxid].plugin; + float min, max; + plugin->range(param, &min, &max); + //offset: + val-= min; + + int intval = plugin->getGuiControlValue(param); + /*if (plugin->isLog(param)) { + intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); + } + else if (plugin->isBool(param)) { + intval = (int) val; + } + else { + float scale = SS_PLUGIN_PARAM_MAX / (max - min); + intval = (int) ((val - min) * scale); + }*/ + if (SS_DEBUG_MIDI) { + printf("Updating gui, fx parameter. fxid=%d, param=%d val=%d\n", fxid, param, intval); + } + + byte d[4]; + d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER_OK; + d[1] = fxid; + d[2] = param; + d[3] = intval; + MidiPlayEvent ev(0, 0, ME_SYSEX, d, 4); + gui->writeEvent(ev); + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::clearSample(int ch) + \brief Clears a sample (actually clears a channel) + */ +void SimpleSynth::clearSample(int ch) + { + SS_TRACE_IN + if (channels[ch].sample) { + if (SS_DEBUG) + printf("Clearing sample on channel %d\n", ch); + SS_State prevstate = synth_state; + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + SWITCH_SYNTH_STATE(SS_CLEARING_SAMPLE); + if (channels[ch].sample->data) { + delete[] channels[ch].sample->data; + channels[ch].sample->data = 0; + } + if (channels[ch].sample) { + delete channels[ch].sample; + channels[ch].sample = 0; + } + SWITCH_SYNTH_STATE(prevstate); + guiNotifySampleCleared(ch); + if (SS_DEBUG) { + printf("Clear sample - sample cleared on channel %d\n", ch); + } + } + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynth::guiNotifySampleCleared(int ch) + */ +void SimpleSynth::guiNotifySampleCleared(int ch) + { + SS_TRACE_IN + byte d[2]; + d[0] = SS_SYSEX_CLEAR_SAMPLE_OK; + d[1] = (byte) ch; + MidiPlayEvent ev(0, 0, ME_SYSEX, d, 2); + gui->writeEvent(ev); + SS_TRACE_OUT + } diff --git a/muse2/synti/simpledrums2/simpledrums.h b/muse2/synti/simpledrums2/simpledrums.h new file mode 100644 index 00000000..f9463800 --- /dev/null +++ b/muse2/synti/simpledrums2/simpledrums.h @@ -0,0 +1,173 @@ +// +// C++ Interface: simplesynth +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef SIMPLESYNTH_H +#define SIMPLESYNTH_H + +#include <sndfile.h> +#include "libsynti/mess.h" +#include "common.h" +#include "libsynti/mpevent.h" +#include "simpledrumsgui.h" +#include "ssplugin.h" + +#define SS_NO_SAMPLE 0 +#define SS_NO_PLUGIN 0 + +#define SS_PROCESS_BUFFER_SIZE 4096 //TODO: Add initialization method for nr of frames in each process from MusE - if nr of frames > than this, this will fail +#define SS_SENDFX_BUFFER_SIZE SS_PROCESS_BUFFER_SIZE + +enum SS_ChannelState + { + SS_CHANNEL_INACTIVE=0, + SS_SAMPLE_PLAYING, + }; + +enum SS_State + { + SS_INITIALIZING=0, + SS_LOADING_SAMPLE, + SS_CLEARING_SAMPLE, + SS_RUNNING, + }; + +enum SS_SendFXState + { + SS_SENDFX_OFF=0, + SS_SENDFX_ON + }; + +struct SS_SendFx + { + SS_SendFXState state; + LadspaPlugin* plugin; + int inputs; + int outputs; + int retgain_ctrlval; + double retgain; + int nrofparameters; + }; + +struct SS_Sample + { + float* data; + int samplerate; + int bits; + std::string filename; + long samples; + long frames; + int channels; + SF_INFO sfinfo; + }; + +struct SS_Channel + { + SS_ChannelState state; + const char* name; + SS_Sample* sample; + int playoffset; + bool noteoff_ignore; + + double volume; + int volume_ctrlval; + + double cur_velo; + double gain_factor; + + int pan; + double balanceFactorL; + double balanceFactorR; + + bool channel_on; + + //Send fx: + double sendfxlevel[SS_NR_OF_SENDEFFECTS]; + }; + +struct SS_Controller + { + std::string name; + int num; + int min, max; + }; + +struct SS_SampleLoader + { + SS_Channel* channel; + std::string filename; + int ch_no; + }; + +class SimpleSynth : public Mess + { + public: + SimpleSynth(int); + + virtual ~SimpleSynth(); + + virtual bool guiVisible() const; + virtual bool hasGui() const; + virtual bool playNote(int arg1, int arg2, int arg3); + virtual bool processEvent(const MidiPlayEvent& arg1); + virtual bool setController(int arg1, int arg2, int arg3); + virtual bool sysex(int arg1, const unsigned char* arg2); + virtual const char* getPatchName(int arg1, int arg2, int arg3) const; + virtual const MidiPatch* getPatchInfo(int arg1, const MidiPatch* arg2) const; + virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5); + virtual void process(float** data, int offset, int len); + virtual void showGui(bool arg1); + virtual void getInitData(int*, const unsigned char**); + bool init(const char* name); + void guiSendSampleLoaded(bool success, int ch, const char* filename); + void guiSendError(const char* errorstring); + + static const char* synth_state_descr[]; + static const char* channel_state_descr[]; + +private: + SimpleSynthGui* gui; + + SS_Channel channels[SS_NR_OF_CHANNELS]; + SS_Controller controllers[SS_NR_OF_CONTROLLERS]; + bool setController(int channel, int id, int val, bool fromGui); + bool loadSample(int ch_no, const char* filename); + void parseInitData(const unsigned char* data); + void updateVolume(int ch, int in_volume_ctrlval); + void updateBalance(int ch, int pan); + void guiNotifySampleCleared(int ch); + void guiUpdateBalance(int ch, int bal); + void guiUpdateVolume(int ch, int val); + void guiUpdateNoff(int ch, bool b); + void guiUpdateChoff(int ch, bool b); + void guiUpdateMasterVol(int val); + void guiUpdateFxParameter(int fxid, int param, float val); + void guiUpdateSendFxLevel(int channel, int fxid, int level); + bool initSendEffect(int sendeffectid, QString lib, QString name); + void setSendFxLevel(int channel, int effectid, double val); + void cleanupPlugin(int id); + void setFxParameter(int fxid, int param, float val); + void clearSample(int ch); + double master_vol; + int master_vol_ctrlval; + + //Send effects: + SS_SendFx sendEffects[SS_NR_OF_SENDEFFECTS]; + float* sendFxLineOut[SS_NR_OF_SENDEFFECTS][2]; //stereo output (fed into LADSPA inputs),sent from the individual channels -> LADSPA fx + float* sendFxReturn[SS_NR_OF_SENDEFFECTS][2]; //stereo inputs, from LADSPA plugins, sent from LADSPA -> SS and added to the mix + double* processBuffer[2]; + }; + +static void* loadSampleThread(void*); +static pthread_mutex_t SS_LoaderMutex; +static SS_State synth_state; +static SimpleSynth* simplesynth_ptr; + +#endif diff --git a/muse2/synti/simpledrums2/simpledrumsgui.cpp b/muse2/synti/simpledrums2/simpledrumsgui.cpp new file mode 100644 index 00000000..ab511cec --- /dev/null +++ b/muse2/synti/simpledrums2/simpledrumsgui.cpp @@ -0,0 +1,890 @@ +// +// C++ Implementation: testogui +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include <QButtonGroup> +#include <QLabel> +#include <QFileDialog> +#include <QSocketNotifier> +#include <QLayout> +#include <QToolTip> +#include <QLineEdit> +#include <QMessageBox> + +#include "simpledrumsgui.h" +#include "libsynti/mpevent.h" +#include "muse/midi.h" +#include "ssplugingui.h" + +#define SS_VOLUME_MIN_VALUE 0 +#define SS_VOLUME_MAX_VALUE 127 +#define SS_VOLUME_DEFAULT_VALUE 100 +#define SS_MASTERVOL_MAX_VALUE 127 +#define SS_MASTERVOL_DEFAULT_VALUE 100.0/127.0 +#define SS_SENDFX_MIN_VALUE 0 +#define SS_SENDFX_MAX_VALUE 127 + +//Gui constants: +#define SS_BTNGRP_WIDTH 50 +#define SS_BTNGRP_HEIGHT 80 +#define SS_ONOFF_WIDTH 16 +#define SS_ONOFF_HEIGHT 21 +#define SS_VOLSLDR_WIDTH (SS_BTNGRP_WIDTH - 8) +#define SS_VOLSLDR_LENGTH 120 +#define SS_PANSLDR_WIDTH (SS_BTNGRP_WIDTH - 8) +#define SS_PANSLDR_LENGTH 20 +#define SS_PANSLDR_DEFAULT_VALUE 63 +#define SS_NONOFF_LABEL_WIDTH 30 +#define SS_NONOFF_LABEL_HEIGHT 16 +#define SS_NONOFF_WIDTH SS_ONOFF_WIDTH +#define SS_NONOFF_HEIGHT SS_ONOFF_HEIGHT +#define SS_SENDFX_WIDTH ((SS_BTNGRP_WIDTH/2) - 4) +//#define SS_SENDFX_WIDTH 28 +#define SS_SENDFX_HEIGHT SS_SENDFX_WIDTH +#define SS_MASTERSLDR_WIDTH (SS_BTNGRP_WIDTH - 8) +#define SS_MASTERSLDR_HEIGHT (SS_BTNGRP_HEIGHT - 4) + + +// Sample groupbox + +#define SS_SAMPLENAME_LABEL_WIDTH 30 +#define SS_SAMPLENAME_LABEL_HEIGHT 21 +#define SS_SAMPLENAME_LABEL_XOFF 4 + +#define SS_SAMPLE_LOAD_WIDTH 15 +#define SS_SAMPLE_LOAD_HEIGHT 19 + +#define SS_SAMPLE_CLEAR_WIDTH SS_SAMPLE_LOAD_WIDTH +#define SS_SAMPLE_CLEAR_HEIGHT SS_SAMPLE_LOAD_HEIGHT + +#define SS_SAMPLENAME_LINEEDIT_WIDTH 90 +#define SS_SAMPLENAME_LINEEDIT_HEIGHT 21 + +#define SS_SAMPLE_INFO_LINE_HEIGHT 22 +#define SS_SAMPLE_INFO_LINE_WIDTH (SS_SAMPLENAME_LINEEDIT_XOFF + SS_SAMPLENAME_LINEEDIT_WIDTH) + +#define SS_GUI_WINDOW_WIDTH ((SS_NR_OF_CHANNELS +1) * SS_BTNGRP_XOFF) +#define SS_MAIN_GROUPBOX_HEIGHT 200 +#define SS_GUI_WINDOW_HEIGHT (SS_BTNGRP_HEIGHT + SS_MAIN_GROUPBOX_HEIGHT) +#define SS_MAIN_GROUPBOX_WIDTH SS_GUI_WINDOW_WIDTH + +SimpleSynthGui* simplesynthgui_ptr; + + +/*! + \fn QChannelSlider::QChannelSlider(Qt::Orientation orientation, int ch, QWidget* parent, const char* name) + */ +QChannelSlider::QChannelSlider(Qt::Orientation orientation, int ch, QWidget* parent) + : QSlider(orientation, parent) + { + channel = ch; + } + + +/*! + \fn QChannelSlider::getChannel() + */ +int QChannelSlider::getChannel() + { + return channel; + } + + +/*! + \fn QChannelSlider::setChannel(int ch) + */ +void QChannelSlider::setChannel(int ch) + { + channel = ch; + } + +/*! + \fn QChannelSlider::setValue(int val) + */ +void QChannelSlider::setValue(int val) + { + val = (val > 127 ? 127 : val); + val = (val < 0 ? 0 : val); + QSlider::setValue(val); + emit valueChanged(channel, val); + } + +/*! + \fn QInvertedChannelSlider::setValue(int val) + */ +void QInvertedChannelSlider::setValue(int val) + { + int inverted = this->maximum() - val; + inverted = (inverted > 127 ? 127 : inverted); + inverted = (inverted < 0 ? 0 : inverted); + QSlider::setValue(val); + emit valueChanged(channel, inverted); + } + +/*! + \fn QInvertedSlider::setValue(int val) + */ +void QInvertedSlider::setValue(int val) + { + int inverted = this->maximum() - val; + inverted = (inverted > 127 ? 127 : inverted); + inverted = (inverted < 0 ? 0 : inverted); + emit invertedValueChanged(inverted); + QSlider::setValue(val); + } + + +/*! + \fn QChannelCheckbox::QChannelCheckbox(QWidget* parent, int ch) + */ +QChannelCheckbox::QChannelCheckbox(QWidget* parent, int ch) + : QCheckBox(parent) + { + channel = ch; + connect(this, SIGNAL(clicked()), SLOT(isClicked())); + } + + +/*! + \fn QChannelCheckbox::isClicked() + */ +void QChannelCheckbox::isClicked() + { + emit channelState(channel, this->isChecked()); + } + +/*! + \fn QChannelButton::QChannelButton(QWidget* parent, const char* text, int ch, const char* name) + */ +QChannelButton::QChannelButton(QWidget* parent, const char* text, int ch) + : QPushButton(parent), channel (ch) + { + connect(this, SIGNAL(clicked()), SLOT(isClicked())); + setText(text); + } + +/*! + \fn QChannelButton::isClicked() + */ +void QChannelButton::isClicked() + { + emit channelState(channel, this->isChecked()); + } + +/*! + \fn QChannelDial() + */ +QChannelDial::QChannelDial(QWidget* parent, int ch, int fxid) + : QDial(parent) + { + setTracking(true); + channel = ch; + sendfxid = fxid; + } + +/*! + \fn QChannelSlider::setValue(int val) + */ +void QChannelDial::setValue(int val) + { + QDial::setValue(val); + emit valueChanged(channel, sendfxid, val); + } + +/*! + \fn SimpleSynthGui::SimpleSynthGui() + */ +SimpleSynthGui::SimpleSynthGui() + { + SS_TRACE_IN + setupUi(this); + simplesynthgui_ptr = this; + pluginGui = new SS_PluginGui(this); + pluginGui->hide(); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); //, 3); + QHBoxLayout* channelLayout = new QHBoxLayout; + mainLayout->addLayout(channelLayout); + + //this->setFixedWidth(SS_GUI_WINDOW_WIDTH); + //this->setFixedHeight(SS_GUI_WINDOW_HEIGHT); + for (int i=0; i<SS_NR_OF_CHANNELS; i++) { + channelButtonGroups[i] = new QGroupBox(this); +// channelButtonGroups[i]->setMinimumSize(SS_BTNGRP_WIDTH, SS_BTNGRP_HEIGHT); + channelButtonGroups[i]->setTitle(QString::number(i + 1)); + + QString name = QString("volumeSlider"); + name.append(i + 1); + + channelLayout->addWidget(channelButtonGroups[i]); + + QVBoxLayout* inchnlLayout = new QVBoxLayout(channelButtonGroups[i]); //, 2, 0, "channelinternallayout"); + inchnlLayout->setAlignment(Qt::AlignHCenter); + + onOff[i] = new QChannelCheckbox(channelButtonGroups[i], i); +// onOff[i]->setMinimumSize(SS_ONOFF_WIDTH, SS_ONOFF_HEIGHT); + onOff[i]->setToolTip("Channel " + QString::number(i + 1) + " on/off"); + inchnlLayout->addWidget(onOff[i]); + connect(onOff[i], SIGNAL(channelState(int, bool)), SLOT(channelOnOff(int, bool))); + + volumeSliders[i] = new QInvertedChannelSlider(Qt::Vertical, i, channelButtonGroups[i]); + volumeSliders[i]->setMinimum(SS_VOLUME_MIN_VALUE); + volumeSliders[i]->setMaximum(SS_VOLUME_MAX_VALUE); + volumeSliders[i]->setValue(SS_VOLUME_MAX_VALUE - SS_VOLUME_DEFAULT_VALUE); +// volumeSliders[i]->setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); + volumeSliders[i]->setToolTip("Volume, channel " + QString::number(i + 1)); +// setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); + inchnlLayout->addWidget(volumeSliders[i]); + connect(volumeSliders[i], SIGNAL(valueChanged(int, int)), SLOT(volumeChanged(int, int))); + + nOffLabel[i] = new QLabel(channelButtonGroups[i]); +// nOffLabel[i]->setMinimumSize(SS_NONOFF_LABEL_WIDTH, SS_NONOFF_LABEL_HEIGHT); + nOffLabel[i]->setText("nOff"); + inchnlLayout->addWidget(nOffLabel[i]); + + nOffIgnore[i] = new QChannelCheckbox(channelButtonGroups[i], i); +// nOffIgnore[i]->setMinimumSize(SS_NONOFF_WIDTH, SS_NONOFF_HEIGHT); + nOffIgnore[i]->setToolTip("Note off ignore, channel " + QString::number(i + 1)); + inchnlLayout->addWidget(nOffIgnore[i]); + connect(nOffIgnore[i], SIGNAL(channelState(int, bool)),SLOT(channelNoteOffIgnore(int, bool))); + + panSliders[i] = new QChannelSlider(Qt::Horizontal, i, channelButtonGroups[i]); + panSliders[i]->setRange(0, 127); + panSliders[i]->setValue(SS_PANSLDR_DEFAULT_VALUE); +// panSliders[i]->setMinimumSize(SS_PANSLDR_WIDTH, SS_PANSLDR_LENGTH); + panSliders[i]->setToolTip("Pan, channel " + QString::number(i + 1)); + inchnlLayout->addWidget(panSliders[i]); + connect(panSliders[i], SIGNAL(valueChanged(int, int)), SLOT(panChanged(int, int))); + + QGridLayout* dialGrid = new QGridLayout; + inchnlLayout->addLayout(dialGrid); + sendFxDial[i][0] = new QChannelDial(channelButtonGroups[i], i, 0); + sendFxDial[i][0]->setRange(0, 127); + sendFxDial[i][0]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][0]->setToolTip("Fx 1 send amount"); + //inchnlLayout->addWidget(sendFxDial[i][0]); + dialGrid->addWidget(sendFxDial[i][0], 0, 0, Qt::AlignCenter | Qt::AlignTop); + + connect(sendFxDial[i][0], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][1] = new QChannelDial(channelButtonGroups[i], i, 1); + sendFxDial[i][1]->setRange(0, 127); + //inchnlLayout->add(sendFxDial[i][1]); + dialGrid->addWidget(sendFxDial[i][1], 0, 1, Qt::AlignCenter | Qt::AlignTop); + sendFxDial[i][1]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][1]->setToolTip("Fx 2 send amount"); + + connect(sendFxDial[i][1], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][2] = new QChannelDial(channelButtonGroups[i], i, 2); + sendFxDial[i][2]->setRange(0, 127); + sendFxDial[i][2]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + //inchnlLayout->add(sendFxDial[i][2]); + dialGrid->addWidget(sendFxDial[i][2], 1, 0, Qt::AlignCenter | Qt::AlignTop); + sendFxDial[i][2]->setToolTip("Fx 3 send amount"); + connect(sendFxDial[i][2], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][3] = new QChannelDial(channelButtonGroups[i], i, 3); + sendFxDial[i][3]->setRange(0, 127); + sendFxDial[i][3]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][3]->setToolTip("Fx 4 send amount"); + + dialGrid->addWidget(sendFxDial[i][3], 1, 1, Qt::AlignCenter | Qt::AlignTop); + connect(sendFxDial[i][3], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + inchnlLayout->activate(); + //channelLayout->activate(); + } + + //Master buttongroup: + masterButtonGroup = new QGroupBox(this); + channelLayout->addWidget(masterButtonGroup); + QVBoxLayout* mbgLayout = new QVBoxLayout(masterButtonGroup); + mbgLayout->setAlignment(Qt::AlignCenter); +// masterButtonGroup->setMinimumSize(SS_BTNGRP_WIDTH, SS_BTNGRP_HEIGHT); + masterSlider = new QInvertedSlider(Qt::Vertical, masterButtonGroup); + masterSlider->setToolTip("Master volume"); + mbgLayout->addWidget(masterSlider); + masterSlider->setRange(0, 127); + masterSlider->setValue(SS_VOLUME_MAX_VALUE - (int)(SS_MASTERVOL_DEFAULT_VALUE*SS_VOLUME_MAX_VALUE)); +// masterSlider->setMinimumSize(SS_MASTERSLDR_WIDTH, SS_MASTERSLDR_HEIGHT); + connect(masterSlider, SIGNAL(invertedValueChanged(int)), SLOT(masterVolChanged(int))); + + //Main groupbox + mainGroupBox = new QGroupBox(this); + mainLayout->addWidget(mainGroupBox); + + QGridLayout* mgbLayout = new QGridLayout(mainGroupBox); // , 8, 3, 1); + + int i=0; + + for (int c=0; c<2; c++) { + for (int r=0; r<SS_NR_OF_CHANNELS/2; r++) { + QHBoxLayout* strip = new QHBoxLayout;//(mgbLayout, 5); + mgbLayout->addLayout(strip, r, c); + + QLabel* channelLabel = new QLabel(QString("Ch ") + QString::number(i + 1), mainGroupBox); + strip->addWidget(channelLabel); + + sampleNameLineEdit[i] = new QLineEdit(mainGroupBox); + sampleNameLineEdit[i]->setReadOnly(true); + strip->addWidget(sampleNameLineEdit[i]); + + loadSampleButton[i] = new QChannelButton(mainGroupBox, "L", i); +// loadSampleButton[i]->setMinimumSize(SS_SAMPLE_LOAD_WIDTH, SS_SAMPLE_LOAD_HEIGHT); + loadSampleButton[i]->setToolTip("Load sample on channel " + QString::number(i + 1)); + strip->addWidget(loadSampleButton[i]); + connect(loadSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(loadSampleDialogue(int))); + + clearSampleButton[i] = new QChannelButton(mainGroupBox, "C", i); +// clearSampleButton[i]->setMinimumSize(SS_SAMPLE_CLEAR_WIDTH, SS_SAMPLE_CLEAR_HEIGHT); + clearSampleButton[i]->setToolTip("Clear sample on channel " + QString::number(i + 1)); + strip->addWidget(clearSampleButton[i]); + connect(clearSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(clearSample(int))); + + i++; + } + } + + // Right bottom panel: + QGroupBox* rbPanel= new QGroupBox(mainGroupBox); + mgbLayout->addWidget(rbPanel, 1, 3, 7, 1, Qt::AlignCenter); + QGridLayout* rbLayout = new QGridLayout(rbPanel); // 6, 1, 8, 5); + + openPluginsButton = new QPushButton("&Send Effects"); + openPluginsButton->setToolTip("Configure LADSPA send effects"); + connect(openPluginsButton, SIGNAL(clicked()), SLOT(openPluginButtonClicked())); + rbLayout->addWidget(openPluginsButton, 2, 1, Qt::AlignCenter | Qt::AlignVCenter); + aboutButton = new QPushButton("About SimpleDrums"); + connect(aboutButton, SIGNAL(clicked()), SLOT(aboutButtonClicked())); +//TD rbLayout->addRowSpacing(3, 20); + rbLayout->addWidget(aboutButton, 4, 1, Qt::AlignLeft | Qt::AlignVCenter); + + + loadButton = new QPushButton(tr("&Load setup"), rbPanel); + connect(loadButton, SIGNAL(clicked()), SLOT(loadSetup())); + saveButton = new QPushButton(tr("&Save setup"), rbPanel); + connect(saveButton, SIGNAL(clicked()), SLOT(saveSetup())); + //rbLayout->addWidget(openPluginsButton, 1, 1, Qt::AlignCenter | Qt::AlignVCenter); +// rbLayout->addRowSpacing(2, 20); + rbLayout->addWidget(loadButton, 3, 1, Qt::AlignCenter | Qt::AlignVCenter); + rbLayout->addWidget(saveButton, 4, 1, Qt::AlignCenter | Qt::AlignVCenter); +// rbLayout->addRowSpacing(5, 20); + rbLayout->addWidget(aboutButton, 6, 1, Qt::AlignCenter | Qt::AlignVCenter); + + lastDir = ""; + //Connect socketnotifier to fifo + QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); + connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); + SS_TRACE_OUT + } + +/*! + \fn SimpleSynthGui::~SimpleSynthGui() + */ +SimpleSynthGui::~SimpleSynthGui() + { + SS_TRACE_IN + simplesynthgui_ptr = 0; + delete pluginGui; + SS_TRACE_OUT + } + +/*! + \fn SimpleSynthGui::readMessage(int) + */ +void SimpleSynthGui::readMessage(int) + { + MessGui::readMessage(); + } + +/*! + \fn SimpleSynthGui::processEvent(const MidiPlayEvent& ev) + */ +void SimpleSynthGui::processEvent(const MidiPlayEvent& ev) + { + SS_TRACE_IN + if (SS_DEBUG_MIDI) { + printf("GUI received midi event\n"); + } + if (ev.type() == ME_CONTROLLER) { + int id = ev.dataA(); + int val = ev.dataB(); + + // Channel controllers: + if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { + // Find out which channel we're dealing with: + id-= SS_FIRST_CHANNEL_CONTROLLER; + int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); + id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); + + int fxid = -1; + + if (SS_DEBUG_MIDI) { + printf("GUI received midi controller - id: %d val %d channel %d\n", id, val, ch); + } + + switch(id) { + case SS_CHANNEL_CTRL_VOLUME: + volumeSliders[ch]->blockSignals(true); + volumeSliders[ch]->setValue(SS_VOLUME_MAX_VALUE - val); + volumeSliders[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_PAN: + panSliders[ch]->blockSignals(true); + panSliders[ch]->setValue(val); + panSliders[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_NOFF: + nOffIgnore[ch]->blockSignals(true); + nOffIgnore[ch]->setChecked(val); + nOffIgnore[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_ONOFF: + onOff[ch]->blockSignals(true); + onOff[ch]->setChecked(val); + onOff[ch]->blockSignals(false); + break; + + case SS_CHANNEL_SENDFX1: + case SS_CHANNEL_SENDFX2: + case SS_CHANNEL_SENDFX3: + case SS_CHANNEL_SENDFX4: + fxid = id - SS_CHANNEL_SENDFX1; + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui::processEvent - Channel sendfx, fxid: %d, val: %d\n", fxid, val); + } + sendFxDial[ch][fxid]->blockSignals(true); + sendFxDial[ch][fxid]->setValue(val); + sendFxDial[ch][fxid]->blockSignals(false); + break; + + default: + if (SS_DEBUG_MIDI) + printf("SimpleSynthGui::processEvent - unknown controller received: %d\n", id); + } + } + // Master controllers: + else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { + if (id == SS_MASTER_CTRL_VOLUME) { + masterSlider->blockSignals(true); + masterSlider->setValue(SS_MASTERVOL_MAX_VALUE - val); + masterSlider->blockSignals(false); + } + } + else if (id>= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { + int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; + int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; + + // Plugin return-gain: + if (cmd == SS_PLUGIN_RETURN) { + if (SS_DEBUG_MIDI) + printf("SimpleSynthGui::processEvent - fx retgain received: fxid: %d val: %d\n", fxid, val); + + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); + pf->setRetGain(val); + } + } + } + // + // Sysexes: + // + else if (ev.type() == ME_SYSEX) { + byte* data = ev.data(); + int cmd = *data; + switch (cmd) { + case SS_SYSEX_LOAD_SAMPLE_OK: { + int ch = *(data+1); + QString filename = (const char*) (data+2); + sampleNameLineEdit[ch]->setText(filename.section('/',-1,-1)); + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sample %s loaded OK on channel: %d\n", filename.toLatin1().data(), ch); + } + if (!onOff[ch]->isChecked()) { + onOff[ch]->blockSignals(true); + onOff[ch]->setChecked(true); + onOff[ch]->blockSignals(false); + channelOnOff(ch, true); + } + break; + } + + case SS_SYSEX_LOAD_SAMPLE_ERROR: { + //int ch = *(data+1); + const char* filename = (const char*) (data+2); + /*QMessageBox* yn = new QMessageBox("Sample not found", "Failed to load sample: " + QString(filename) + "\n" + + "Do you want to open file browser and try to locate it elsewhere?", + QMessageBox::Warning, + QMessageBox::Yes, + QMessageBox::No, + QMessageBox::NoButton, + this);*/ + /*int res = QMessageBox::warning(this, + "SimpleDrums","Failed to load sample: " + QString(filename) + "\n" + + "Do you want to open file browser and try to locate it elsewhere?", + "&Yes", "&No"); + */ + //int res = yn->exec(); + printf("Error: Sample %s not found! TODO: Fix this\n", filename); + //if (res == 0) { + // loadSampleDialogue(ch); + // } + break; + } + + case SS_SYSEX_LOAD_SENDEFFECT_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex load sendeffect OK on fxid: %d\n", *(data+1)); + } + int fxid = *(data+1); + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); + pf->updatePluginValue(*(data+2)); + break; + } + + case SS_SYSEX_CLEAR_SENDEFFECT_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex clear sendeffect OK on fxid: %d\n", *(data+1)); + } + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); + pf->clearPluginDisplay(); + break; + } + + case SS_SYSEX_CLEAR_SAMPLE_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex clear samle OK on channel: %d\n", *(data+1)); + } + byte ch = *(data+1); + sampleNameLineEdit[ch]->setText(""); + break; + } + + case SS_SYSEX_SET_PLUGIN_PARAMETER_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - plugin parameter OK on fxid: %d\n", *(data+1)); + } + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); + int param = *(data+2); + int val = *(data+3); + pf->blockSignals(true); + pf->setParameterValue(param, val); + pf->blockSignals(false); + break; + } + + case SS_SYSEX_SEND_INIT_DATA: { +// FN: TODO +#if 1 + const unsigned initdata_len = ev.len() - 1; + byte* init_data = (data + 1); + QFileInfo fileInfo = QFileInfo(lastSavedProject); + + lastProjectDir = fileInfo.path(); + if (fileInfo.suffix() != "sds" && fileInfo.suffix() != "SDS") { + lastSavedProject += ".sds"; + fileInfo = QFileInfo(lastSavedProject); + } + QFile theFile(fileInfo.filePath()); + + // Write data + if (theFile.open(QIODevice::WriteOnly)) { + theFile.write((const char*)&initdata_len, sizeof(initdata_len)); // First write length + if (theFile.write((const char*)init_data, initdata_len) == -1) { + // Fatal error writing + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Fatal error when writing to file. Setup not saved.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } + theFile.close(); + } + else { + // An error occured when opening + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Error opening file. Setup was not saved.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } +#endif + + break; + } + + + default: + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui::processEvent - unknown sysex cmd received: %d\n", cmd); + } + break; + } + } + SS_TRACE_OUT + } + + +/*! + \fn SimpleSynthGui::volumeChanged(int val) + */ +void SimpleSynthGui::volumeChanged(int channel, int val) + { + setChannelVolume(channel, val); + } + +/*! + \fn SimpleSynthGui::panChanged(int channel, int value) + */ +void SimpleSynthGui::panChanged(int channel, int value) + { + sendController(0, SS_CHANNEL_PAN_CONTROLLER(channel), value); + } + +/*! + \fn SimpleSynthGui::channelOnOff(int channel, bool state) + */ +void SimpleSynthGui::channelOnOff(int channel, bool state) + { + sendController(0, SS_CHANNEL_ONOFF_CONTROLLER(channel), state); + } + +/*! + \fn SimpleSynthGui::channelNoteOffIgnore(bool state) + */ +void SimpleSynthGui::channelNoteOffIgnore(int channel, bool state) + { + sendController(0, SS_CHANNEL_NOFF_CONTROLLER(channel), (int) state); + } + +/*! + \fn SimpleSynthGui::sendFxChanged(int ch, int fxid, int val) + */ +void SimpleSynthGui::sendFxChanged(int ch, int fxid, int val) + { + sendController(0, SS_CHANNEL_SENDFX_CONTROLLER(ch, fxid), (int) val); + } + +/*! + \fn SimpleSynthGui::masterVolChanged(int val) + */ +void SimpleSynthGui::masterVolChanged(int val) + { + sendController(0, SS_MASTER_CTRL_VOLUME, val); + } + +/*! + \fn SimpleSynthGui::setChannelVolume(int channel, byte volume) + */ +void SimpleSynthGui::setChannelVolume(int channel, int volume) + { + //volumeSliders[channel]->setValue(SS_VOLUME_MAX_VALUE - volume); + sendController(0, SS_CHANNEL_VOLUME_CONTROLLER(channel), (int)volume); + } + + +/*! + \fn SimpleSynthGui::loadSampleDialogue(int channel) + */ +void SimpleSynthGui::loadSampleDialogue(int channel) + { + QString filename = + QFileDialog::getOpenFileName( + this, + tr("Load sample dialog"), + lastDir, + QString("*.wav *.WAV")); + + if (filename != QString::null) { + QFileInfo fi(filename); + lastDir = fi.path(); + + if (SS_DEBUG) + printf("lastDir = %s\n", lastDir.toLatin1().data()); + + int l = filename.length() + 4; + byte d[l]; + + d[0] = SS_SYSEX_LOAD_SAMPLE; + d[1] = (byte) channel; + d[2] = (byte) filename.length(); + memcpy(d+3, filename.toLatin1().data(), filename.length()+1); + sendSysex(d, l); + } + } + + + +/*! + \fn SimpleSynthGui::clearSample(int ch) + */ +void SimpleSynthGui::clearSample(int ch) + { + if (sampleNameLineEdit[ch]->text().length() > 0) { //OK, we've got a live one here + byte d[2]; + d[0] = SS_SYSEX_CLEAR_SAMPLE; + d[1] = (byte) ch; + sendSysex(d, 2); + sampleNameLineEdit[ch]->setText(""); + } + } + +/*! + \fn SimpleSynthGui::displayPluginGui() + */ +void SimpleSynthGui::displayPluginGui() + { + pluginGui->show(); + } + +/*! + \fn SimpleSynthGui::loadEffectInvoked(int fxid, QString lib, QString label) + */ +void SimpleSynthGui::loadEffectInvoked(int fxid, QString lib, QString label) + { + int l = 4 + lib.length() + label.length(); + byte d[l]; + d[0] = SS_SYSEX_LOAD_SENDEFFECT; + d[1] = (byte) fxid; + memcpy (d+2, lib.toLatin1().data(), lib.length()+1); + memcpy (d+3+lib.length(), label.toLatin1().data(), label.length()+1); + sendSysex(d, l); + } + + +/*! + \fn SimpleSynthGui::returnLevelChanged(int fxid, int val) + */ +void SimpleSynthGui::returnLevelChanged(int fxid, int val) + { + sendController(0, SS_PLUGIN_RETURNLEVEL_CONTROLLER(fxid), val); + } + + +/*! + \fn SimpleSynthGui::toggleEffectOnOff(int fxid, int state) + */ +void SimpleSynthGui::toggleEffectOnOff(int fxid, int state) + { + sendController(0, SS_PLUGIN_ONOFF_CONTROLLER(fxid), state); + } + + +/*! + \fn SimpleSynthGui::clearPlugin(int fxid) + */ +void SimpleSynthGui::clearPlugin(int fxid) + { + byte d[2]; + d[0] = SS_SYSEX_CLEAR_SENDEFFECT; + d[1] = fxid; + sendSysex(d, 2); + } + + +/*! + \fn SimpleSynthGui::effectParameterChanged(int fxid, int parameter, int val) + */ +void SimpleSynthGui::effectParameterChanged(int fxid, int parameter, int val) + { + //printf("Gui: effectParameterChanged: %d %d %d\n", fxid, parameter, val); + int len = 4; + byte d[len]; + d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER; + d[1] = (byte) fxid; + d[2] = (byte) parameter; + d[3] = (byte) val; + sendSysex(d, len); + } + + +/*! + \fn SimpleSynthGui::openPluginButtonClicked() + */ +void SimpleSynthGui::openPluginButtonClicked() + { + if (pluginGui->isVisible()) + pluginGui->raise(); + else + displayPluginGui(); + } + + +/*! + \fn SimpleSynthGui::aboutButtonClicked() + */ +void SimpleSynthGui::aboutButtonClicked() + { + QString caption = "SimpleDrums ver"; + caption+= SS_VERSIONSTRING; + QString text = caption + "\n\n(C) Copyright 2000-2004 Mathias Lundgren (lunar_shuttle@users.sf.net), Werner Schweer\nPublished under the GNU Public License"; + QMessageBox* msgBox = new QMessageBox(caption, text, QMessageBox::NoIcon, + QMessageBox::Ok, Qt::NoButton, Qt::NoButton, this); + msgBox->exec(); + } + + +/*! + \fn SimpleSynthGui::loadSetup() + \brief Load setup from file + */ +void SimpleSynthGui::loadSetup() + { + bool success = true; + QString filename = + QFileDialog::getOpenFileName(this, "Load setup dialog", lastProjectDir, + QString("*.sds *.SDS")); + + if (filename != QString::null) { + QFile theFile(filename); + if (theFile.open(QIODevice::ReadOnly)) { + unsigned initdata_len = 0; + if (theFile.read((char*)&initdata_len, sizeof(initdata_len)) == -1) + success = false; + + byte* init_data = new byte[initdata_len]; + if (theFile.read((char*)(init_data), initdata_len) == -1) + success = false; + + if (!success) { + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums Error Dialog", "Error opening/reading from file. Setup not loaded.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } + else { + sendSysex(init_data, initdata_len); + } + + delete[] init_data; + } + } + } + + +/*! + \fn SimpleSynthGui::saveSetup() + \brief Save setup to file + */ +void SimpleSynthGui::saveSetup() + { + QString filename = + QFileDialog::getSaveFileName(this, "Save setup dialog", lastProjectDir, + QString("*.sds *.SDS")); + + if (filename != QString::null) { + lastSavedProject = filename; + byte d[1]; + d[0] = SS_SYSEX_GET_INIT_DATA; + sendSysex(d, 1); // Makes synth send gui initdata, where rest of the saving takes place + } + } + diff --git a/muse2/synti/simpledrums2/simpledrumsgui.h b/muse2/synti/simpledrums2/simpledrumsgui.h new file mode 100644 index 00000000..16a5caf5 --- /dev/null +++ b/muse2/synti/simpledrums2/simpledrumsgui.h @@ -0,0 +1,214 @@ +// +// C++ Interface: testogui +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef __MUSE_TESTOGUI_H__ +#define __MUSE_TESTOGUI_H__ + +#include <QSlider> +#include <QCheckBox> +#include <QPushButton> +#include <QDial> +#include <QLabel> +//#include <QFileInfo> +#include <QGroupBox> + +#include "libsynti/gui.h" +#include "ui_simpledrumsguibase.h" +#include "common.h" + +class QButtonGroup; +class QLabel; +class SS_PluginGui; + +//-------------------------------------- +// QChannelSlider +//-------------------------------------- +class QChannelSlider: public QSlider + { + Q_OBJECT + + public: + QChannelSlider(Qt::Orientation, int ch, QWidget* paren = 0); + int getChannel(); + void setChannel(int ch); + + public slots: + virtual void setValue(int val); + + signals: + void valueChanged(int channel, int value); + + protected: + int channel; + }; + +//-------------------------------------- +// QInvertedSlider +//-------------------------------------- +class QInvertedSlider : public QSlider + { + Q_OBJECT + public: + QInvertedSlider(Qt::Orientation o, QWidget* parent = 0) + : QSlider(o, parent) {} + + public slots: + virtual void setValue(int val); + + signals: + void invertedValueChanged(int value); + }; + +//-------------------------------------- +// QInvertedChannelSlider +//-------------------------------------- +class QInvertedChannelSlider : public QChannelSlider + { + Q_OBJECT + public: + QInvertedChannelSlider(Qt::Orientation o, int channel, QWidget* parent = 0) + : QChannelSlider(o, channel, parent) {}; + + public slots: + virtual void setValue(int val); + }; + + +//-------------------------------------- +// QChannelOnOff +//-------------------------------------- + +class QChannelCheckbox : public QCheckBox + { + Q_OBJECT + public: + QChannelCheckbox(QWidget* parent, int channel); + + private: + int channel; + + private slots: + void isClicked(); + + signals: + void channelState(int channel, bool state); + }; + +//-------------------------------------- +// QChannelButton +//-------------------------------------- +class QChannelButton : public QPushButton + { + Q_OBJECT + + private: + int channel; + + public: + QChannelButton(QWidget* parent, const char* text, int ch); + + private slots: + void isClicked(); + + signals: + void channelState(int channel, bool state); + + }; + +//-------------------------------------- +// QChannelDial +//-------------------------------------- + +class QChannelDial : public QDial + { + Q_OBJECT + + public: + QChannelDial(QWidget* parent, int ch, int fxid); + + signals: + void valueChanged(int channel, int fxid, int val); + + public slots: + virtual void setValue(int val); + + protected: + int channel; + int sendfxid; + }; + +//-------------------------------------- +// SimpleSynthGui - the Gui +//-------------------------------------- +class SimpleSynthGui : public QDialog, public Ui::SimpleDrumsGuiBase, public MessGui + { + Q_OBJECT + private: + // MESS interface: + virtual void processEvent(const MidiPlayEvent& ev); + void setChannelVolume(int channel, int volume); + void displayPluginGui(); + QGroupBox* channelButtonGroups[SS_NR_OF_CHANNELS]; + QGroupBox* masterButtonGroup; + QGroupBox* mainGroupBox; + QInvertedChannelSlider* volumeSliders[SS_NR_OF_CHANNELS]; + QChannelSlider* panSliders[SS_NR_OF_CHANNELS]; + QChannelCheckbox* onOff[SS_NR_OF_CHANNELS]; + QChannelCheckbox* nOffIgnore[SS_NR_OF_CHANNELS]; + QChannelButton* loadSampleButton[SS_NR_OF_CHANNELS]; + QChannelButton* clearSampleButton[SS_NR_OF_CHANNELS]; + QLabel* nOffLabel[SS_NR_OF_CHANNELS]; + QLineEdit* sampleNameLineEdit[SS_NR_OF_CHANNELS]; + QInvertedSlider* masterSlider; + QChannelDial* sendFxDial[SS_NR_OF_CHANNELS][SS_NR_OF_SENDEFFECTS]; + + QPushButton* openPluginsButton; + QPushButton* aboutButton; + QPushButton* loadButton; + QPushButton* saveButton; + + + QString lastDir; + QString lastSavedProject; + QString lastProjectDir; + SS_PluginGui* pluginGui; + + public: + SimpleSynthGui(); + virtual ~SimpleSynthGui(); + + public slots: + void loadEffectInvoked(int fxid, QString lib, QString label); + void returnLevelChanged(int fxid, int val); + void toggleEffectOnOff(int fxid, int state); + void clearPlugin(int fxid); + void effectParameterChanged(int fxid, int parameter, int val); + + private slots: + void volumeChanged(int channel, int val); + void panChanged(int channel, int value); + void channelOnOff(int channel, bool state); + void channelNoteOffIgnore(int channel, bool state); + void masterVolChanged(int val); + void loadSampleDialogue(int channel); + void readMessage(int); + void clearSample(int ch); + void sendFxChanged(int ch, int fxid, int val); + void openPluginButtonClicked(); + void aboutButtonClicked(); + void loadSetup(); + void saveSetup(); + + }; + +extern SimpleSynthGui* simplesynthgui_ptr; + +#endif diff --git a/muse2/synti/simpledrums2/simpledrumsguibase.ui b/muse2/synti/simpledrums2/simpledrumsguibase.ui new file mode 100644 index 00000000..412bfdf6 --- /dev/null +++ b/muse2/synti/simpledrums2/simpledrumsguibase.ui @@ -0,0 +1,23 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>SimpleDrumsGuiBase</class> + <widget class="QDialog" name="SimpleDrumsGuiBase" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>602</width> + <height>509</height> + </rect> + </property> + <property name="windowTitle" > + <string>DrumSynth 0.1</string> + </property> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction>qPixmapFromMimeSource</pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/muse2/synti/simpledrums2/ssplugin.cpp b/muse2/synti/simpledrums2/ssplugin.cpp new file mode 100644 index 00000000..7e8986e7 --- /dev/null +++ b/muse2/synti/simpledrums2/ssplugin.cpp @@ -0,0 +1,460 @@ +// +// C++ Implementation: plugin +// +// Description: +// +// +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// Additions/modifications: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// Copyright: See COPYING file that comes with this distribution +// +// + +#include <QtCore> +#include <QtGui> +#include <stdlib.h> +#include <unistd.h> +#include <dlfcn.h> +#include "ssplugin.h" +#include "common.h" + +PluginList plugins; + + +Plugin::Plugin(const QFileInfo* f) + : fi(*f) + { + } + +//--------------------------------------------------------- +// loadPluginLib +//--------------------------------------------------------- + +static void loadPluginLib(QFileInfo* fi) + { + SS_TRACE_IN + if (SS_DEBUG_LADSPA) { + printf("loadPluginLib: %s\n", fi->fileName().toLatin1().data()); + } + void* handle = dlopen(fi->filePath().toAscii().data(), RTLD_NOW); + if (handle == 0) { + fprintf(stderr, "dlopen(%s) failed: %s\n", + fi->filePath().toAscii().data(), dlerror()); + return; + } + LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); + + if (!ladspa) { + const char *txt = dlerror(); + if (txt) { + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + fi->filePath().toAscii().data(), + txt); + return;//exit(1); + } + } + const LADSPA_Descriptor* descr; + for (int i = 0;; ++i) { + descr = ladspa(i); + if (descr == NULL) + break; + plugins.push_back(new LadspaPlugin(fi, ladspa, descr)); + } + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// loadPluginDir +//--------------------------------------------------------- + +static void loadPluginDir(const QString& s) + { + SS_TRACE_IN + QDir pluginDir(s, QString("*.so"), 0, QDir::Files); + if (pluginDir.exists()) { + QFileInfoList list = pluginDir.entryInfoList(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + QFileInfo fi = list.at(i); + loadPluginLib(&fi); + } + } + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// initPlugins +// search for LADSPA plugins +//--------------------------------------------------------- + +void SS_initPlugins() + { + SS_TRACE_IN + //loadPluginDir(museGlobalLib + QString("/plugins")); + + char* ladspaPath = getenv("LADSPA_PATH"); + if (ladspaPath == 0) + ladspaPath = "/usr/lib/ladspa:/usr/local/lib/ladspa"; + + char* p = ladspaPath; + while (*p != '\0') { + char* pe = p; + while (*pe != ':' && *pe != '\0') + pe++; + + int n = pe - p; + if (n) { + char* buffer = new char[n + 1]; + strncpy(buffer, p, n); + buffer[n] = '\0'; + loadPluginDir(QString(buffer)); + delete[] buffer; + } + p = pe; + if (*p == ':') + p++; + } + SS_TRACE_OUT + } + + +//--------------------------------------------------------- +// LadspaPlugin +//--------------------------------------------------------- + +LadspaPlugin::LadspaPlugin(const QFileInfo* f, + const LADSPA_Descriptor_Function ldf, + const LADSPA_Descriptor* d) + : Plugin(f), ladspa(ldf), plugin(d) + { + SS_TRACE_IN + _inports = 0; + _outports = 0; + _parameter = 0; + handle = 0; + active = false; + controls = 0; + inputs = 0; + outputs = 0; + + for (unsigned k = 0; k < plugin->PortCount; ++k) { + LADSPA_PortDescriptor pd = d->PortDescriptors[k]; + static const int CI = LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT; + if ((pd & CI) == CI) { + ++_parameter; + pIdx.push_back(k); + } + else if (pd & LADSPA_PORT_INPUT) { + ++_inports; + iIdx.push_back(k); + } + else if (pd & LADSPA_PORT_OUTPUT) { + ++_outports; + oIdx.push_back(k); + } + } + + /*if (SS_DEBUG_LADSPA) { + printf("Label: %s\tLib: %s\tPortCount: %d\n", this->label().toLatin1().data(), this->lib().toLatin1().data(), plugin->PortCount); + printf("LADSPA_PORT_CONTROL|LADSPA_PORT_INPUT: %d\t", pIdx.size()); + printf("Input ports: %d\t", iIdx.size()); + printf("Output ports: %d\n\n", oIdx.size()); + }*/ + + LADSPA_Properties properties = plugin->Properties; + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(properties); + if (_inports != _outports) + _inPlaceCapable = false; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// ~LadspaPlugin +//--------------------------------------------------------- +LadspaPlugin::~LadspaPlugin() + { + SS_TRACE_IN + if (active) { + stop(); + } + if (handle) { + SS_DBG_LADSPA2("Cleaning up ", this->label().toLatin1().data()); + plugin->cleanup(handle); + } + + //Free ports: + if (controls) + delete controls; + if (inputs) + delete inputs; + if (outputs) + delete outputs; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// instantiate +//--------------------------------------------------------- + +bool LadspaPlugin::instantiate() + { + bool success = false; + handle = plugin->instantiate(plugin, SS_samplerate); + success = (handle != NULL); + if (success) + SS_DBG_LADSPA2("Plugin instantiated", label().toLatin1().data()); + return success; + } + +//--------------------------------------------------------- +// start +// activate and connect control ports +//--------------------------------------------------------- + +bool LadspaPlugin::start() + { + SS_TRACE_IN + if (handle) { + if (plugin->activate) { + plugin->activate(handle); + SS_DBG_LADSPA("Plugin activated"); + } + active = true; + } + else { + SS_DBG_LADSPA("Error trying to activate plugin - plugin not instantiated!"); + SS_TRACE_OUT + return false; + } + + //Connect ports: + controls = new Port[_parameter]; + + for (int k = 0; k < _parameter; ++k) { + double val = defaultValue(k); + controls[k].val = val; + plugin->connect_port(handle, pIdx[k], &controls[k].val); + } + + outputs = new Port[_outports]; + inputs = new Port[_inports]; + + SS_TRACE_OUT + return true; + } + +//--------------------------------------------------------- +// stop +// deactivate +//--------------------------------------------------------- +void LadspaPlugin::stop() + { + SS_TRACE_IN + if (handle) { + SS_DBG_LADSPA2("Trying to stop plugin", label().toLatin1().data()); + if (plugin->deactivate) { + SS_DBG_LADSPA2("Deactivating ", label().toLatin1().data()); + plugin->deactivate(handle); + active = false; + } + } + else + SS_DBG_LADSPA("Warning - tried to stop plugin, but plugin was never started...\n"); + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// range +//--------------------------------------------------------- + +void LadspaPlugin::range(int i, float* min, float* max) const + { + SS_TRACE_IN + i = pIdx[i]; + LADSPA_PortRangeHint range = plugin->PortRangeHints[i]; + LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; + if (desc & LADSPA_HINT_TOGGLED) { + *min = 0.0; + *max = 1.0; + return; + } + float m = 1.0; + if (desc & LADSPA_HINT_SAMPLE_RATE) + m = (float) SS_samplerate; + + if (desc & LADSPA_HINT_BOUNDED_BELOW) + *min = range.LowerBound * m; + else + *min = 0.0; + if (desc & LADSPA_HINT_BOUNDED_ABOVE) + *max = range.UpperBound * m; + else + *max = 1.0; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// defaultValue +//--------------------------------------------------------- + +float LadspaPlugin::defaultValue(int k) const + { + SS_TRACE_IN + k = pIdx[k]; + LADSPA_PortRangeHint range = plugin->PortRangeHints[k]; + LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; + double val = 1.0; + if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) + val = range.LowerBound; + else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) + if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) + val = exp(fast_log10(range.LowerBound) * .75 + + log(range.UpperBound) * .25); + else + val = range.LowerBound*.75 + range.UpperBound*.25; + else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) + if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) + val = exp(log(range.LowerBound) * .5 + + log(range.UpperBound) * .5); + else + val = range.LowerBound*.5 + range.UpperBound*.5; + else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) + if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) + val = exp(log(range.LowerBound) * .25 + + log(range.UpperBound) * .75); + else + val = range.LowerBound*.25 + range.UpperBound*.75; + else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) + val = range.UpperBound; + else if (LADSPA_IS_HINT_DEFAULT_0(rh)) + val = 0.0; + else if (LADSPA_IS_HINT_DEFAULT_1(rh)) + val = 1.0; + else if (LADSPA_IS_HINT_DEFAULT_100(rh)) + val = 100.0; + else if (LADSPA_IS_HINT_DEFAULT_440(rh)) + val = 440.0; + SS_TRACE_OUT + return val; + } + +//--------------------------------------------------------- +// find +//--------------------------------------------------------- + +Plugin* PluginList::find(const QString& file, const QString& name) + { + SS_TRACE_IN + for (iPlugin i = begin(); i != end(); ++i) { + if ((file == (*i)->lib()) && (name == (*i)->label())) { + SS_TRACE_OUT + return *i; + } + } + printf("Plugin <%s> not found\n", name.toLatin1().data()); + SS_TRACE_OUT + return 0; + } + +//--------------------------------------------------------- +// connectInport +//--------------------------------------------------------- +void LadspaPlugin::connectInport(int k, LADSPA_Data* datalocation) + { + SS_TRACE_IN + plugin->connect_port(handle, iIdx[k], datalocation); + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// connectOutport +//--------------------------------------------------------- +void LadspaPlugin::connectOutport(int k, LADSPA_Data* datalocation) + { + SS_TRACE_IN + plugin->connect_port(handle, oIdx[k], datalocation); + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// process +//--------------------------------------------------------- +void LadspaPlugin::process(unsigned long frames) + { + plugin->run(handle, frames); + } + +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void LadspaPlugin::setParam(int k, float val) + { + SS_TRACE_IN + controls[k].val = val; + SS_TRACE_OUT + } + +//--------------------------------------------------------- +// getGuiControlValue +// scale control value to gui-slider/checkbox representation +//--------------------------------------------------------- + +int LadspaPlugin::getGuiControlValue(int param) const + { + SS_TRACE_IN + float val = getControlValue(param); + float min, max; + range(param, &min, &max); + int intval; + if (isLog(param)) { + intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); + } + else if (isBool(param)) { + intval = (int) val; + } + else { + float scale = SS_PLUGIN_PARAM_MAX / (max - min); + intval = (int) ((val - min) * scale); + } + SS_TRACE_OUT + return intval; + } + +//--------------------------------------------------------- +// convertGuiControlValue +// scale control value to gui-slider/checkbox representation +//--------------------------------------------------------- + +float LadspaPlugin::convertGuiControlValue(int parameter, int val) const + { + SS_TRACE_IN + float floatval = 0; + float min, max; + range(parameter, &min, &max); + + if (isLog(parameter)) { + if (val > 0) { + float logged = SS_map_pluginparam2logdomain(val); + float e = expf(logged) * (max - min); + e+=min; + floatval = e; + } + } + else if (isBool(parameter)) { + floatval = (float) val; + } + else if (isInt(parameter)) { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (float) round((((float) val) * scale) + min); + } + else { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (((float) val) * scale) + min; + } + SS_TRACE_OUT + return floatval; + } diff --git a/muse2/synti/simpledrums2/ssplugin.h b/muse2/synti/simpledrums2/ssplugin.h new file mode 100644 index 00000000..64e80921 --- /dev/null +++ b/muse2/synti/simpledrums2/ssplugin.h @@ -0,0 +1,173 @@ +// +// C++ Interface: plugin +// +// Description: +// +// +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// Additions/modifications: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#include <QFileInfo> + +#include <ladspa.h> +#include <math.h> + +//--------------------------------------------------------- +// Port +//--------------------------------------------------------- + +struct Port { + float val; + }; + +//--------------------------------------------------------- +// Plugin +//--------------------------------------------------------- + +class Plugin + { + protected: + QFileInfo fi; + + public: + Plugin(const QFileInfo* f); + virtual ~Plugin() {} + virtual QString label() const { return QString(); } + virtual QString name() const { return QString(); } + virtual unsigned long id() const { return 0; } + virtual QString maker() const { return QString(); } + virtual QString copyright() const { return QString(); } + virtual int parameter() const { return 0; } + virtual int inports() const { return 0; } + virtual int outports() const { return 0; } + virtual bool inPlaceCapable() const { return false; } + + virtual bool isLog(int) const { return false; } + virtual bool isBool(int) const { return false; } + virtual bool isInt(int) const { return false; } + virtual float defaultValue(int) const { return 0.0f; } + virtual void range(int, float* min, float* max) const { + *min = 0.0f; + *max = 1.0f; + } + virtual const char* getParameterName(int /*param*/) const { return ""; } + QString lib() const { return fi.baseName(); } + QString path() const { return fi.absolutePath(); } + }; + +//--------------------------------------------------------- +// LadspaPlugin +//--------------------------------------------------------- + +class LadspaPlugin : public Plugin + { + LADSPA_Descriptor_Function ladspa; + const LADSPA_Descriptor* plugin; + LADSPA_Handle handle; + bool active; + + Port* controls; + Port* inputs; + Port* outputs; + + protected: + int _parameter; + std::vector<int> pIdx; //control port numbers + + int _inports; + std::vector<int> iIdx; //input port numbers + + int _outports; + std::vector<int> oIdx; //output port numbers + + bool _inPlaceCapable; + + public: + LadspaPlugin(const QFileInfo* f, const LADSPA_Descriptor_Function, const LADSPA_Descriptor* d); + virtual ~LadspaPlugin(); + virtual QString label() const { return QString(plugin->Label); } + virtual QString name() const { return QString(plugin->Name); } + virtual unsigned long id() const { return plugin->UniqueID; } + virtual QString maker() const { return QString(plugin->Maker); } + virtual QString copyright() const { return QString(plugin->Copyright); } + virtual int parameter() const { return _parameter; } + virtual int inports() const { return _inports; } + virtual int outports() const { return _outports; } + virtual bool inPlaceCapable() const { return _inPlaceCapable; } + const LADSPA_Descriptor* ladspaDescriptor() const { return plugin; } + virtual bool isLog(int k) const { + LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; + return LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor); + } + virtual bool isBool(int k) const { + return LADSPA_IS_HINT_TOGGLED(plugin->PortRangeHints[pIdx[k]].HintDescriptor); + } + virtual bool isInt(int k) const { + LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; + return LADSPA_IS_HINT_INTEGER(r.HintDescriptor); + } + virtual void range(int i, float*, float*) const; + virtual const char* getParameterName(int i) const { + return plugin->PortNames[pIdx[i]]; + } + virtual float defaultValue(int) const; + virtual float getControlValue(int k) const { + return controls[k].val; + } + + int getGuiControlValue(int parameter) const; + float convertGuiControlValue(int parameter, int val) const; + + bool instantiate(); + bool start(); + void stop(); + void connectInport(int k, LADSPA_Data* datalocation); + void connectOutport(int k, LADSPA_Data* datalocation); + void process(unsigned long); + void setParam(int i, float val); + + }; + + +static inline float fast_log2 (float val) + { + /* don't use reinterpret_cast<> because that prevents this + from being used by pure C code (for example, GnomeCanvasItems) + */ + int* const exp_ptr = (int *)(&val); + int x = *exp_ptr; + const int log_2 = ((x >> 23) & 255) - 128; + x &= ~(255 << 23); + x += 127 << 23; + *exp_ptr = x; + val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) + return (val + log_2); + } + +static inline float fast_log10 (const float val) + { + return fast_log2(val) / 3.312500f; + } + +//--------------------------------------------------------- +// PluginList +//--------------------------------------------------------- + +typedef std::list<Plugin*>::iterator iPlugin; + +class PluginList : public std::list<Plugin*> { + public: + Plugin* find(const QString& file, const QString& name); + PluginList() {} + }; + +extern void SS_initPlugins(); +extern PluginList plugins; + +#endif diff --git a/muse2/synti/simpledrums2/sspluginchooserbase.ui b/muse2/synti/simpledrums2/sspluginchooserbase.ui new file mode 100644 index 00000000..63d254df --- /dev/null +++ b/muse2/synti/simpledrums2/sspluginchooserbase.ui @@ -0,0 +1,106 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>SS_PluginChooserBase</class> + <widget class="QDialog" name="SS_PluginChooserBase" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>777</width> + <height>681</height> + </rect> + </property> + <property name="windowTitle" > + <string>SimpleDrums - Ladspa Plugin Chooser</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>10</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QTreeWidget" name="effectsListView" > + <column> + <property name="text" > + <string>Name</string> + </property> + </column> + <column> + <property name="text" > + <string>Label</string> + </property> + </column> + <column> + <property name="text" > + <string>Inports</string> + </property> + </column> + <column> + <property name="text" > + <string>Outports</string> + </property> + </column> + <column> + <property name="text" > + <string>Creator</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>301</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>&Cancel</string> + </property> + <property name="shortcut" > + <string>Alt+C</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>&OK</string> + </property> + <property name="shortcut" > + <string>Alt+O</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction>qPixmapFromMimeSource</pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/muse2/synti/simpledrums2/ssplugingui.cpp b/muse2/synti/simpledrums2/ssplugingui.cpp new file mode 100644 index 00000000..0428988b --- /dev/null +++ b/muse2/synti/simpledrums2/ssplugingui.cpp @@ -0,0 +1,530 @@ +// +// C++ Implementation: ssplugingui +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "ssplugingui.h" +#include "ssplugin.h" +#include "simpledrumsgui.h" + +#define SS_PLUGINGUI_XOFF 300 +#define SS_PLUGINGUI_YOFF 300 +#define SS_PLUGINGUI_WIDTH 450 +#define SS_PLUGINGUI_MAX_WIDTH 700 + +#define SS_PLUGINFRONT_MINWIDTH SS_PLUGINGUI_WIDTH +#define SS_PLUGINFRONT_MINHEIGHT 70 +#define SS_PLUGINFRONT_MARGIN 9 +#define SS_PLUGINFRONT_INC_PARAM 30 +#define SS_PLUGINFRONT_INC_PARAM_MIN 60 +#define SS_PLUGINGUI_HEIGHT (SS_NR_OF_SENDEFFECTS * SS_PLUGINFRONT_MINHEIGHT) + +#define SS_PLUGINCHOOSER_NAMECOL 0 +#define SS_PLUGINCHOOSER_LABELCOL 1 +#define SS_PLUGINCHOOSER_INPORTSCOL 2 +#define SS_PLUGINCHOOSER_OUTPORTSCOL 3 +#define SS_PLUGINCHOOSER_CREATORCOL 4 + + +/*! + \fn SS_PluginChooser::SS_PluginChooser(QWidget* parent, const char* name = 0) + */ +SS_PluginChooser::SS_PluginChooser(QWidget* parent) + :QDialog(parent) + { + SS_TRACE_IN + setupUi(this); + selectedPlugin = 0; + + for (iPlugin i=plugins.begin(); i !=plugins.end(); i++) { + //Support for only 2 or 1 inport/outports + if ( ((*i)->outports() == 2 || (*i)->outports() == 1) && ((*i)->inports() == 2 || (*i)->inports() == 1) ) { + QTreeWidgetItem* tmpItem = new QTreeWidgetItem(effectsListView); + tmpItem->setText(SS_PLUGINCHOOSER_NAMECOL, (*i)->name()); + tmpItem->setText(SS_PLUGINCHOOSER_LABELCOL, (*i)->label()); + tmpItem->setText(SS_PLUGINCHOOSER_INPORTSCOL, QString::number((*i)->inports())); + tmpItem->setText(SS_PLUGINCHOOSER_OUTPORTSCOL, QString::number((*i)->outports())); + tmpItem->setText(SS_PLUGINCHOOSER_CREATORCOL, (*i)->maker()); + effectsListView->addTopLevelItem(tmpItem); + } + } + connect(okButton, SIGNAL(pressed()), SLOT(okPressed())); + connect(cancelButton, SIGNAL(pressed()), SLOT(cancelPressed())); + connect(effectsListView, SIGNAL(selectionChanged(QTreeWidgetItem*)), SLOT(selectionChanged(QTreeWidgetItem*))); + connect(effectsListView, SIGNAL(doubleClicked(QTreeWidgetItem*)), SLOT(doubleClicked(QTreeWidgetItem*))); + SS_TRACE_OUT + } + +/*! + \fn SS_PluginChooser::selectionChanged(QListViewItem* item) + */ +void SS_PluginChooser::selectionChanged(QTreeWidgetItem* item) + { + SS_TRACE_IN + selectedItem = item; + SS_TRACE_OUT + } + +/*! + \fn SS_PluginChooser::okPressed() + */ +void SS_PluginChooser::okPressed() + { + SS_TRACE_IN + selectedPlugin = findSelectedPlugin(); + done(QDialog::Accepted); + SS_TRACE_OUT + } + +/*! + \fn SS_PluginChooser::cancelPressed() + */ +void SS_PluginChooser::cancelPressed() + { + SS_TRACE_IN + SS_TRACE_OUT + done(QDialog::Rejected); + } + +/*! + \fn SS_PluginChooser::doubleClicked(QListViewItem* item) + */ +void SS_PluginChooser::doubleClicked(QTreeWidgetItem* /*item*/) + { + SS_TRACE_IN + selectedPlugin = findSelectedPlugin(); + SS_TRACE_OUT + done(QDialog::Accepted); + } + +/*! + \fn SS_PluginChooser::getSelectedPlugin() + */ +LadspaPlugin* SS_PluginChooser::findSelectedPlugin() + { + SS_TRACE_IN + LadspaPlugin* selected = 0; + for (iPlugin i=plugins.begin(); i != plugins.end(); i++) { + if ((*i)->name() == selectedItem->text(SS_PLUGINCHOOSER_NAMECOL)) + selected = (LadspaPlugin*) (*i); + } + SS_TRACE_OUT + return selected; + } + +/*! + \fn SS_PluginFront::SS_PluginFront(QWidget* parent, const char* name = 0) + */ +SS_PluginFront::SS_PluginFront(QWidget* parent, int in_fxid) + : QGroupBox(parent), fxid (in_fxid) + { + SS_TRACE_IN + expanded = false; + pluginChooser = 0; + plugin = 0; + expGroup = 0; + +//TD setLineWidth(3); + setFlat(false); +//TD setFrameStyle( Q3Frame::Box | Q3Frame::Raised ); +//TD setFrameShape(QFrame::StyledPanel); +// setFrameShadow(Qt::Sunken); + setFocusPolicy(Qt::NoFocus); + setMinimumSize(SS_PLUGINFRONT_MINWIDTH, SS_PLUGINFRONT_MINHEIGHT); + setMaximumSize(SS_PLUGINGUI_MAX_WIDTH, SS_PLUGINFRONT_MINHEIGHT); + + QVBoxLayout* bigLayout = new QVBoxLayout(this); + bigLayout->setMargin(SS_PLUGINFRONT_MARGIN); + bigLayout->setAlignment(Qt::AlignTop); +//TODO bigLayout->setResizeMode(QLayout::SetNoConstraint); + + layout = new QHBoxLayout; + bigLayout->addLayout(layout); + layout->setAlignment(Qt::AlignVCenter); +//TODO layout->setResizeMode(QLayout::SetNoConstraint); + + + QVBoxLayout* onOffLayout = new QVBoxLayout; + layout->addLayout(onOffLayout); + onOffLayout->setMargin(SS_PLUGINFRONT_MARGIN); + onOff = new QCheckBox(this); + onOffLayout->addWidget(new QLabel("On/Off", this)); + onOffLayout->addWidget(onOff); + connect(onOff, SIGNAL(toggled(bool)), SLOT(onOffToggled(bool))); + + pluginName = new QLineEdit(this); + pluginName->setReadOnly(true); + layout->addWidget(pluginName); + + loadFxButton = new QPushButton("L", this); + QRect r = loadFxButton->geometry(); + loadFxButton->setGeometry(r.x(), r.y(), 20, pluginName->geometry().height()); + loadFxButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + loadFxButton->setMinimumSize(20,pluginName->geometry().height()); + loadFxButton->setMaximumSize(30,pluginName->geometry().height()); + connect(loadFxButton, SIGNAL(clicked()), SLOT(loadButton())); + layout->addWidget(loadFxButton); + + clearFxButton = new QPushButton("C", this); + r = clearFxButton->geometry(); + clearFxButton->setGeometry(r.x(), r.y(), 20, pluginName->geometry().height()); + clearFxButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + clearFxButton->setMinimumSize(20,pluginName->geometry().height()); + clearFxButton->setMaximumSize(30,pluginName->geometry().height()); + connect(clearFxButton, SIGNAL(clicked()), SLOT(clearButtonPressed())); + layout->addWidget(clearFxButton); + + layout->addSpacing(5); + + expandButton = new QPushButton("->", this); + r = loadFxButton->geometry(); + expandButton->setGeometry(r.x(), r.y(), 20, pluginName->geometry().height()); + expandButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + expandButton->setMinimumSize(20,pluginName->geometry().height()); + expandButton->setMaximumSize(30,pluginName->geometry().height()); + connect(expandButton, SIGNAL(clicked()), SLOT(expandButtonPressed())); + layout->addWidget(expandButton); + + layout->addSpacing(5); + + QVBoxLayout* gainSliderLayout = new QVBoxLayout; + layout->addLayout(gainSliderLayout); + gainSliderLayout->addWidget(new QLabel("Return level", this)); + gainSliderLayout->setMargin(SS_PLUGINFRONT_MARGIN); + outGainSlider = new QSlider(Qt::Horizontal, this); + outGainSlider->setMinimumSize(100, pluginName->geometry().height()); + outGainSlider->setMaximumSize(500, pluginName->geometry().height()); + loadFxButton->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + outGainSlider->setRange(0, 127); + outGainSlider->setValue(75); + connect(outGainSlider, SIGNAL(valueChanged(int)), SLOT(returnSliderMoved(int))); + gainSliderLayout->addWidget(outGainSlider); + clearPluginDisplay(); + + expLayout = new QVBoxLayout; // (bigLayout, 2); + bigLayout->addLayout(expLayout); + + clearFxButton->setToolTip(tr("Clear and unload effect")); + loadFxButton->setToolTip(tr("Load effect")); + expandButton->setToolTip(tr("Toggle display of effect parameters")); + onOff->setToolTip(tr("Turn effect on/off")); + SS_TRACE_OUT + } + +SS_PluginFront::~SS_PluginFront() + { + if (pluginChooser) + delete pluginChooser; + } + +/*! + \fn SS_PluginFront::clearPluginDisplay() + */ +void SS_PluginFront::clearPluginDisplay() + { + SS_TRACE_IN + if (expanded) + expandButtonPressed(); + + pluginName->setText("No plugin loaded"); + pluginName->setEnabled(false); + onOff->setEnabled(false); + onOff->blockSignals(true); + onOff->setChecked(false); + onOff->blockSignals(false); + + clearFxButton->setEnabled(false); + expandButton->setEnabled(false); + outGainSlider->setEnabled(false); + SS_TRACE_OUT + } + +/*! + \fn SS_PluginFront::setPluginName(QString name) + */ +void SS_PluginFront::setPluginName(QString name) + { + pluginName->setText(name); + } + + +/*! + \fn SS_PluginFront::loadButton() + */ +void SS_PluginFront::loadButton() + { + SS_TRACE_IN + if (!pluginChooser) + pluginChooser = new SS_PluginChooser(this); + + pluginChooser->exec(); + if ((pluginChooser->result() == QDialog::Accepted) && pluginChooser->getSelectedPlugin()) { + Plugin* p = pluginChooser->getSelectedPlugin(); + //printf("Selected plugin: %s\n", pluginChooser->getSelectedPlugin()->name().toLatin1().data()); + emit loadPlugin(fxid, p->lib(), p->label()); + } + SS_TRACE_OUT + } + +/*! + \fn SS_PluginFront::returnSliderMoved(int val) + */ +void SS_PluginFront::returnSliderMoved(int val) + { + emit returnLevelChanged(fxid, val); + } + + +/*! + \fn SS_PluginFront::updatePluginValue(unsigned i) + */ +void SS_PluginFront::updatePluginValue(unsigned k) + { + SS_TRACE_IN + // If parameters are shown - close them + if (expanded) { + expandButtonPressed(); + } + + unsigned j=0; + if (k > plugins.size()) { + fprintf(stderr, "Internal error, tried to update plugin w range outside of list\n"); + return; + } + + iPlugin i; + for (i = plugins.begin(); j != k; i++, j++); + plugin = (LadspaPlugin*) *(i); + setPluginName(plugin->label()); + outGainSlider->setEnabled(true); + clearFxButton->setEnabled(true); + expandButton->setEnabled(true); + pluginName->setEnabled(true); + onOff->setEnabled(true); + onOff->setChecked(true); + SS_TRACE_OUT + } + +/*! + \fn SS_PluginFront::onOffToggled(bool state) + */ +void SS_PluginFront::onOffToggled(bool state) + { + emit fxToggled(fxid, state); + } + +/*! + \fn SS_PluginFront::sizeHint() const + */ +QSize SS_PluginFront::sizeHint() const + { + return QSize(SS_PLUGINFRONT_MINWIDTH, 50); + } + +/*! + \fn SS_PluginFront::sizePolicy() const + */ +QSizePolicy SS_PluginFront::sizePolicy() const + { + return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + } + + +/*! + \fn SS_PluginFront::clearButtonPressed() + */ +void SS_PluginFront::clearButtonPressed() + { + // If parameters are shown - close them + if (expanded) { + expandButtonPressed(); + } + emit clearPlugin(fxid); + } + +/*! + \fn SS_PluginFront::setRetGain(int val) + */ +void SS_PluginFront::setRetGain(int val) + { + outGainSlider->blockSignals(true); + outGainSlider->setValue(val); + outGainSlider->blockSignals(false); + } + +/*! + \fn SS_PluginFront::expandButtonPressed() + */ +void SS_PluginFront::expandButtonPressed() + { + SS_TRACE_IN + int sizeIncrease = 0; + QRect pf = geometry(); + + if (!expanded) { + plugin->parameter() == 1 ? sizeIncrease = SS_PLUGINFRONT_INC_PARAM_MIN : sizeIncrease = plugin->parameter() * SS_PLUGINFRONT_INC_PARAM; + pf.setHeight(pf.height() + sizeIncrease); + setMinimumSize(QSize(pf.width(), pf.height())); + setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, pf.height())); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + setGeometry(pf); + emit sizeChanged(fxid, sizeIncrease); + + expanded = true; + expandButton->setText("<-"); + createPluginParameters(); + } + else { +//TODO expLayout->remove(expGroup); + expGroup->hide(); + expGroup->deleteLater(); + paramWidgets.clear(); + expGroup = 0; + plugin->parameter() == 1 ? sizeIncrease = (0-SS_PLUGINFRONT_INC_PARAM_MIN) : sizeIncrease = 0 - (plugin->parameter() * SS_PLUGINFRONT_INC_PARAM); + expandButton->setText("->"); + expanded = false; + pf.setHeight(pf.height() + sizeIncrease); + pf.setTop(pf.top() + sizeIncrease); + pf.setBottom(pf.bottom() + sizeIncrease); + setGeometry(pf); + adjustSize(); + layout->activate(); + setMinimumSize(QSize(pf.width(), pf.height())); + setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, pf.height())); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + emit sizeChanged(fxid, sizeIncrease); + } + SS_TRACE_OUT + } + +/*! + \fn SS_PluginFront::createPluginParameters() + */ +void SS_PluginFront::createPluginParameters() + { + SS_TRACE_IN + expGroup = new QGroupBox(this); + + expGroup->setMinimumSize(QSize(50, 50)); + expGroup->setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, (plugin->parameter() * SS_PLUGINFRONT_INC_PARAM - SS_PLUGINFRONT_MARGIN))); + expGroup->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + expLayout->addWidget(expGroup); + expGroup->show(); + QVBoxLayout* expGroupLayout = new QVBoxLayout(expGroup); // , 1); + expGroupLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); +//TD expGroupLayout->setResizeMode(QLayout::FreeResize); + expGroupLayout->setMargin(SS_PLUGINFRONT_MARGIN); + + for (int i=0; i < plugin->parameter(); i++) { + QHBoxLayout* paramStrip = new QHBoxLayout; // (expGroupLayout, 3); + expGroupLayout->addLayout(paramStrip); + paramStrip->setAlignment(Qt::AlignLeft); + QLabel* paramName = new QLabel(plugin->getParameterName(i), expGroup); + paramName->show(); + paramName->setMinimumSize(QSize(150, 10)); + paramName->setMaximumSize(QSize(300, SS_PLUGINFRONT_INC_PARAM)); + paramName->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + + paramStrip->addWidget(paramName); + + if (plugin->isBool(i)) { + SS_ParameterCheckBox* paramCheckBox = new SS_ParameterCheckBox(expGroup, plugin, fxid, i); + paramCheckBox->setEnabled(true); + paramCheckBox->setParamValue((int) plugin->getControlValue(i)); + paramCheckBox->show(); + paramStrip->addWidget(paramCheckBox); + connect(paramCheckBox, SIGNAL(valueChanged(int, int, int)), SLOT(parameterValueChanged(int, int, int))); + } + else { + SS_ParameterSlider* paramSlider = new SS_ParameterSlider(expGroup, plugin, fxid, i); + paramSlider->setEnabled(true); + paramSlider->show(); + paramSlider->setRange(SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX); + + float max, min; + plugin->range(i, &min, &max); + //int intval = 0; + paramSlider->setParamValue(plugin->getGuiControlValue(i)); + connect(paramSlider, SIGNAL(valueChanged(int, int, int)), SLOT(parameterValueChanged(int, int, int))); + paramStrip->addWidget(paramSlider); + } + } + expLayout->activate(); + SS_TRACE_OUT + } + +/*! + \fn SS_PluginFront::parameterValueChanged(int fxid, int parameter, int val) + */ +void SS_PluginFront::parameterValueChanged(int fxid, int parameter, int val) + { + emit effectParameterChanged(fxid, parameter, val); + } + +/*! + \fn SS_PluginFront::setParameterValue(int param, float val) + */ +void SS_PluginFront::setParameterValue(int param, int val) + { + SS_TRACE_IN + int j=0; + for (SS_iParameterWidgetList i=paramWidgets.begin(); i != paramWidgets.end(); i++, j++) { + if (j == param) { + (*i)->setParamValue(val); + } + } + SS_TRACE_OUT + } + +SS_PluginGui::SS_PluginGui(QWidget* parent) + : QDialog(parent, false) + { + setWindowTitle("SimpleDrums LADSPA sendeffects"); + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + pluginFronts[i] = 0; + } + layout = new QVBoxLayout(this); + + for (int i=0; i<SS_NR_OF_SENDEFFECTS; i++) { + pluginFronts[i] = new SS_PluginFront(this, i); + pluginFronts[i]->update(); + layout->addWidget(pluginFronts[i]); + connect(pluginFronts[i], SIGNAL(loadPlugin(int, QString, QString)), simplesynthgui_ptr, SLOT(loadEffectInvoked(int, QString, QString))); + connect(pluginFronts[i], SIGNAL(returnLevelChanged(int, int)), simplesynthgui_ptr, SLOT(returnLevelChanged(int, int))); + connect(pluginFronts[i], SIGNAL(fxToggled(int, int)), simplesynthgui_ptr, SLOT(toggleEffectOnOff(int, int))); + connect(pluginFronts[i], SIGNAL(clearPlugin(int)), simplesynthgui_ptr, SLOT(clearPlugin(int))); + connect(pluginFronts[i], SIGNAL(sizeChanged(int, int)), SLOT(pluginFrontSizeChanged(int, int))); + connect(pluginFronts[i], SIGNAL(effectParameterChanged(int, int, int)), simplesynthgui_ptr, SLOT(effectParameterChanged(int, int, int))); + } + setMinimumSize(QSize(SS_PLUGINGUI_WIDTH, geometry().height())); + setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, geometry().height())); + } + + +/*! + \fn SS_PluginGui::pluginFrontSizeChanged(int fxid, int val) + */ +void SS_PluginGui::pluginFrontSizeChanged(int /*fxid*/, int val) + { + QRect r = geometry(); + r.setHeight(r.height() + val); + setMinimumSize(QSize(SS_PLUGINGUI_WIDTH, r.height())); + setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, r.height())); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + setGeometry(r); + adjustSize(); + } + +SS_PluginFront* SS_PluginGui::getPluginFront(unsigned i) + { + SS_TRACE_IN + if (i<SS_NR_OF_SENDEFFECTS) + SS_TRACE_OUT + return pluginFronts[i]; + } diff --git a/muse2/synti/simpledrums2/ssplugingui.h b/muse2/synti/simpledrums2/ssplugingui.h new file mode 100644 index 00000000..3d77ecf0 --- /dev/null +++ b/muse2/synti/simpledrums2/ssplugingui.h @@ -0,0 +1,204 @@ +// +// C++ Interface: ssplugingui +// +// Description: +// +// +// Author: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef __SS_PLUGINGUI_H__ +#define __SS_PLUGINGUI_H__ +#include <QDialog> +#include <QSlider> +#include <QButtonGroup> +#include <QtGui> +//#include <QHBoxLayout> +//#include <QVBoxLayout> + +#include "ui_sspluginchooserbase.h" +#include "common.h" +#include "ssplugin.h" + +class SS_ParameterWidget + { + protected: + int fxid; + int parameter; + + LadspaPlugin* plugin; + + public: + SS_ParameterWidget() { } + virtual ~SS_ParameterWidget() { } + int getFxId() { SS_TRACE_IN SS_TRACE_OUT return fxid; } + bool isBool() { SS_TRACE_IN SS_TRACE_OUT return plugin->isBool(parameter); } + bool isLog() { SS_TRACE_IN SS_TRACE_OUT return plugin->isLog(parameter); } + bool isInt() { SS_TRACE_IN SS_TRACE_OUT return plugin->isInt(parameter); } + virtual void setParamValue(int) { printf("Virtual function - should not be called!"); }; + }; + +class SS_ParameterCheckBox : public QCheckBox, public SS_ParameterWidget + { + Q_OBJECT + + public: + SS_ParameterCheckBox(QWidget* parent, LadspaPlugin* in_plugin, int in_id, int in_parameter) + : QCheckBox(parent) , SS_ParameterWidget() + { + SS_TRACE_IN + plugin = in_plugin; + fxid = in_id; + parameter = in_parameter; + connect(this, SIGNAL(clicked()), SLOT(isClicked())); + SS_TRACE_OUT + } + + virtual void setParamValue(int val) { SS_TRACE_IN setChecked(val); SS_TRACE_OUT} + + private slots: + void isClicked() { SS_TRACE_IN emit valueChanged(fxid, parameter, (int)this->isChecked()); SS_TRACE_OUT} + + signals: + void valueChanged(int id, int param, int val); + }; + +class SS_ParameterSlider : public QSlider, public SS_ParameterWidget + { + Q_OBJECT + + public: + SS_ParameterSlider(QWidget* parent, LadspaPlugin* in_plugin, int in_id, int in_parameter) + : QSlider(Qt::Horizontal, parent), SS_ParameterWidget() + { + SS_TRACE_IN + plugin = in_plugin; + fxid = in_id; + parameter = in_parameter; + SS_TRACE_OUT + } + + virtual void setParamValue(int val) { SS_TRACE_IN setValue(val); SS_TRACE_OUT} + + public slots: + virtual void setValue(int val) { SS_TRACE_IN QSlider::setValue(val); emit valueChanged(fxid, parameter, val); SS_TRACE_OUT } + + signals: + void valueChanged(int id, int param, int val); + }; + +typedef std::list<SS_ParameterWidget*> SS_ParameterWidgetList; +typedef std::list<SS_ParameterWidget*>::iterator SS_iParameterWidgetList ; + +//------------------------------- +// SS_PluginChooser +//------------------------------- +class SS_PluginChooser : public QDialog, Ui::SS_PluginChooserBase +{ + Q_OBJECT + private: + LadspaPlugin* selectedPlugin; + protected: + + public: + SS_PluginChooser(QWidget* parent); + LadspaPlugin* getSelectedPlugin() { SS_TRACE_IN SS_TRACE_OUT return selectedPlugin; } + + private slots: + void okPressed(); + void cancelPressed(); + void selectionChanged(QTreeWidgetItem* item); + void doubleClicked(QTreeWidgetItem* item); + + private: + QTreeWidgetItem* selectedItem; + LadspaPlugin* findSelectedPlugin(); + +}; + +//------------------------------- +// SS_PluginGuiFront +//------------------------------- +class SS_PluginFront : public QGroupBox + { + Q_OBJECT + private: + QHBoxLayout* layout; + QVBoxLayout* expLayout; + QLineEdit* pluginName; + QCheckBox* onOff; + QPushButton* loadFxButton; + QPushButton* clearFxButton; + QPushButton* expandButton; + QSlider* outGainSlider; + SS_PluginChooser* pluginChooser; + LadspaPlugin* plugin; + QGroupBox* expGroup; + + int fxid; + bool expanded; + + //For effect parameters: + SS_ParameterWidgetList paramWidgets; + + protected: + + public: + SS_PluginFront(QWidget* parent, int id); + void setPluginName(QString name); + ~SS_PluginFront(); + void updatePluginValue(unsigned i); + void clearPluginDisplay(); + void setParameterValue(int param, int val); + void setRetGain(int val); + + protected: + virtual QSize sizeHint() const; + virtual QSizePolicy sizePolicy() const; + + private slots: + void loadButton(); + void returnSliderMoved(int val); + void onOffToggled(bool state); + void clearButtonPressed(); + void expandButtonPressed(); + void parameterValueChanged(int fxid, int parameter, int val); + + signals: + void loadPlugin(int fxid, QString lib, QString name); + void returnLevelChanged(int fxid, int val); + void fxToggled(int fxid, int state); + void clearPlugin(int fxid); + void sizeChanged(int fxid, int val); + void effectParameterChanged(int fxid, int param, int val); + + private: + void createPluginParameters(); + }; + + +//------------------------------- +// SS_PluginGui +// Main plugin class, dialog +//------------------------------- +class SS_PluginGui : public QDialog + { + Q_OBJECT + private: + QVBoxLayout* layout; + SS_PluginFront* pluginFronts[4]; + + public: + SS_PluginGui(QWidget* parent); + SS_PluginFront* getPluginFront(unsigned i); + ~SS_PluginGui() {} +private slots: + void pluginFrontSizeChanged(int fxid, int val); + }; + + +#endif + diff --git a/muse2/synti/vam/CMakeLists.txt b/muse2/synti/vam/CMakeLists.txt index b93a5c9d..d146b2f9 100644 --- a/muse2/synti/vam/CMakeLists.txt +++ b/muse2/synti/vam/CMakeLists.txt @@ -38,7 +38,7 @@ add_library ( vam SHARED # set_target_properties ( vam PROPERTIES PREFIX "" - COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + COMPILE_FLAGS "-fvisibility=hidden -include ${PROJECT_BINARY_DIR}/all-pic.h" ) target_link_libraries(vam diff --git a/muse2/synti/vam/vam.cpp b/muse2/synti/vam/vam.cpp index 95b80c13..9d0d6e30 100644 --- a/muse2/synti/vam/vam.cpp +++ b/muse2/synti/vam/vam.cpp @@ -1044,7 +1044,12 @@ extern "C" { MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; - + // We must compile with -fvisibility=hidden to avoid namespace + // conflicts with global variables. + // Only visible symbol is "mess_descriptor". + // (TODO: all plugins should be compiled this way) + + __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } |