Imported Upstream version 0.11.0~dfsg1
authorJaromír Mikeš <mira.mikes@seznam.cz>
Wed, 2 Dec 2015 08:47:59 +0000 (09:47 +0100)
committerJaromír Mikeš <mira.mikes@seznam.cz>
Wed, 2 Dec 2015 08:47:59 +0000 (09:47 +0100)
56 files changed:
.travis.yml
ChangeLog
Makefile.am
configure.ac
src/core/channel.cpp
src/core/channel.h
src/core/conf.cpp
src/core/conf.h
src/core/const.h
src/core/dataStorage.cpp [deleted file]
src/core/dataStorage.h [deleted file]
src/core/dataStorageIni.cpp [new file with mode: 0644]
src/core/dataStorageIni.h [new file with mode: 0644]
src/core/dataStorageJson.cpp [new file with mode: 0644]
src/core/dataStorageJson.h [new file with mode: 0644]
src/core/init.cpp
src/core/midiChannel.cpp
src/core/midiChannel.h
src/core/midiMapConf.h
src/core/mixer.cpp
src/core/mixerHandler.cpp
src/core/mixerHandler.h
src/core/patch.cpp
src/core/patch.h
src/core/patch_DEPR_.cpp [new file with mode: 0644]
src/core/patch_DEPR_.h [new file with mode: 0644]
src/core/pluginHost.cpp
src/core/pluginHost.h
src/core/recorder.cpp
src/core/sampleChannel.cpp
src/core/sampleChannel.h
src/glue/glue.cpp
src/glue/glue.h
src/glue/storage.cpp [new file with mode: 0644]
src/glue/storage.h [new file with mode: 0644]
src/gui/dialogs/gd_about.cpp
src/gui/dialogs/gd_beatsInput.cpp
src/gui/dialogs/gd_browser.cpp
src/gui/dialogs/gd_config.cpp
src/gui/dialogs/gd_mainWindow.cpp
src/gui/elems/ge_channel.cpp
src/gui/elems/ge_channelButton.cpp
src/gui/elems/ge_channelButton.h
src/gui/elems/ge_column.cpp
src/gui/elems/ge_column.h
src/gui/elems/ge_keyboard.cpp
src/gui/elems/ge_keyboard.h
src/gui/elems/ge_midiChannel.cpp
src/gui/elems/ge_sampleChannel.cpp
src/gui/elems/ge_sampleChannel.h
src/main.cpp
src/utils/gui_utils.cpp
src/utils/gvector.h [new file with mode: 0644]
src/utils/utils.cpp
src/utils/utils.h
tests/patch.cpp [new file with mode: 0644]

index 98ce5e802629b7ee5c6054b688d9d13d5c633ed4..f254f19c118d78ee4498fb5ddd6fd622bf913e72 100644 (file)
@@ -2,7 +2,7 @@ language: cpp
 
 compiler: gcc
 
-notifications: 
+notifications:
   email:
     recipients:
       - giadaloopmachine@gmail.com
@@ -13,10 +13,15 @@ before_install:
   - sudo apt-get update -qq
   - sudo apt-get install -y libsndfile1-dev libsamplerate0-dev libfltk1.3-dev libasound2-dev libxpm-dev libpulse-dev libjack-dev
 
-before_script: 
+before_script:
   - wget http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-2.1.0.tar.gz
   - tar -xvf rtmidi-2.1.0.tar.gz
-  - cd rtmidi-2.1.0 && ./configure --with-jack --with-alsa && make && sudo make install || true 
+  - cd rtmidi-2.1.0 && ./configure --with-jack --with-alsa && make && sudo make install || true
+  - cd ..
+  - wget http://www.digip.org/jansson/releases/jansson-2.7.tar.gz
+  - tar -xvf jansson-2.7.tar.gz
+  - cd jansson-2.7 && ./configure && make && sudo make install || true
+  - sudo ldconfig
   - cd ..
   - ./autogen.sh
   - ./configure --target=linux --enable-vst
index 620e85c19ae5063acda954a1f1c61cbe8d56c884..466f6df858b6662b93448f59cc59ef83d404e0d5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 
 
 
+0.11.0 --- 2015 . 12 .02
+- New JSON-based patch system
+- Properly store column width in patch
+- Port all const char* strings to std::string in patch/project glue layer
+- Switch to SemVer-like internal versioning system
+- More source code reorganization
+- Fix potential memory leaks in Mixer
+- Fix missing static link of RtMidi on Linux
+- Unable to store pitch values > 2.0 (fixed)
+- Missing assigned key after opening patch (fixed)
+
+
 0.10.2 --- 2015 . 10 . 21
 - Setup Travis CI automated builds
 - Add base framework for unit testing (with Catch)
index 1c99cc30933083d90e2e3b3ed22996ba2811d29d..84b2f4e16f77e752238b7ade4973da623e1f3c24 100644 (file)
@@ -37,16 +37,22 @@ src/core/kernelMidi.h                  \
 src/core/kernelMidi.cpp                \
 src/core/graphics.h                    \
 src/core/graphics.cpp                  \
+src/core/patch_DEPR_.h                 \
+src/core/patch_DEPR_.cpp               \
 src/core/patch.h                       \
 src/core/patch.cpp                     \
 src/core/recorder.h                    \
 src/core/recorder.cpp                  \
 src/core/mixer.h                       \
 src/core/mixer.cpp                     \
-src/core/dataStorage.h                \
-src/core/dataStorage.cpp               \
+src/core/dataStorageIni.h                   \
+src/core/dataStorageIni.cpp            \
+src/core/dataStorageJson.h                \
+src/core/dataStorageJson.cpp           \
 src/glue/glue.h                        \
 src/glue/glue.cpp                      \
+src/glue/storage.h                     \
+src/glue/storage.cpp                   \
 src/gui/dialogs/gd_keyGrabber.h        \
 src/gui/dialogs/gd_keyGrabber.cpp      \
 src/gui/dialogs/gd_about.h             \
@@ -123,6 +129,7 @@ src/utils/log.h                        \
 src/utils/log.cpp                      \
 src/utils/gui_utils.h                  \
 src/utils/gui_utils.cpp                \
+src/utils/gvector.h                    \
 src/utils/utils.h                      \
 src/utils/utils.cpp
 
@@ -132,19 +139,19 @@ src/utils/utils.cpp
 if LINUX
 giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm \
                                                        src/deps/rtaudio-mod/librtaudio.a -ljack -lasound -lpthread -ldl \
-                                                       -lpulse-simple -lpulse -lsamplerate -lrtmidi
+                                                       -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson
 endif
 if WINDOWS
 giada_LDADD   = -lrtaudio -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
                 -lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 \
                 -lsndfile -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser \
-                -lpthreadGC2
+                -lpthreadGC2 -ljansson
 giada_LDFLAGS = -mwindows -static
 giada_SOURCES += resource.rc
 endif
 if OSX
 giada_LDADD    = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
-                                                                -lsamplerate
+                                                                -lsamplerate -ljansson
 giada_CXXFLAGS = -m32
 giada_LDFLAGS  = -m32 -framework CoreAudio -framework Cocoa -framework Carbon \
                 -framework CoreMIDI -framework CoreFoundation
@@ -153,7 +160,7 @@ endif
 # used only under MinGW to compile the resource.rc file (program icon)
 
 resource.o:
-       windres src/ext/resource.rc -o resource.o 
+       windres src/ext/resource.rc -o resource.o
 
 # custom rtaudio
 
@@ -161,23 +168,30 @@ src/deps/rtaudio-mod/librtaudio.a:
        @cd src/deps/rtaudio-mod; echo "Building RtAudio for Linux..."; \
        ./configure --with-jack --with-alsa --with-pulse; \
        make;
-       
+
 # make test --------------------------------------------------------------------
 
 TESTS = giada_tests
 check_PROGRAMS = giada_tests
-giada_tests_SOURCES =    \
-tests/main.cpp           \
-tests/conf.cpp           \
-tests/utils.cpp          \
-src/core/conf.cpp        \
-src/core/dataStorage.cpp \
-src/utils/utils.cpp      \
-src/utils/log.cpp   
-
-# Ancient gcc on Travis complains about missing parentheses. 
-
-giada_tests_CXXFLAGS = -Wno-parentheses     
+giada_tests_SOURCES =        \
+tests/main.cpp               \
+tests/conf.cpp               \
+tests/patch.cpp              \
+tests/utils.cpp              \
+src/core/conf.cpp            \
+src/core/patch.cpp           \
+src/core/dataStorageIni.cpp  \
+src/core/dataStorageJson.cpp \
+src/utils/utils.cpp          \
+src/utils/log.cpp
+
+giada_tests_LDADD = -ljansson
+
+# Ancient gcc on Travis complains about missing parentheses.
+# Catch also goes crazy with GCC 5.x (https://github.com/philsquared/Catch/issues/487)
+# --std=c++11
+
+giada_tests_CXXFLAGS = -Wno-parentheses -std=c++0x
 
 # make rename ------------------------------------------------------------------
 
index a501966047ef17c441c6a8db95cf2f89d1f2a61c..695cf23d7ccf3d333dd2683068f90bcfb9ab05aa 100644 (file)
@@ -4,7 +4,7 @@
 # prereq & init
 
 AC_PREREQ(2.60)
-AC_INIT([giada], [0.10], [giadaloopmachine@gmail.com])
+AC_INIT([giada], [0.11], [giadaloopmachine@gmail.com])
 AC_CONFIG_SRCDIR([src/main.cpp])
 AM_INIT_AUTOMAKE([subdir-objects])
 
@@ -100,6 +100,14 @@ AC_CHECK_HEADER(
 )
 AC_LANG_POP
 
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER(
+       [jansson.h],
+       [],
+       [AC_MSG_ERROR([library 'Jansson' not found!])]
+)
+AC_LANG_POP
+
 AC_LANG_PUSH([C++])
 AC_CHECK_HEADER(
        [sndfile.h],
index 85ee975b2551fc4c248d40fb9a75dee2f785b90c..762ba0dbe92336fed37a375c274426523b55228b 100644 (file)
 #include "channel.h"
 #include "pluginHost.h"
 #include "kernelMidi.h"
+#include "patch_DEPR_.h"
 #include "patch.h"
 #include "wave.h"
 #include "mixer.h"
 #include "mixerHandler.h"
 #include "conf.h"
+#include "patch.h"
 #include "waveFx.h"
 #include "midiMapConf.h"
 
 
+extern Patch_DEPR_ G_Patch_DEPR_;
 extern Patch       G_Patch;
 extern Mixer       G_Mixer;
 extern Conf        G_Conf;
@@ -124,23 +127,23 @@ void Channel::sendMidiLmessage(uint32_t learn, int chan, uint32_t msg, int offse
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::readPatchMidiIn(int i)
+void Channel::readPatchMidiIn_DEPR_(int i)
 {
-       midiIn         = G_Patch.getMidiValue(i, "In");
-       midiInKeyPress = G_Patch.getMidiValue(i, "InKeyPress");
-       midiInKeyRel   = G_Patch.getMidiValue(i, "InKeyRel");
-  midiInKill     = G_Patch.getMidiValue(i, "InKill");
-  midiInVolume   = G_Patch.getMidiValue(i, "InVolume");
-  midiInMute     = G_Patch.getMidiValue(i, "InMute");
-  midiInSolo     = G_Patch.getMidiValue(i, "InSolo");
+       midiIn         = G_Patch_DEPR_.getMidiValue(i, "In");
+       midiInKeyPress = G_Patch_DEPR_.getMidiValue(i, "InKeyPress");
+       midiInKeyRel   = G_Patch_DEPR_.getMidiValue(i, "InKeyRel");
+  midiInKill     = G_Patch_DEPR_.getMidiValue(i, "InKill");
+  midiInVolume   = G_Patch_DEPR_.getMidiValue(i, "InVolume");
+  midiInMute     = G_Patch_DEPR_.getMidiValue(i, "InMute");
+  midiInSolo     = G_Patch_DEPR_.getMidiValue(i, "InSolo");
 }
 
-void Channel::readPatchMidiOut(int i)
+void Channel::readPatchMidiOut_DEPR_(int i)
 {
-       midiOutL        = G_Patch.getMidiValue(i, "OutL");
-       midiOutLplaying = G_Patch.getMidiValue(i, "OutLplaying");
-       midiOutLmute    = G_Patch.getMidiValue(i, "OutLmute");
-       midiOutLsolo    = G_Patch.getMidiValue(i, "OutLsolo");
+       midiOutL        = G_Patch_DEPR_.getMidiValue(i, "OutL");
+       midiOutLplaying = G_Patch_DEPR_.getMidiValue(i, "OutLplaying");
+       midiOutLmute    = G_Patch_DEPR_.getMidiValue(i, "OutLmute");
+       midiOutLsolo    = G_Patch_DEPR_.getMidiValue(i, "OutLsolo");
 }
 
 
@@ -156,30 +159,119 @@ bool Channel::isPlaying()
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::writePatch(FILE *fp, int i, bool isProject)
+int Channel::writePatch(int i, bool isProject)
 {
-       fprintf(fp, "chanType%d=%d\n",     i, type);
-       fprintf(fp, "chanIndex%d=%d\n",    i, index);
-       fprintf(fp, "chanColumn%d=%d\n",   i, guiChannel->getColumnIndex());
-       fprintf(fp, "chanMute%d=%d\n",     i, mute);
-       fprintf(fp, "chanMute_s%d=%d\n",   i, mute_s);
-       fprintf(fp, "chanSolo%d=%d\n",     i, solo);
-       fprintf(fp, "chanvol%d=%f\n",      i, volume);
-       fprintf(fp, "chanPanLeft%d=%f\n",  i, panLeft);
-       fprintf(fp, "chanPanRight%d=%f\n", i, panRight);
-
-       fprintf(fp, "chanMidiIn%d=%u\n",         i, midiIn);
-       fprintf(fp, "chanMidiInKeyPress%d=%u\n", i, midiInKeyPress);
-       fprintf(fp, "chanMidiInKeyRel%d=%u\n",   i, midiInKeyRel);
-       fprintf(fp, "chanMidiInKill%d=%u\n",     i, midiInKill);
-       fprintf(fp, "chanMidiInVolume%d=%u\n",   i, midiInVolume);
-       fprintf(fp, "chanMidiInMute%d=%u\n",     i, midiInMute);
-       fprintf(fp, "chanMidiInSolo%d=%u\n",     i, midiInSolo);
-
-       fprintf(fp, "chanMidiOutL%d=%u\n",        i, midiOutL);
-       fprintf(fp, "chanMidiOutLplaying%d=%u\n", i, midiOutLplaying);
-       fprintf(fp, "chanMidiOutLmute%d=%u\n",    i, midiOutLmute);
-       fprintf(fp, "chanMidiOutLsolo%d=%u\n",    i, midiOutLsolo);
+       Patch::channel_t pch;
+       pch.type            = type;
+       pch.key             = key;
+       pch.index           = index;
+       pch.column          = guiChannel->getColumnIndex();
+       pch.mute            = mute;
+       pch.mute_s          = mute_s;
+       pch.solo            = solo;
+       pch.volume          = volume;
+       pch.panLeft         = panLeft;
+       pch.panRight        = panRight;
+       pch.midiIn          = midiIn;
+       pch.midiInKeyPress  = midiInKeyPress;
+       pch.midiInKeyRel    = midiInKeyRel;
+       pch.midiInKill      = midiInKill;
+       pch.midiInVolume    = midiInVolume;
+       pch.midiInMute      = midiInMute;
+       pch.midiInSolo      = midiInSolo;
+       pch.midiOutL        = midiOutL;
+       pch.midiOutLplaying = midiOutLplaying;
+       pch.midiOutLmute    = midiOutLmute;
+       pch.midiOutLsolo    = midiOutLsolo;
+
+       for (unsigned i=0; i<recorder::global.size; i++) {
+               for (unsigned k=0; k<recorder::global.at(i).size; k++) {
+                       recorder::action *action = recorder::global.at(i).at(k);
+                       if (action->chan == index) {
+                               Patch::action_t pac;
+                               pac.type   = action->type;
+                   pac.frame  = action->frame;
+                   pac.fValue = action->fValue;
+                   pac.iValue = action->iValue;
+                               pch.actions.add(pac);
+                       }
+               }
+       }
+
+#ifdef WITH_VST
+
+       unsigned numPlugs = G_PluginHost.countPlugins(PluginHost::CHANNEL, this);
+       for (int i=0; i<numPlugs; i++) {
+               Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, PluginHost::CHANNEL, this);
+               if (pPlugin->status) {
+                       Patch::plugin_t pp;
+                       pp.path   = pPlugin->pathfile;
+           pp.bypass = pPlugin->bypass;
+                       for (int k=0; k<pPlugin->getNumParams(); k++)
+                               pp.params.add(pPlugin->getParam(k));
+                       pch.plugins.add(pp);
+               }
+       }
+
+#endif
+
+       G_Patch.channels.add(pch);
+
+       return G_Patch.channels.size - 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Channel::readPatch(const string &path, int i)
+{
+       int ret = 1;
+       Patch::channel_t *pch = &G_Patch.channels.at(i);
+       key             = pch->key;
+       type            = pch->type;
+       index           = pch->index;
+       mute            = pch->mute;
+       mute_s          = pch->mute_s;
+       solo            = pch->solo;
+       volume          = pch->volume;
+       panLeft         = pch->panLeft;
+       panRight        = pch->panRight;
+       midiIn          = pch->midiIn;
+       midiInKeyPress  = pch->midiInKeyPress;
+       midiInKeyRel    = pch->midiInKeyRel;
+  midiInKill      = pch->midiInKill;
+  midiInVolume    = pch->midiInVolume;
+  midiInMute      = pch->midiInMute;
+  midiInSolo      = pch->midiInSolo;
+       midiOutL        = pch->midiOutL;
+       midiOutLplaying = pch->midiOutLplaying;
+       midiOutLmute    = pch->midiOutLmute;
+       midiOutLsolo    = pch->midiOutLsolo;
+
+       for (unsigned k=0; k<pch->actions.size; k++) {
+               Patch::action_t *ac = &pch->actions.at(k);
+               recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
+       }
+
+#ifdef WITH_VST
+
+       for (unsigned k=0; k<pch->plugins.size; k++) {
+               Patch::plugin_t *ppl = &pch->plugins.at(k);
+               Plugin *plugin = G_PluginHost.addPlugin(ppl->path.c_str(), PluginHost::CHANNEL, this);
+               if (plugin != NULL) {
+                       plugin->bypass = ppl->bypass;
+                       for (unsigned j=0; j<ppl->params.size; j++)
+                               plugin->setParam(j, ppl->params.at(j));
+                       ret &= 1;
+               }
+               else
+                       ret &= 0;
+       }
+
+#endif
+
+       return ret;
 }
 
 
index e34628024d5fda8e0b90bc2a15d6a2a49461ee3a..0a50284734b428bdf18784c3b57ef6465fca8743 100644 (file)
@@ -52,6 +52,9 @@
 #endif
 
 
+using std::string;
+
+
 class Channel {
 
 protected:
@@ -73,14 +76,16 @@ public:
        virtual ~Channel();
 
        /* writePatch
-        * store values in patch, writing to *fp. */
+        * Fill a patch with channel values. Returns the index of the last
+        * Patch::channel_t added. */
 
-       virtual void writePatch(FILE *fp, int i, bool isProject);
+       virtual int writePatch(int i, bool isProject);
 
-       /* loadByPatch
-        * load a sample inside a patch. */
+       /* readPatch
+        * Fill channel with data from patch. */
 
-       virtual int loadByPatch(const char *file, int i) = 0;
+       virtual int readPatch_DEPR_(const char *file, int i) = 0;
+       virtual int readPatch(const string &basePath, int i);
 
        /* process
         * merge vChannels into buffer, plus plugin processing (if any). */
@@ -206,8 +211,8 @@ public:
         * read from patch all midi-related parameters such as keypress, mute
         * and so on. */
 
-       void readPatchMidiIn(int i);
-       void readPatchMidiOut(int i);
+       void readPatchMidiIn_DEPR_(int i);
+       void readPatchMidiOut_DEPR_(int i);
 
        /* sendMidiL*
         * send MIDI lightning events to a physical device. */
index 0dd258e4b28517a3db1edbe9c493818ed0eee284..06c86da01726fe1ff2255af73373285b6513cdd0 100644 (file)
@@ -353,7 +353,7 @@ int Conf::write()
 
        fprintf(fp, "# --- Giada configuration file --- \n");
        fprintf(fp, "header=GIADACFG\n");
-       fprintf(fp, "version=%s\n", VERSIONE);
+       fprintf(fp, "version=%s\n", G_VERSION_STR);
 
        fprintf(fp, "logMode=%d\n",    logMode);
 
index 9120f2ed4aec1dadd0bca167dc136cae275e7bf5..0c4a3e16485e7236467d114363c1f7d9cbf2c035 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <limits.h>
 #include <stdint.h>
-#include "dataStorage.h"
+#include "dataStorageIni.h"
 
 
 #if defined(__APPLE__)
@@ -41,7 +41,7 @@
 #endif
 
 
-class Conf : public DataStorage
+class Conf : public DataStorageIni
 {
 private:
 
index d771c406899282e4f1aaa15f9462edc8c551d7d7..4cf0af34cb6d772c852d5718314551e27c3ee368 100644 (file)
  *
  * -------------------------------------------------------------------------- */
 
+
 #ifndef CONST_H
 #define CONST_H
 
 
 /* -- version --------------------------------------------------------------- */
-#define VERSIONE                               "0.10.2"
-#define VERSIONE_STR           "Giada"
-#define VERSIONE_FLOAT 1.01f
+#define G_VERSION_STR   "0.11.0"
+#define G_APP_NAME      "Giada"
+#define G_VERSION_MAJOR 0
+#define G_VERSION_MINOR 11
+#define G_VERSION_PATCH 0
 
 #define CONF_FILENAME          "giada.conf"
 
@@ -69,6 +72,7 @@
 #define MAX_PATCHNAME_LEN       32
 #define DB_MIN_SCALE              60.0f
 #define MAX_VST_EVENTS     32
+#define MIN_COLUMN_WIDTH   140
 
 
 
 #define DEFAULT_BARS                      1
 #define DEFAULT_QUANTIZE     0           // quantizer off
 #define DEFAULT_FADEOUT_STEP 0.01f  // micro-fadeout speed
+#define DEFAULT_COLUMN_WIDTH 380
 
 
 
 #define WID_KEY_GRABBER   -10
 
 
-/* -- patch signals --------------------------------------------------------- */
-#define PATCH_UNREADABLE  0
-#define PATCH_INVALID    -1
-#define PATCH_OPEN_OK     1
 
-/** TODO - addo to PATCH_ serie the signals for saving/loading */
+/* -- patch signals --------------------------------------------------------- */
+#define PATCH_UNREADABLE    0x01
+#define PATCH_INVALID       0x02
+#define PATCH_READ_OK       0x04
+#define PATCH_WRONG_PLUGINS 0x08  // currently unused
+#define PATCH_WRONG_SAMPLES 0x10  // currently unused
 
 
 
@@ -288,4 +294,70 @@ const int MIDI_CHANS[16] = {
 #define MIDI_SYNC_MTC_M     0x04
 #define MIDI_SYNC_MTC_S     0x08
 
+/* JSON patch keys */
+
+#define PATCH_KEY_HEADER                       "header"
+#define PATCH_KEY_VERSION                      "version"
+#define PATCH_KEY_VERSION_MAJOR                "version_major"
+#define PATCH_KEY_VERSION_MINOR                "version_minor"
+#define PATCH_KEY_VERSION_PATCH                "version_patch"
+#define PATCH_KEY_NAME                         "name"
+#define PATCH_KEY_BPM                          "bpm"
+#define PATCH_KEY_BARS                         "bars"
+#define PATCH_KEY_BEATS                        "beats"
+#define PATCH_KEY_QUANTIZE                     "quantize"
+#define PATCH_KEY_MASTER_VOL_IN                "master_vol_in"
+#define PATCH_KEY_MASTER_VOL_OUT               "master_vol_out"
+#define PATCH_KEY_METRONOME                    "metronome"
+#define PATCH_KEY_LAST_TAKE_ID                 "last_take_id"
+#define PATCH_KEY_SAMPLERATE                   "samplerate"
+#define PATCH_KEY_COLUMNS                      "columns"
+#define PATCH_KEY_MASTER_OUT_PLUGINS           "master_out_plugins"
+#define PATCH_KEY_MASTER_IN_PLUGINS            "master_in_plugins"
+#define PATCH_KEY_CHANNELS                     "channels"
+#define PATCH_KEY_CHANNEL_TYPE                 "type"
+#define PATCH_KEY_CHANNEL_INDEX                "index"
+#define PATCH_KEY_CHANNEL_COLUMN               "column"
+#define PATCH_KEY_CHANNEL_MUTE                 "mute"
+#define PATCH_KEY_CHANNEL_MUTE_S               "mute_s"
+#define PATCH_KEY_CHANNEL_SOLO                 "solo"
+#define PATCH_KEY_CHANNEL_VOLUME               "volume"
+#define PATCH_KEY_CHANNEL_PAN_LEFT             "pan_left"
+#define PATCH_KEY_CHANNEL_PAN_RIGHT            "pan_right"
+#define PATCH_KEY_CHANNEL_MIDI_IN              "midi_in"
+#define PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS     "midi_in_keypress"
+#define PATCH_KEY_CHANNEL_MIDI_IN_KEYREL       "midi_in_keyrel"
+#define PATCH_KEY_CHANNEL_MIDI_IN_KILL         "midi_in_kill"
+#define PATCH_KEY_CHANNEL_MIDI_IN_VOLUME       "midi_in_volume"
+#define PATCH_KEY_CHANNEL_MIDI_IN_MUTE         "midi_in_mute"
+#define PATCH_KEY_CHANNEL_MIDI_IN_SOLO         "midi_in_solo"
+#define PATCH_KEY_CHANNEL_MIDI_OUT_L           "midi_out_l"
+#define PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING   "midi_out_l_playing"
+#define PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE      "midi_out_l_mute"
+#define PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO      "midi_out_l_solo"
+#define PATCH_KEY_CHANNEL_SAMPLE_PATH          "sample_path"
+#define PATCH_KEY_CHANNEL_KEY                  "key"
+#define PATCH_KEY_CHANNEL_MODE                 "mode"
+#define PATCH_KEY_CHANNEL_BEGIN                "begin"
+#define PATCH_KEY_CHANNEL_END                  "end"
+#define PATCH_KEY_CHANNEL_BOOST                "boost"
+#define PATCH_KEY_CHANNEL_REC_ACTIVE           "rec_active"
+#define PATCH_KEY_CHANNEL_PITCH                "pitch"
+#define PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS "midi_in_read_actions"
+#define PATCH_KEY_CHANNEL_MIDI_IN_PITCH        "midi_in_pitch"
+#define PATCH_KEY_CHANNEL_MIDI_OUT             "midi_out"
+#define PATCH_KEY_CHANNEL_MIDI_OUT_CHAN        "midi_out_chan"
+#define PATCH_KEY_CHANNEL_PLUGINS              "plugins"
+#define PATCH_KEY_CHANNEL_ACTIONS              "actions"
+#define PATCH_KEY_ACTION_TYPE                  "type"
+#define PATCH_KEY_ACTION_FRAME                 "frame"
+#define PATCH_KEY_ACTION_F_VALUE               "f_value"
+#define PATCH_KEY_ACTION_I_VALUE               "i_value"
+#define PATCH_KEY_PLUGIN_PATH                  "path"
+#define PATCH_KEY_PLUGIN_BYPASS                "bypass"
+#define PATCH_KEY_PLUGIN_PARAMS                "params"
+#define PATCH_KEY_COLUMN_INDEX                 "index"
+#define PATCH_KEY_COLUMN_WIDTH                 "width"
+#define PATCH_KEY_COLUMN_CHANNELS              "channels"
+
 #endif
diff --git a/src/core/dataStorage.cpp b/src/core/dataStorage.cpp
deleted file mode 100644 (file)
index 0cea01f..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorage
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <stdlib.h>
-#include <limits.h>
-#include "../utils/log.h"
-#include "dataStorage.h"
-#include "const.h"
-
-
-std::string DataStorage::getValue(const char *in) {
-
-       /* on each call reset the pointe to the beginning of the file. Not so
-        * good but necessary if you want to pick up random values from the
-        * file. */
-
-       fseek(fp, 0L, SEEK_SET);
-       std::string out = "";
-
-       while (!feof(fp)) {
-
-               char buffer[MAX_LINE_LEN];
-               if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) {
-                       gLog("[PATCH] get_value error (key=%s)\n", in);
-                       return "";
-               }
-
-               if (buffer[0] == '#')
-                       continue;
-
-               unsigned len = strlen(in);
-               if (strncmp(buffer, in, len) == 0) {
-
-                       for (unsigned i=len+1; i<MAX_LINE_LEN; i++) {
-                               if (buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r')
-                                       break;
-                               out += buffer[i];
-                       }
-
-                       break; // string found
-               }
-       }
-       return out;
-}
diff --git a/src/core/dataStorage.h b/src/core/dataStorage.h
deleted file mode 100644 (file)
index 9d54ddf..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorage
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef __DATA_STORAGE_H__
-#define __DATA_STORAGE_H__
-
-#include <stdio.h>
-#include <string>
-#include <string.h>
-
-#define MAX_LINE_LEN 1024
-
-
-class DataStorage {
-
-protected:
-
-       FILE *fp;
-       std::string getValue(const char *in);
-};
-
-#endif
diff --git a/src/core/dataStorageIni.cpp b/src/core/dataStorageIni.cpp
new file mode 100644 (file)
index 0000000..abf374b
--- /dev/null
@@ -0,0 +1,70 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorageIni
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <stdlib.h>
+#include <limits.h>
+#include "../utils/log.h"
+#include "dataStorageIni.h"
+#include "const.h"
+
+
+std::string DataStorageIni::getValue(const char *in)
+{
+       /* on each call reset the pointe to the beginning of the file. Not so
+        * good but necessary if you want to pick up random values from the
+        * file. */
+
+       fseek(fp, 0L, SEEK_SET);
+       std::string out = "";
+
+       while (!feof(fp)) {
+
+               char buffer[MAX_LINE_LEN];
+               if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) {
+                       gLog("[DataStorageIni::getValue] key '%s' not found\n", in);
+                       return "";
+               }
+
+               if (buffer[0] == '#')
+                       continue;
+
+               unsigned len = strlen(in);
+               if (strncmp(buffer, in, len) == 0) {
+
+                       for (unsigned i=len+1; i<MAX_LINE_LEN; i++) {
+                               if (buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r')
+                                       break;
+                               out += buffer[i];
+                       }
+
+                       break; // string found
+               }
+       }
+       return out;
+}
diff --git a/src/core/dataStorageIni.h b/src/core/dataStorageIni.h
new file mode 100644 (file)
index 0000000..17b65a9
--- /dev/null
@@ -0,0 +1,48 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorageIni
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef __DATA_STORAGE_INI_H__
+#define __DATA_STORAGE_INI_H__
+
+#include <stdio.h>
+#include <string>
+#include <string.h>
+
+#define MAX_LINE_LEN 1024
+
+
+class DataStorageIni 
+{
+protected:
+
+       FILE *fp;
+       std::string getValue(const char *in);
+};
+
+#endif
diff --git a/src/core/dataStorageJson.cpp b/src/core/dataStorageJson.cpp
new file mode 100644 (file)
index 0000000..d8ffb86
--- /dev/null
@@ -0,0 +1,148 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorageIni
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include "../utils/log.h"
+#include "dataStorageJson.h"
+
+
+using std::string;
+
+
+bool DataStorageJson::setString(json_t *jRoot, const char *key, string &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!json_is_string(jObject)) {
+    gLog("[dataStorageJson::setString] key '%s' is not a string!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_string_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::setFloat(json_t *jRoot, const char *key, float &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gLog("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key);
+    output = 0.0f;
+    return true;
+  }
+  if (!json_is_real(jObject)) {
+    gLog("[dataStorageJson::setFloat] key '%s' is not a float!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_real_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::setUint32(json_t *jRoot, const char *key, uint32_t &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gLog("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key);
+    output = 0;
+    return true;
+  }
+  if (!json_is_integer(jObject)) {
+    gLog("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_integer_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::setBool(json_t *jRoot, const char *key, bool &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gLog("[dataStorageJson::setBool] key '%s' not found, using default value\n", key);
+    output = false;
+    return true;
+  }
+  if (!json_is_boolean(jObject)) {
+    gLog("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_boolean_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::setInt(json_t *jRoot, const char *key, int &output)
+{
+  return setUint32(jRoot, key, (uint32_t&) output);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::checkObject(json_t *jRoot, const char *key)
+{
+  if (!json_is_object(jRoot)) {
+    gLog("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool DataStorageJson::checkArray(json_t *jRoot, const char *key)
+{
+  if (!json_is_array(jRoot)) {
+    gLog("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  return true;
+}
diff --git a/src/core/dataStorageJson.h b/src/core/dataStorageJson.h
new file mode 100644 (file)
index 0000000..ad3cdea
--- /dev/null
@@ -0,0 +1,65 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorageIni
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef __DATA_STORAGE_JSON_H__
+#define __DATA_STORAGE_JSON_H__
+
+
+#include <stdint.h>
+#include <jansson.h>
+
+
+using std::string;
+
+
+class DataStorageJson
+{
+protected:
+
+  json_t       *jRoot;
+  json_error_t  jError;
+
+  bool setString(json_t *jRoot, const char *key, string &output);
+  bool setFloat(json_t *jRoot, const char *key, float &output);
+  bool setUint32(json_t *jRoot, const char *key, uint32_t &output);
+  bool setInt(json_t *jRoot, const char *key, int &output);
+  bool setBool(json_t *jRoot, const char *key, bool &output);
+
+  /* checkObject
+  check whether the jRoot object is a valid json object {} */
+
+  bool checkObject(json_t *jRoot, const char *key);
+
+  /* checkArray
+  check whether the jRoot object is a valid json array [] */
+
+  bool checkArray(json_t *jRoot, const char *key);
+};
+
+#endif
index 1ecbb2d90a48901f4c68ca9802933375df5dcb09..473ecacd2f1fe0ae0a476401c7a3766b380823a1 100644 (file)
@@ -38,6 +38,7 @@
 #include "wave.h"
 #include "const.h"
 #include "mixerHandler.h"
+#include "patch_DEPR_.h"
 #include "patch.h"
 #include "conf.h"
 #include "pluginHost.h"
@@ -49,7 +50,8 @@
 extern Mixer                      G_Mixer;
 extern bool                               G_audio_status;
 extern bool                               G_quit;
-extern Patch              G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
+extern Patch         G_Patch;
 extern Conf          G_Conf;
 extern MidiMapConf   G_MidiMap;
 extern gdMainWindow *mainWin;
@@ -62,12 +64,13 @@ extern PluginHost      G_PluginHost;
 void init_prepareParser()
 {
        G_Conf.read();
-       G_Patch.setDefault();
+       G_Patch_DEPR_.setDefault();
+       G_Patch.init();
        if (!gLog_init(G_Conf.logMode))
                gLog("[init] log init failed! Using default stdout\n");
   time_t t;
   time (&t);
-       gLog("[init] Giada "VERSIONE" - %s", ctime(&t));
+       gLog("[init] Giada " G_VERSION_STR " - %s", ctime(&t));
        gLog("[init] configuration file ready\n");
 }
 
@@ -119,8 +122,8 @@ void init_startGUI(int argc, char **argv)
 {
        char win_label[32];
        sprintf(win_label, "%s - %s",
-                                       VERSIONE_STR,
-                                       !strcmp(G_Patch.name, "") ? "(default patch)" : G_Patch.name);
+                                       G_APP_NAME,
+                                       !strcmp(G_Patch_DEPR_.name, "") ? "(default patch)" : G_Patch_DEPR_.name);
 
        mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv);
        mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH);
@@ -196,6 +199,6 @@ void init_shutdown()
        gLog("[init] Plugin Host cleaned up\n");
 #endif
 
-       gLog("[init] Giada "VERSIONE" closed\n\n");
+       gLog("[init] Giada " G_VERSION_STR " closed\n\n");
        gLog_close();
 }
index dfb95f8d9913ed7fa01528bd132fbbf42698ff40..a890f38413c0bbc1b86f90dc49745f2973467279 100644 (file)
 #include "midiChannel.h"
 #include "channel.h"
 #include "pluginHost.h"
+#include "patch_DEPR_.h"
 #include "patch.h"
 #include "conf.h"
 #include "kernelMidi.h"
 
 
+extern Patch_DEPR_ G_Patch_DEPR_;
 extern Patch       G_Patch;
 extern Mixer       G_Mixer;
 extern Conf        G_Conf;
@@ -274,21 +276,21 @@ void MidiChannel::kill(int frame)
 /* -------------------------------------------------------------------------- */
 
 
-int MidiChannel::loadByPatch(const char *f, int i)
+int MidiChannel::readPatch_DEPR_(const char *f, int i)
 {
-       volume      = G_Patch.getVol(i);
-       index       = G_Patch.getIndex(i);
-       mute        = G_Patch.getMute(i);
-       mute_s      = G_Patch.getMute_s(i);
-       solo        = G_Patch.getSolo(i);
-       panLeft     = G_Patch.getPanLeft(i);
-       panRight    = G_Patch.getPanRight(i);
+       volume      = G_Patch_DEPR_.getVol(i);
+       index       = G_Patch_DEPR_.getIndex(i);
+       mute        = G_Patch_DEPR_.getMute(i);
+       mute_s      = G_Patch_DEPR_.getMute_s(i);
+       solo        = G_Patch_DEPR_.getSolo(i);
+       panLeft     = G_Patch_DEPR_.getPanLeft(i);
+       panRight    = G_Patch_DEPR_.getPanRight(i);
 
-       midiOut     = G_Patch.getMidiValue(i, "Out");
-       midiOutChan = G_Patch.getMidiValue(i, "OutChan");
+       midiOut     = G_Patch_DEPR_.getMidiValue(i, "Out");
+       midiOutChan = G_Patch_DEPR_.getMidiValue(i, "OutChan");
 
-       readPatchMidiIn(i);
-       readPatchMidiOut(i);
+       readPatchMidiIn_DEPR_(i);
+       readPatchMidiOut_DEPR_(i);
 
        return SAMPLE_LOADED_OK;  /// TODO - change name, it's meaningless here
 }
@@ -297,6 +299,22 @@ int MidiChannel::loadByPatch(const char *f, int i)
 /* -------------------------------------------------------------------------- */
 
 
+int MidiChannel::readPatch(const string &basePath, int i)
+{
+       Channel::readPatch("", i);
+
+       Patch::channel_t *pch = &G_Patch.channels.at(i);
+
+       midiOut     = pch->midiOut;
+       midiOutChan = pch->midiOutChan;
+       
+       return SAMPLE_LOADED_OK;  /// TODO - change name, it's meaningless here
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void MidiChannel::sendMidi(recorder::action *a, int localFrame)
 {
        if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
@@ -339,10 +357,13 @@ void MidiChannel::rewind()
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::writePatch(FILE *fp, int i, bool isProject)
+int MidiChannel::writePatch(int i, bool isProject)
 {
-       Channel::writePatch(fp, i, isProject);
+       int pchIndex = Channel::writePatch(i, isProject);
+       Patch::channel_t *pch = &G_Patch.channels.at(pchIndex);
+
+       pch->midiOut     = midiOut;
+       pch->midiOutChan = midiOutChan;
 
-       fprintf(fp, "chanMidiOut%d=%u\n",        i, midiOut);
-       fprintf(fp, "chanMidiOutChan%d=%u\n",    i, midiOutChan);
+       return 0;
 }
index 275081c8d08204bf8478dd6300abfe91de33aaca..cd26ad98ad0a2dff2857c8e5ded85c4e506defaf 100644 (file)
@@ -50,8 +50,8 @@
 #endif
 
 
-class MidiChannel : public Channel {
-
+class MidiChannel : public Channel
+{
 public:
 
        MidiChannel(int bufferSize);
@@ -60,21 +60,22 @@ public:
   bool    midiOut;           // enable midi output
   uint8_t midiOutChan;       // midi output channel
 
-       void  process    (float *buffer);
-       void  start      (int frame, bool doQuantize);
-       void  kill       (int frame);
-       void  empty      ();
-       void  stopBySeq  ();
-       void  stop       ();
-       void  rewind     ();
-       void  setMute    (bool internal);
-       void  unsetMute  (bool internal);
-       int   loadByPatch(const char *file, int i);
-       void  writePatch (FILE *fp, int i, bool isProject);
-       void  quantize   (int index, int localFrame, int globalFrame);
-       void  onZero     (int frame);
-       void  onBar      (int frame);
-       void  parseAction(recorder::action *a, int localFrame, int globalFrame);
+       void process    (float *buffer);
+       void start      (int frame, bool doQuantize);
+       void kill       (int frame);
+       void empty      ();
+       void stopBySeq  ();
+       void stop       ();
+       void rewind     ();
+       void setMute    (bool internal);
+       void unsetMute  (bool internal);
+       int  readPatch_DEPR_  (const char *file, int i);
+       int  readPatch  (const string &basePath, int i);
+       int  writePatch (int i, bool isProject);
+       void quantize   (int index, int localFrame, int globalFrame);
+       void onZero     (int frame);
+       void onBar      (int frame);
+       void parseAction(recorder::action *a, int localFrame, int globalFrame);
 
        /* ---------------------------------------------------------------- */
 
index d56795abe467e5b3161b72a68d3a4a392a0a8636..3f397bec9d1fdda2f5ac2a00d58520a95a970b67 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <limits.h>
 #include <stdint.h>
-#include "dataStorage.h"
+#include "dataStorageIni.h"
 #include "../utils/utils.h"
 #if defined(__APPLE__)
 #include <pwd.h>
@@ -43,7 +43,7 @@
 using std::string;
 
 
-class MidiMapConf : public DataStorage
+class MidiMapConf : public DataStorageIni
 {
 private:
 
index 2930f8147a071593c9f9456aabf3a193dda03254..24a9b25d3cb5b1fff68628a67a024d8a9eec1796 100644 (file)
@@ -35,7 +35,7 @@
 #include "wave.h"
 #include "recorder.h"
 #include "pluginHost.h"
-#include "patch.h"
+#include "patch_DEPR_.h"
 #include "conf.h"
 #include "mixerHandler.h"
 #include "channel.h"
 
 
 extern Mixer                    G_Mixer;
-extern Patch                    G_Patch;
+extern Patch_DEPR_ G_Patch_DEPR_;
 extern Conf                             G_Conf;
 #ifdef WITH_VST
 extern PluginHost  G_PluginHost;
 #endif
 
 
-Mixer::Mixer()         {}
+Mixer::Mixer()
+       : vChanInput(NULL),
+               vChanInToOut(NULL)
+{
+       gLog("[mixer] construct\n");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 Mixer::~Mixer() {}
 
 
+/* -------------------------------------------------------------------------- */
+
+
 #define TICKSIZE 38
 
 
@@ -84,7 +97,8 @@ float Mixer::tick[TICKSIZE] = {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::init() {
+void Mixer::init()
+{
        quanto      = 1;
        docross     = false;
        rewindWait  = false;
@@ -138,8 +152,8 @@ void Mixer::init() {
 /* ------------------------------------------------------------------ */
 
 
-Channel *Mixer::addChannel(int type) {
-
+Channel *Mixer::addChannel(int type)
+{
        Channel *ch;
        int bufferSize = kernelAudio::realBufsize*2;
 
@@ -166,8 +180,8 @@ Channel *Mixer::addChannel(int type) {
 /* ------------------------------------------------------------------ */
 
 
-int Mixer::getNewIndex() {
-
+int Mixer::getNewIndex()
+{
        /* always skip last channel: it's the last one just added */
 
        if (channels.size == 1)
@@ -186,7 +200,8 @@ int Mixer::getNewIndex() {
 /* ------------------------------------------------------------------ */
 
 
-int Mixer::deleteChannel(Channel *ch) {
+int Mixer::deleteChannel(Channel *ch)
+{
        int lockStatus;
        while (true) {
                lockStatus = pthread_mutex_trylock(&mutex_chans);
@@ -205,7 +220,8 @@ int Mixer::deleteChannel(Channel *ch) {
 /* ------------------------------------------------------------------ */
 
 
-Channel *Mixer::getChannelByIndex(int index) {
+Channel *Mixer::getChannelByIndex(int index)
+{
        for (unsigned i=0; i<channels.size; i++)
                if (channels.at(i)->index == index)
                        return channels.at(i);
@@ -217,8 +233,8 @@ Channel *Mixer::getChannelByIndex(int index) {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::sendMIDIsync() {
-
+void Mixer::sendMIDIsync()
+{
        if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
                if (actualFrame % (framesPerBeat/24) == 0)
                        kernelMidi::send(MIDI_CLOCK, -1, -1);
@@ -283,8 +299,8 @@ void Mixer::sendMIDIsync() {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::sendMIDIrewind() {
-
+void Mixer::sendMIDIrewind()
+{
        midiTCframes  = 0;
        midiTCseconds = 0;
        midiTCminutes = 0;
@@ -308,7 +324,8 @@ void Mixer::sendMIDIrewind() {
 
 int Mixer::masterPlay(
        void *out_buf, void *in_buf, unsigned n_frames,
-       double streamTime, RtAudioStreamStatus status, void *userData) {
+       double streamTime, RtAudioStreamStatus status, void *userData)
+{
        return G_Mixer.__masterPlay(out_buf, in_buf, n_frames);
 }
 
@@ -316,8 +333,8 @@ int Mixer::masterPlay(
 /* ------------------------------------------------------------------ */
 
 
-int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) {
-
+int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames)
+{
        if (!ready)
                return 0;
 
@@ -548,8 +565,8 @@ int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::updateFrameBars() {
-
+void Mixer::updateFrameBars()
+{
        /* seconds ....... total time of play (in seconds) of the whole
         *                 sequencer. 60 / bpm == how many seconds lasts one bpm
         * totalFrames ... number of frames in the whole sequencer, x2 because
@@ -587,12 +604,20 @@ void Mixer::updateFrameBars() {
 /* ------------------------------------------------------------------ */
 
 
-int Mixer::close() {
+int Mixer::close()
+{
        running = false;
        while (channels.size > 0)
                deleteChannel(channels.at(0));
-       free(vChanInput);
-       free(vChanInToOut);
+
+       if (vChanInput) {
+               free(vChanInput);
+               vChanInput = NULL;
+       }
+       if (vChanInToOut) {
+               free(vChanInToOut);
+               vChanInToOut = NULL;
+       }
        return 1;
 }
 
@@ -600,7 +625,8 @@ int Mixer::close() {
 /* ------------------------------------------------------------------ */
 
 
-bool Mixer::isSilent() {
+bool Mixer::isSilent()
+{
        for (unsigned i=0; i<channels.size; i++)
                if (channels.at(i)->status == STATUS_PLAY)
                        return false;
@@ -611,8 +637,8 @@ bool Mixer::isSilent() {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::rewind() {
-
+void Mixer::rewind()
+{
        actualFrame = 0;
        actualBeat  = 0;
 
@@ -627,8 +653,8 @@ void Mixer::rewind() {
 /* ------------------------------------------------------------------ */
 
 
-void Mixer::updateQuanto() {
-
+void Mixer::updateQuanto()
+{
        /* big troubles if frames are odd. */
 
        if (quantize != 0)
@@ -641,7 +667,8 @@ void Mixer::updateQuanto() {
 /* ------------------------------------------------------------------ */
 
 
-bool Mixer::hasLogicalSamples() {
+bool Mixer::hasLogicalSamples()
+{
        for (unsigned i=0; i<channels.size; i++)
                if (channels.at(i)->type == CHANNEL_SAMPLE)
                        if (((SampleChannel*)channels.at(i))->wave)
@@ -654,7 +681,8 @@ bool Mixer::hasLogicalSamples() {
 /* ------------------------------------------------------------------ */
 
 
-bool Mixer::hasEditedSamples() {
+bool Mixer::hasEditedSamples()
+{
        for (unsigned i=0; i<channels.size; i++)
                if (channels.at(i)->type == CHANNEL_SAMPLE)
                        if (((SampleChannel*)channels.at(i))->wave)
@@ -667,7 +695,8 @@ bool Mixer::hasEditedSamples() {
 /* ------------------------------------------------------------------ */
 
 
-bool Mixer::mergeVirtualInput() {
+bool Mixer::mergeVirtualInput()
+{
        if (vChanInput == NULL) {
                gLog("[Mixer] virtual input channel not alloc'd\n");
                return false;
index 427808b12f56c3f231e1be116f04544d6635a0cb..b03b1545ec5a631757fb2e7acf26e5d8ea32ede7 100644 (file)
@@ -45,6 +45,7 @@
 #include "plugin.h"
 #include "waveFx.h"
 #include "conf.h"
+#include "patch_DEPR_.h"
 #include "patch.h"
 #include "recorder.h"
 #include "channel.h"
 #include "wave.h"
 
 
-extern Mixer             G_Mixer;
-extern Patch             G_Patch;
-extern Conf              G_Conf;
+extern Mixer              G_Mixer;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern Patch       G_Patch;
+extern Conf               G_Conf;
 
 #ifdef WITH_VST
-extern PluginHost G_PluginHost;
+extern PluginHost  G_PluginHost;
 #endif
 
 
@@ -72,17 +74,6 @@ void mh_stopSequencer()
 /* -------------------------------------------------------------------------- */
 
 
-void mh_clear()
-{
-       G_Mixer.running = false;
-       while (G_Mixer.channels.size > 0)
-               G_Mixer.channels.del(0U);  // unsigned
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 bool mh_uniqueSolo(Channel *ch)
 {
        int solos = 0;
@@ -100,36 +91,63 @@ bool mh_uniqueSolo(Channel *ch)
 
 /** TODO - revision needed: mh should not call glue_addChannel */
 
-void mh_loadPatch(bool isProject, const char *projPath)
+void mh_loadPatch_DEPR_(bool isProject, const char *projPath)
 {
        G_Mixer.init();
        G_Mixer.ready = false;   // put it in wait mode
 
-       int numChans = G_Patch.getNumChans();
+       int numChans = G_Patch_DEPR_.getNumChans();
        for (int i=0; i<numChans; i++) {
+               Channel *ch = glue_addChannel(G_Patch_DEPR_.getColumn(i), G_Patch_DEPR_.getType(i));
+               string samplePath = isProject ? projPath + gGetSlash() + G_Patch_DEPR_.getSamplePath(i) : "";
+               ch->readPatch_DEPR_(samplePath.c_str(), i);
+       }
 
-               Channel *ch = glue_addChannel(G_Patch.getColumn(i), G_Patch.getType(i));
+       G_Mixer.outVol     = G_Patch_DEPR_.getOutVol();
+       G_Mixer.inVol      = G_Patch_DEPR_.getInVol();
+       G_Mixer.bpm        = G_Patch_DEPR_.getBpm();
+       G_Mixer.bars       = G_Patch_DEPR_.getBars();
+       G_Mixer.beats      = G_Patch_DEPR_.getBeats();
+       G_Mixer.quantize   = G_Patch_DEPR_.getQuantize();
+       G_Mixer.metronome  = G_Patch_DEPR_.getMetronome();
+       G_Patch_DEPR_.lastTakeId = G_Patch_DEPR_.getLastTakeId();
+       G_Patch_DEPR_.samplerate = G_Patch_DEPR_.getSamplerate();
 
-               char smpPath[PATH_MAX];
-               sprintf(smpPath, "%s%s%s", gDirname(projPath).c_str(), gGetSlash().c_str(), G_Patch.getSamplePath(i).c_str());
+       /* rewind and update frames in Mixer (it's vital) */
 
-               ch->loadByPatch(smpPath, i);
-       }
+       G_Mixer.rewind();
+       G_Mixer.updateFrameBars();
+       G_Mixer.ready = true;
+}
 
-       G_Mixer.outVol     = G_Patch.getOutVol();
-       G_Mixer.inVol      = G_Patch.getInVol();
-       G_Mixer.bpm        = G_Patch.getBpm();
-       G_Mixer.bars       = G_Patch.getBars();
-       G_Mixer.beats      = G_Patch.getBeats();
-       G_Mixer.quantize   = G_Patch.getQuantize();
-       G_Mixer.metronome  = G_Patch.getMetronome();
-       G_Patch.lastTakeId = G_Patch.getLastTakeId();
-       G_Patch.samplerate = G_Patch.getSamplerate();
 
-       /* rewind and update frames in Mixer (it's vital) */
+/* -------------------------------------------------------------------------- */
+
+
+void mh_readPatch()
+{
+       G_Mixer.ready = false;
+
+       G_Mixer.outVol     = G_Patch.masterVolOut;
+       G_Mixer.inVol      = G_Patch.masterVolIn;
+       G_Mixer.bpm        = G_Patch.bpm;
+       G_Mixer.bars       = G_Patch.bars;
+       G_Mixer.beats      = G_Patch.beats;
+       G_Mixer.quantize   = G_Patch.quantize;
+       G_Mixer.metronome  = G_Patch.metronome;
+
+#ifdef WITH_VST
+
+       __mh_readPatchPlugins__(&G_Patch.masterInPlugins, PluginHost::MASTER_IN);
+       __mh_readPatchPlugins__(&G_Patch.masterOutPlugins, PluginHost::MASTER_OUT);
+
+#endif
+
+       /* rewind and update frames in Mixer (it's essential) */
 
        G_Mixer.rewind();
        G_Mixer.updateFrameBars();
+
        G_Mixer.ready = true;
 }
 
@@ -174,13 +192,14 @@ SampleChannel *mh_startInputRec()
        /* increase lastTakeId until the sample name TAKE-[n] is unique */
 
        char name[32];
-       sprintf(name, "TAKE-%d", G_Patch.lastTakeId);
+       sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId);
        while (!mh_uniqueSamplename(chan, name)) {
+               G_Patch_DEPR_.lastTakeId++;
                G_Patch.lastTakeId++;
-               sprintf(name, "TAKE-%d", G_Patch.lastTakeId);
+               sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId);
        }
 
-       chan->allocEmpty(G_Mixer.totalFrames, G_Patch.lastTakeId);
+       chan->allocEmpty(G_Mixer.totalFrames, G_Patch_DEPR_.lastTakeId);
        G_Mixer.chanInput = chan;
 
        /* start to write from the actualFrame, not the beginning */
@@ -228,3 +247,29 @@ bool mh_uniqueSamplename(SampleChannel *ch, const char *name)
        }
        return true;
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+int __mh_readPatchPlugins__(gVector<Patch::plugin_t> *list, int type)
+{
+       int ret = 1;
+       for (unsigned i=0; i<list->size; i++) {
+               Patch::plugin_t *ppl = &list->at(i);
+               Plugin *plugin = G_PluginHost.addPlugin(ppl->path.c_str(), type, NULL);
+               if (plugin != NULL) {
+                       plugin->bypass = ppl->bypass;
+                       for (unsigned j=0; j<ppl->params.size; j++)
+                               plugin->setParam(j, ppl->params.at(j));
+                       ret &= 1;
+               }
+               else
+                       ret &= 0;
+       }
+       return ret;
+}
+
+#endif
index f84200406ca04630dae5cf1048903b9187b3a293..8785e3518fd2de13a02e5d7f6ef86f2aa3554df6 100644 (file)
@@ -32,6 +32,7 @@
 
 
 #include "recorder.h"
+#include "patch.h"
 
 
 /* stopSequencer
@@ -42,11 +43,6 @@ void mh_stopSequencer();
 
 void mh_rewindSequencer();
 
-/* clear
- * stop everything and clear all channels. */
-void mh_clear();
-
 /* uniqueSolo
  * true if ch is the only solo'd channel in mixer. */
 
@@ -56,7 +52,8 @@ bool mh_uniqueSolo(class Channel *ch);
  * load a path or a project (if isProject) into Mixer. If isProject, path
  * must contain the address of the project folder. */
 
-void mh_loadPatch(bool isProject, const char *projPath=0);
+void mh_loadPatch_DEPR_(bool isProject, const char *projPath=0);
+void mh_readPatch();
 
 /* startInputRec - record from line in
  * creates a new empty wave in the first available channels and returns
@@ -73,4 +70,10 @@ SampleChannel *mh_stopInputRec();
 
 bool mh_uniqueSamplename(class SampleChannel *ch, const char *name);
 
+#ifdef WITH_VST
+
+static int __mh_readPatchPlugins__(gVector<Patch::plugin_t> *list, int type);
+
+#endif
+
 #endif
index 3613070f7706c5e4c98b3fe18d23c50ad6c6ac10..5bc84c9c3db0ae836d5c5853a19ff886474d15d8 100644 (file)
@@ -33,6 +33,7 @@
 #include "../gui/dialogs/gd_mainWindow.h"
 #include "../gui/elems/ge_keyboard.h"
 #include "patch.h"
+#include "const.h"
 #include "init.h"
 #include "recorder.h"
 #include "conf.h"
@@ -50,28 +51,15 @@ extern PluginHost    G_PluginHost;
 extern gdMainWindow *mainWin;
 
 
-int Patch::open(const char *file)
+void Patch::init()
 {
-       fp = fopen(file, "r");
-       if (fp == NULL)
-               return PATCH_UNREADABLE;
-
-       if (getValue("header") != "GIADAPTC")
-               return PATCH_INVALID;
-
-       version = atof(getValue("versionf").c_str());
-       gLog("[patch] open patch version %f\n", version);
-
-       return PATCH_OPEN_OK;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Patch::setDefault()
-{
-       name[0]    = '\0';
+  columns.clear();
+  channels.clear();
+#ifdef WITH_VST
+  masterInPlugins.clear();
+  masterOutPlugins.clear();
+#endif
+  header     = "GIADAPTC";
   lastTakeId = 0;
   samplerate = DEFAULT_SAMPLERATE;
 }
@@ -80,660 +68,417 @@ void Patch::setDefault()
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::close()
-{
-       return fclose(fp);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Patch::getName()
-{
-       std::string out = getValue("patchname");
-       strncpy(name, out.c_str(), MAX_PATCHNAME_LEN);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string Patch::getSamplePath(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "samplepath%d", c);
-       return getValue(tmp);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch::getPitch(int c)
+int Patch::write(const string &file)
 {
-       char tmp[16];
-       sprintf(tmp, "chanPitch%d", c);
-       float out = atof(getValue(tmp).c_str());
-       if (out > 2.0f || out < 0.1f)
-               return 1.0f;
-       return out;
-}
-
+  jRoot = json_object();
 
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getNumChans()
-{
-       if (version == 0.0)      // backward compatibility with version < 0.6.1
-               return 32;
-       return atoi(getValue("channels").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getNumColumns()
-{
-       return atoi(getValue("columns").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getColumn(int c)
-{
-       if (version == 0.0)      // backward compatibility with version < 0.6.1
-               return 0;
-       char tmp[16];
-       sprintf(tmp, "chanColumn%d", c);
-       return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getIndex(int c)
-{
-       if (version == 0.0)      // backward compatibility with version < 0.6.1
-               return c;
+  writeCommons(jRoot);
+  writeColumns(jRoot, &columns);
+  writeChannels(jRoot, &channels);
+#ifdef WITH_VST
+  writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
+  writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
+#endif
 
-       char tmp[16];
-       sprintf(tmp, "chanIndex%d", c);
-       return atoi(getValue(tmp).c_str());
+  if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
+    gLog("[Patch::write] unable to write patch file!\n");
+    return 0;
+  }
+  return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-float Patch::getVol(int c)
+int Patch::read(const string &file)
 {
-       char tmp[16];
-       sprintf(tmp, "chanvol%d", c);
-       float out = atof(getValue(tmp).c_str());
-       if (out > 1.0f || out < 0.0f)
-               return DEFAULT_VOL;
-       return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
+  jRoot = json_load_file(file.c_str(), 0, &jError);
+  if (!jRoot) {
+    gLog("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
+    return PATCH_UNREADABLE;
+  }
 
+  if (!checkObject(jRoot, "root element"))
+    return PATCH_INVALID;
 
-int Patch::getMode(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "chanmode%d", c);
-       int out = atoi(getValue(tmp).c_str());
-       if (out & (LOOP_ANY | SINGLE_ANY))
-               return out;
-       return DEFAULT_CHANMODE;
-}
+  init();
 
+  /* TODO json_decref also when PATCH_INVALID */
 
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getMute(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "chanMute%d", c);
-       return atoi(getValue(tmp).c_str());
-}
-
+  if (!readCommons(jRoot))  return setInvalid();
+  if (!readColumns(jRoot))  return setInvalid();
+  if (!readChannels(jRoot)) return setInvalid();
+#ifdef WITH_VST
+  if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS))   return setInvalid();
+  if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid();
+#endif
 
-/* -------------------------------------------------------------------------- */
+  json_decref(jRoot);
 
+  sanitize();
 
-int Patch::getMute_s(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "chanMute_s%d", c);
-       return atoi(getValue(tmp).c_str());
+  return PATCH_READ_OK;
 }
 
-
 /* -------------------------------------------------------------------------- */
 
+#ifdef WITH_VST
 
-int Patch::getSolo(int c)
+void Patch::writePlugins(json_t *jContainer, gVector<plugin_t> *plugins, const char *key)
 {
-       char tmp[16];
-       sprintf(tmp, "chanSolo%d", c);
-       return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
+  json_t *jPlugins = json_array();
+  for (unsigned j=0; j<plugins->size; j++) {
+    json_t   *jPlugin = json_object();
+    plugin_t  plugin  = plugins->at(j);
+    json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PATH,     json_string(plugin.path.c_str()));
+    json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_BYPASS,   json_boolean(plugin.bypass));
+    json_array_append_new(jPlugins, jPlugin);
 
+    /* plugin params */
 
-int Patch::getType(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "chanType%d", c);
-       int out = atoi(getValue(tmp).c_str());
-       if (out == 0)
-               return CHANNEL_SAMPLE;
-       return out;
+    json_t *jPluginParams = json_array();
+    for (unsigned z=0; z<plugin.params.size; z++) {
+      json_array_append_new(jPluginParams, json_real(plugin.params.at(z)));
+    }
+    json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PARAMS, jPluginParams);
+  }
+  json_object_set_new(jContainer, key, jPlugins);
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getBegin(int c)
-{
-       char tmp[16];
-       if (version < 0.73f)
-               sprintf(tmp, "chanstart%d", c);
-       else
-               sprintf(tmp, "chanBegin%d", c);
-       int out = atoi(getValue(tmp).c_str());
-       if (out < 0)
-               return 0;
-       return out;
-}
+#endif
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::getEnd(int c, unsigned size)
+void Patch::writeColumns(json_t *jContainer, gVector<column_t> *columns)
 {
-       char tmp[16];
-       sprintf(tmp, "chanend%d", c);
-
-       /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0.
-        * good in theory, a disaster in practice. */
-
-       std::string val = getValue(tmp);
-       if (val == "")
-               return size;
-
-       unsigned out = atoi(val.c_str());
-       if (out <= 0 || out > size)
-               return size;
-       return out;
+  json_t *jColumns = json_array();
+  for (unsigned i=0; i<columns->size; i++) {
+    json_t   *jColumn = json_object();
+    column_t  column  = columns->at(i);
+    json_object_set_new(jColumn, PATCH_KEY_COLUMN_INDEX, json_integer(column.index));
+    json_object_set_new(jColumn, PATCH_KEY_COLUMN_WIDTH, json_integer(column.width));
+    json_array_append_new(jColumns, jColumn);
+  }
+  json_object_set_new(jContainer, PATCH_KEY_COLUMNS, jColumns);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-float Patch::getBoost(int c)
+void Patch::writeActions(json_t *jContainer, gVector<action_t> *actions)
 {
-       char tmp[16];
-       sprintf(tmp, "chanBoost%d", c);
-       float out = atof(getValue(tmp).c_str());
-       if (out < 1.0f)
-               return DEFAULT_BOOST;
-       return out;
+  json_t *jActions = json_array();
+  for (unsigned k=0; k<actions->size; k++) {
+    json_t   *jAction = json_object();
+    action_t  action  = actions->at(k);
+    json_object_set_new(jAction, PATCH_KEY_ACTION_TYPE,    json_integer(action.type));
+    json_object_set_new(jAction, PATCH_KEY_ACTION_FRAME,   json_integer(action.frame));
+    json_object_set_new(jAction, PATCH_KEY_ACTION_F_VALUE, json_real(action.fValue));
+    json_object_set_new(jAction, PATCH_KEY_ACTION_I_VALUE, json_integer(action.iValue));
+    json_array_append_new(jActions, jAction);
+  }
+  json_object_set_new(jContainer, PATCH_KEY_CHANNEL_ACTIONS, jActions);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-float Patch::getPanLeft(int c)
+void Patch::writeCommons(json_t *jContainer)
 {
-       char tmp[16];
-       sprintf(tmp, "chanPanLeft%d", c);
-       std::string val = getValue(tmp);
-       if (val == "")
-               return 1.0f;
-
-       float out = atof(val.c_str());
-       if (out < 0.0f || out > 1.0f)
-               return 1.0f;
-       return out;
+  json_object_set_new(jContainer, PATCH_KEY_HEADER,         json_string(header.c_str()));
+  json_object_set_new(jContainer, PATCH_KEY_VERSION,        json_string(version.c_str()));
+  json_object_set_new(jContainer, PATCH_KEY_VERSION_MAJOR,  json_integer(versionMajor));
+  json_object_set_new(jContainer, PATCH_KEY_VERSION_MINOR,  json_integer(versionMinor));
+  json_object_set_new(jContainer, PATCH_KEY_VERSION_PATCH,  json_integer(versionPatch));
+  json_object_set_new(jContainer, PATCH_KEY_NAME,           json_string(name.c_str()));
+  json_object_set_new(jContainer, PATCH_KEY_BPM,            json_real(bpm));
+  json_object_set_new(jContainer, PATCH_KEY_BARS,           json_integer(bars));
+  json_object_set_new(jContainer, PATCH_KEY_BEATS,          json_integer(beats));
+  json_object_set_new(jContainer, PATCH_KEY_QUANTIZE,       json_integer(quantize));
+  json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_IN,  json_real(masterVolIn));
+  json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_OUT, json_real(masterVolOut));
+       json_object_set_new(jContainer, PATCH_KEY_METRONOME,      json_integer(metronome));
+       json_object_set_new(jContainer, PATCH_KEY_LAST_TAKE_ID,   json_integer(lastTakeId));
+       json_object_set_new(jContainer, PATCH_KEY_SAMPLERATE,     json_integer(samplerate));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::getKey(int c)
+void Patch::writeChannels(json_t *jContainer, gVector<channel_t> *channels)
 {
-       if (version == 0.0)      // backward compatibility with version < 0.6.1
-               return 0;
-       char tmp[16];
-       sprintf(tmp, "chanKey%d", c);
-       return atoi(getValue(tmp).c_str());
-}
+  json_t *jChannels = json_array();
+  for (unsigned i=0; i<channels->size; i++) {
+    json_t    *jChannel = json_object();
+    channel_t  channel  = channels->at(i);
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE,                 json_integer(channel.type));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX,                json_integer(channel.index));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN,               json_integer(channel.column));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE,                 json_integer(channel.mute));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S,               json_integer(channel.mute_s));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO,                 json_integer(channel.solo));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME,               json_real(channel.volume));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN_LEFT,             json_real(channel.panLeft));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN_RIGHT,            json_real(channel.panRight));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN,              json_boolean(channel.midiIn));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS,     json_integer(channel.midiInKeyPress));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL,       json_integer(channel.midiInKeyRel));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL,         json_integer(channel.midiInKill));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME,       json_integer(channel.midiInVolume));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE,         json_integer(channel.midiInMute));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO,         json_integer(channel.midiInSolo));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L,           json_boolean(channel.midiOutL));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING,   json_integer(channel.midiOutLplaying));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE,      json_integer(channel.midiOutLmute));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO,      json_integer(channel.midiOutLsolo));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH,          json_string(channel.samplePath.c_str()));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_KEY,                  json_integer(channel.key));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MODE,                 json_integer(channel.mode));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BEGIN,                json_integer(channel.begin));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_END,                  json_integer(channel.end));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BOOST,                json_real(channel.boost));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE,           json_integer(channel.recActive));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PITCH,                json_real(channel.pitch));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, json_integer(channel.midiInReadActions));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH,        json_integer(channel.midiInPitch));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT,             json_integer(channel.midiOut));
+    json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN,        json_integer(channel.midiOutChan));
+    json_array_append_new(jChannels, jChannel);
 
+    writeActions(jChannel, &channel.actions);
 
-/* -------------------------------------------------------------------------- */
+#ifdef WITH_VST
 
+    writePlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
 
-float Patch::getPanRight(int c)
-{
-       char tmp[16];
-       sprintf(tmp, "chanPanRight%d", c);
-       std::string val = getValue(tmp);
-       if (val == "")
-               return 1.0f;
-
-       float out = atof(val.c_str());
-       if (out < 0.0f || out > 1.0f)
-               return 1.0f;
-       return out;
+#endif
+  }
+  json_object_set_new(jContainer, PATCH_KEY_CHANNELS, jChannels);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool Patch::getRecActive(int c)
+bool Patch::readCommons(json_t *jContainer)
 {
-       char tmp[16];
-       sprintf(tmp, "chanRecActive%d", c);
-       return atoi(getValue(tmp).c_str());
+  if (!setString(jContainer, PATCH_KEY_HEADER, header))  return 0;
+  if (!setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
+  if (!setString(jContainer, PATCH_KEY_NAME, name)) return 0;
+  if (!setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_BARS, bars)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_BEATS, beats)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
+  if (!setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
+  if (!setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
+  if (!setInt   (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
+  return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-float Patch::getOutVol()
+bool Patch::readColumns(json_t *jContainer)
 {
-       return atof(getValue("outVol").c_str());
-}
+  json_t *jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
+  if (!checkArray(jColumns, PATCH_KEY_COLUMNS))
+    return 0;
 
+  size_t columnIndex;
+  json_t *jColumn;
+  json_array_foreach(jColumns, columnIndex, jColumn) {
 
-/* -------------------------------------------------------------------------- */
+    string columnIndexStr = "column " + gItoa(columnIndex);
+    if (!checkObject(jColumn, columnIndexStr.c_str()))
+      return 0;
 
+    column_t column;
+    if (!setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
+    if (!setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
 
-float Patch::getInVol()
-{
-       return atof(getValue("inVol").c_str());
+    columns.add(column);
+  }
+  return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-float Patch::getBpm()
+bool Patch::readChannels(json_t *jContainer)
 {
-       float out = atof(getValue("bpm").c_str());
-       if (out < 20.0f || out > 999.0f)
-               return DEFAULT_BPM;
-       return out;
-}
+  json_t *jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
+  if (!checkArray(jChannels, PATCH_KEY_CHANNELS))
+    return 0;
 
+  size_t channelIndex;
+  json_t *jChannel;
+  json_array_foreach(jChannels, channelIndex, jChannel) {
 
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::getBars()
-{
-       int out = atoi(getValue("bars").c_str());
-       if (out <= 0 || out > 32)
-               return DEFAULT_BARS;
-       return out;
-}
+    string channelIndexStr = "channel " + gItoa(channelIndex);
+    if (!checkObject(jChannel, channelIndexStr.c_str()))
+      return 0;
 
+    channel_t channel;
 
-/* -------------------------------------------------------------------------- */
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_TYPE,                 channel.type)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_INDEX,                channel.index)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_COLUMN,               channel.column)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_MUTE,                 channel.mute)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_MUTE_S,               channel.mute_s)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_SOLO,                 channel.solo)) return 0;
+    if (!setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME,               channel.volume)) return 0;
+    if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PAN_LEFT,             channel.panRight)) return 0;
+    if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PAN_RIGHT,            channel.panLeft)) return 0;
+    if (!setBool  (jChannel, PATCH_KEY_CHANNEL_MIDI_IN,              channel.midiIn)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS,     channel.midiInKeyPress)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL,       channel.midiInKeyRel)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL,         channel.midiInKill)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME,       channel.midiInVolume)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE,         channel.midiInMute)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO,         channel.midiInSolo)) return 0;
+    if (!setBool  (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L,           channel.midiOutL)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING,   channel.midiOutLplaying)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE,      channel.midiOutLmute)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO,      channel.midiOutLsolo)) return 0;
+    if (!setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH,          channel.samplePath)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_KEY,                  channel.key)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_MODE,                 channel.mode)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_BEGIN,                channel.begin)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_END,                  channel.end)) return 0;
+    if (!setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST,                channel.boost)) return 0;
+    if (!setInt   (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE,           channel.recActive)) return 0;
+    if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH,                channel.pitch)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH,        channel.midiInPitch)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT,             channel.midiOut)) return 0;
+    if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN,        channel.midiOutChan)) return 0;
 
+    readActions(jChannel, &channel);
 
-int Patch::getBeats()
-{
-       int out = atoi(getValue("beats").c_str());
-       if (out <= 0 || out > 32)
-               return DEFAULT_BEATS;
-       return out;
+#ifdef WITH_VST
+    readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+#endif
+    channels.add(channel);
+  }
+  return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::getQuantize()
+bool Patch::readActions(json_t *jContainer, channel_t *channel)
 {
-       int out = atoi(getValue("quantize").c_str());
-       if (out < 0 || out > 8)
-               return DEFAULT_QUANTIZE;
-       return out;
-}
+  json_t *jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
+  if (!checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
+    return 0;
 
+  size_t actionIndex;
+  json_t *jAction;
+  json_array_foreach(jActions, actionIndex, jAction) {
 
-/* -------------------------------------------------------------------------- */
-
+    if (!checkObject(jAction, "")) // TODO pass actionIndex as string
+      return 0;
 
-bool Patch::getMetronome()
-{
-       return atoi(getValue("metronome").c_str());
+    action_t action;
+    if (!setInt   (jAction, PATCH_KEY_ACTION_TYPE,    action.type)) return 0;
+    if (!setInt   (jAction, PATCH_KEY_ACTION_FRAME,   action.frame)) return 0;
+    if (!setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
+    if (!setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
+    channel->actions.add(action);
+  }
+  return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::getLastTakeId()
-{
-       return atoi(getValue("lastTakeId").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
+#ifdef WITH_VST
 
-int Patch::getSamplerate()
+bool Patch::readPlugins(json_t *jContainer, gVector<plugin_t> *container, const char *key)
 {
-       int out = atoi(getValue("samplerate").c_str());
-       if (out <= 0)
-               return DEFAULT_SAMPLERATE;
-       return out;
-}
-
+  json_t *jPlugins = json_object_get(jContainer, key);
+  if (!checkArray(jPlugins, key))
+    return 0;
 
-/* -------------------------------------------------------------------------- */
+  size_t pluginIndex;
+  json_t *jPlugin;
+  json_array_foreach(jPlugins, pluginIndex, jPlugin) {
 
+    if (!checkObject(jPlugin, "")) // TODO pass pluginIndex as string
+      return 0;
 
-uint32_t Patch::getMidiValue(int i, const char *c)
-{
-       char tmp[32];
-       sprintf(tmp, "chanMidi%s%d", c, i);
-       return strtoul(getValue(tmp).c_str(), NULL, 10);
-}
+    plugin_t plugin;
+    if (!setString (jPlugin, PATCH_KEY_PLUGIN_PATH,   plugin.path)) return 0;
+    if (!setBool   (jPlugin, PATCH_KEY_PLUGIN_BYPASS, plugin.bypass)) return 0;
 
+    /* read plugin params */
 
-/* -------------------------------------------------------------------------- */
+    json_t *jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS);
+    if (!checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
 
+    size_t paramIndex;
+    json_t *jParam;
+    json_array_foreach(jParams, paramIndex, jParam)
+      plugin.params.add(json_real_value(jParam));
 
-int Patch::readRecs()
-{
-       gLog("[patch] Reading recs...\n");
-
-       unsigned numrecs = atoi(getValue("numrecs").c_str());
-
-       for (unsigned i=0; i<numrecs; i++) {
-               int frame, recPerFrame;
-
-               /* parsing 'dddddd d': [framenumber] [num. recs for that frame]  */
-
-               char tmpbuf[16];
-               sprintf(tmpbuf, "recframe%d", i);
-               sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame);
-
-//gLog("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
-
-               for (int k=0; k<recPerFrame; k++) {
-                       int      chan = 0;
-                       int      type = 0;
-                       float    fValue = 0.0f;
-                       int      iValue_fix = 0;
-                       uint32_t iValue = 0;
-
-                       /* reading info for each frame: %d|%d */
-
-                       char tmpbuf[16];
-                       sprintf(tmpbuf, "f%da%d", i, k);
-
-                       if (version < 0.61f)    // no float and int values
-                               sscanf(getValue(tmpbuf).c_str(), "%d|%d", &chan, &type);
-                       else
-                               if (version < 0.83f)  // iValues were stored as signed int (wrong)
-                                       sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%d", &chan, &type, &fValue, &iValue_fix);
-                               else
-                                       sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%u", &chan, &type, &fValue, &iValue);
-
-//gLog("  loading chan=%d, type=%d, fValue=%f, iValue=%u\n", chan, type, fValue, iValue);
-
-                       Channel *ch = G_Mixer.getChannelByIndex(chan);
-                       if (ch)
-                               if (ch->status & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) {
-                                       if (version < 0.83f)
-                                               recorder::rec(ch->index, type, frame, iValue_fix, fValue);
-                                       else
-                                               recorder::rec(ch->index, type, frame, iValue, fValue);
-                               }
-               }
-       }
-       return 1;
+    container->add(plugin);
+  }
+  return 1;
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-int Patch::readPlugins()
-{
-       gLog("[patch] Reading plugins...\n");
-
-       int globalOut = 1;
-
-       /* master plugins */
-
-       globalOut &= readMasterPlugins(PluginHost::MASTER_IN);
-       globalOut &= readMasterPlugins(PluginHost::MASTER_OUT);
-
-       /* channel plugins */
-
-       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
-               Channel *ch = G_Mixer.channels.at(i);
-
-               char tmp[MAX_LINE_LEN];
-               sprintf(tmp, "chan%dPlugins", ch->index);
-               int np = atoi(getValue(tmp).c_str());
-
-               for (int j=0; j<np; j++) {
-                       sprintf(tmp, "chan%d_p%dpathfile", ch->index, j);
-                       int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, ch);
-                       if (out != 0) {
-                               sprintf(tmp, "chan%d_p%dnumParams", ch->index, j);
-                               int nparam = atoi(getValue(tmp).c_str());
-                               Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
-                               sprintf(tmp, "chan%d_p%dbypass", ch->index, j);
-                               pPlugin->bypass = atoi(getValue(tmp).c_str());
-                               for (int k=0; k<nparam; k++) {
-                                       sprintf(tmp, "chan%d_p%dparam%dvalue", ch->index, j, k);
-                                       float pval = atof(getValue(tmp).c_str());
-                                       pPlugin->setParam(k, pval);
-                               }
-                       }
-                       globalOut &= out;
-               }
-       }
-       return globalOut;
-}
 #endif
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::write(const char *file, const char *name, bool project)
+void Patch::sanitize()
 {
-       fp = fopen(file, "w");
-       if (fp == NULL)
-               return 0;
-
-       fprintf(fp, "# --- Giada patch file --- \n");
-       fprintf(fp, "header=GIADAPTC\n");
-       fprintf(fp, "version=%s\n",    VERSIONE);
-       fprintf(fp, "versionf=%f\n",   VERSIONE_FLOAT);
-       fprintf(fp, "patchname=%s\n",  name);
-       fprintf(fp, "bpm=%f\n",        G_Mixer.bpm);
-       fprintf(fp, "bars=%d\n",       G_Mixer.bars);
-       fprintf(fp, "beats=%d\n",      G_Mixer.beats);
-       fprintf(fp, "quantize=%d\n",   G_Mixer.quantize);
-       fprintf(fp, "outVol=%f\n",     G_Mixer.outVol);
-       fprintf(fp, "inVol=%f\n",      G_Mixer.inVol);
-       fprintf(fp, "metronome=%d\n",  G_Mixer.metronome);
-       fprintf(fp, "lastTakeId=%d\n", lastTakeId);
-       fprintf(fp, "samplerate=%d\n", G_Conf.samplerate);      // original samplerate when the patch was saved
-       fprintf(fp, "channels=%d\n",   G_Mixer.channels.size);
-       fprintf(fp, "columns=%d\n",    mainWin->keyboard->getTotalColumns());
-
-       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
-               fprintf(fp, "# --- channel %d --- \n", i);
-               G_Mixer.channels.at(i)->writePatch(fp, i, project);
-       }
-
-       /* writing recs. Warning: channel index is not mixer.channels.at(chan),
-        * but mixer.channels.at(chan)->index! */
-
-       fprintf(fp, "# --- actions --- \n");
-       fprintf(fp, "numrecs=%d\n", recorder::global.size);
-       for (unsigned i=0; i<recorder::global.size; i++) {
-               fprintf(fp, "recframe%d=%d %d\n", i, recorder::frames.at(i), recorder::global.at(i).size);
-               for (unsigned k=0; k<recorder::global.at(i).size; k++) {
-                       fprintf(fp, "f%da%d=%d|%d|%f|%u\n",
-                               i, k,
-                               recorder::global.at(i).at(k)->chan,
-                               recorder::global.at(i).at(k)->type,
-                               recorder::global.at(i).at(k)->fValue,
-                               recorder::global.at(i).at(k)->iValue);
-               }
-       }
+  bpm          = bpm < 20.0f || bpm > 999.0f ? DEFAULT_BPM : bpm;
+  bars         = bars <= 0 || bars > 32 ? DEFAULT_BARS : bars;
+  beats        = beats <= 0 || beats > 32 ? DEFAULT_BEATS : beats;
+  quantize     = quantize < 0 || quantize > 8 ? DEFAULT_QUANTIZE : quantize;
+  masterVolIn  = masterVolIn < 0.0f || masterVolIn > 1.0f ? DEFAULT_VOL : masterVolIn;
+  masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? DEFAULT_VOL : masterVolOut;
+  samplerate   = samplerate <= 0 ? DEFAULT_SAMPLERATE : samplerate;
 
-#ifdef WITH_VST
+  for (unsigned i=0; i<columns.size; i++) {
+    column_t *col = &columns.at(i);
+    col->index = col->index < 0 ? 0 : col->index;
+    col->width = col->width < MIN_COLUMN_WIDTH ? MIN_COLUMN_WIDTH : col->width;
+  }
 
-       /* writing master VST parameters */
-
-       writeMasterPlugins(PluginHost::MASTER_IN);
-       writeMasterPlugins(PluginHost::MASTER_OUT);
-
-       /* writing VST parameters, channels. chan%d is mixer::channels.at(%d)->index,
-        * not mixer::chanels.at(%d)! */
-
-       int numPlugs;
-       int numParams;
-       Plugin *pPlugin;
-
-       fprintf(fp, "# --- VST / channels --- \n");
-       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
-               Channel *ch = G_Mixer.channels.at(i);
-               numPlugs    = G_PluginHost.countPlugins(PluginHost::CHANNEL, ch);
-               fprintf(fp, "chan%dPlugins=%d\n", ch->index, numPlugs);
-
-               for (int j=0; j<numPlugs; j++) {
-                       pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
-                       if (!pPlugin->status) {
-                               gLog("[patch] Plugin %d is in a bad status, skip writing params\n", i);
-                               continue;
-                       }
-                       fprintf(fp, "chan%d_p%dpathfile=%s\n", ch->index, j, pPlugin->pathfile);
-                       fprintf(fp, "chan%d_p%dbypass=%d\n",   ch->index, j, pPlugin->bypass);
-                       numParams = pPlugin->getNumParams();
-                       fprintf(fp, "chan%d_p%dnumParams=%d\n", ch->index, j, numParams);
-
-                       for (int k=0; k<numParams; k++)
-                               fprintf(fp, "chan%d_p%dparam%dvalue=%f\n", ch->index, j, k, pPlugin->getParam(k));
-               }
-       }
-
-#endif
-
-       fclose(fp);
-       return 1;
+  for (unsigned i=0; i<channels.size; i++) {
+    channel_t *ch = &channels.at(i);
+    ch->volume   = ch->volume < 0.0f || ch->volume > 1.0f ? DEFAULT_VOL : ch->volume;
+    ch->panLeft  = ch->panLeft < 0.0f || ch->panLeft > 1.0f ? 1.0f : ch->panLeft;
+    ch->panRight = ch->panRight < 0.0f || ch->panRight > 1.0f ? 1.0f : ch->panRight;
+    ch->boost    = ch->boost < 1.0f ? DEFAULT_BOOST : ch->boost;
+    ch->pitch    = ch->pitch < 0.1f || ch->pitch > 4.0f ? gDEFAULT_PITCH : ch->pitch;
+  }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-#ifdef WITH_VST
 
-int Patch::readMasterPlugins(int type)
+int Patch::setInvalid()
 {
-       int  nmp;
-       char chr;
-       int  res = 1;
-
-       if (type == PluginHost::MASTER_IN) {
-               chr = 'I';
-               nmp = atoi(getValue("masterIPlugins").c_str());
-       }
-       else {
-               chr = 'O';
-               nmp = atoi(getValue("masterOPlugins").c_str());
-       }
-
-       for (int i=0; i<nmp; i++) {
-               char tmp[MAX_LINE_LEN];
-               sprintf(tmp, "master%c_p%dpathfile", chr, i);
-               int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), type);
-               if (out != 0) {
-                       Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
-                       sprintf(tmp, "master%c_p%dbypass", chr, i);
-                       pPlugin->bypass = atoi(getValue(tmp).c_str());
-                       sprintf(tmp, "master%c_p%dnumParams", chr, i);
-                       int nparam = atoi(getValue(tmp).c_str());
-                       for (int j=0; j<nparam; j++) {
-                               sprintf(tmp, "master%c_p%dparam%dvalue", chr, i, j);
-                               float pval = atof(getValue(tmp).c_str());
-                               pPlugin->setParam(j, pval);
-                       }
-               }
-               res &= out;
-       }
-
-       return res;
+  json_decref(jRoot);
+  return PATCH_INVALID;
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Patch::writeMasterPlugins(int type)
-{
-       char chr;
-
-       if (type == PluginHost::MASTER_IN) {
-               fprintf(fp, "# --- VST / master in --- \n");
-               chr = 'I';
-       }
-       else {
-               fprintf(fp, "# --- VST / master out --- \n");
-               chr = 'O';
-       }
-
-       int nmp = G_PluginHost.countPlugins(type);
-       fprintf(fp, "master%cPlugins=%d\n", chr, nmp);
-
-       for (int i=0; i<nmp; i++) {
-
-               Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
-               if (!pPlugin->status) {
-                       gLog("[patch] Plugin %d is in a bad status, skip writing params\n", i);
-                       continue;
-               }
-
-               fprintf(fp, "master%c_p%dpathfile=%s\n", chr, i, pPlugin->pathfile);
-               fprintf(fp, "master%c_p%dbypass=%d\n", chr, i, pPlugin->bypass);
-               int numParams = pPlugin->getNumParams();
-               fprintf(fp, "master%c_p%dnumParams=%d\n", chr, i, numParams);
-
-               for (int j=0; j<numParams; j++)
-                       fprintf(fp, "master%c_p%dparam%dvalue=%f\n", chr, i, j, pPlugin->getParam(j));
-       }
-}
-
-#endif
index e431b85171f3f6ef7f93be5300c6d349b4ead1ae..5d07f0ccaa76a4f3480f203afe09ff9e2a1f866c 100644 (file)
 #ifndef __PATCH_H__
 #define __PATCH_H__
 
-#include <stdio.h>
+
 #include <string>
 #include <stdint.h>
-#include "dataStorage.h"
+#include "../utils/utils.h"
+#include "dataStorageJson.h"
 #include "const.h"
 
 
-class Patch : public DataStorage {
+using std::string;
 
-private:
-       int  readMasterPlugins(int type);
-       void writeMasterPlugins(int type);
 
+class Patch : public DataStorageJson
+{
 public:
 
-       char  name[MAX_PATCHNAME_LEN];
-       float version;
-       int   lastTakeId;
-       int   samplerate;
-
-       int         open(const char *file);
-       void        setDefault();
-       int         close();
-
-       void                            getName       ();
-       int         getNumChans   ();
-       int                                     getNumColumns ();
-       std::string getSamplePath (int i);
-       float       getVol        (int i);
-       int         getMode       (int i);
-       int         getMute       (int i);
-       int         getMute_s     (int i);
-       int         getSolo       (int i);
-       int         getBegin      (int i);
-       int         getEnd        (int i, unsigned sampleSize);
-       float       getBoost      (int i);
-       float       getPanLeft    (int i);
-       float       getPanRight   (int i);
-       float       getPitch      (int i);
-       bool        getRecActive  (int i);
-       int         getColumn     (int i);
-       int         getIndex      (int i);
-       int         getType       (int i);
-       int         getKey        (int i);
-       uint32_t    getMidiValue  (int i, const char *c);
-       float       getOutVol     ();
-       float       getInVol      ();
-       float       getBpm        ();
-       int         getBars       ();
-       int         getBeats      ();
-       int         getQuantize   ();
-       bool        getMetronome  ();
-       int         getLastTakeId ();
-       int         getSamplerate ();
-
-       int         write(const char *file, const char *name, bool isProject);
-       int         readRecs();
+  struct action_t
+  {
+    int      type;
+    int      frame;
+    float    fValue;
+    uint32_t iValue;
+  };
+
+#ifdef WITH_VST
+  struct plugin_t
+  {
+    string         path;
+    bool           bypass;
+    gVector<float> params;
+  };
+#endif
+
+  struct channel_t
+  {
+    int         type;
+    int         index;
+    int         column;
+    int         mute;
+    int         mute_s;
+    int         solo;
+    float       volume;
+    float       panLeft;
+    float       panRight;
+    bool        midiIn;
+    uint32_t    midiInKeyPress;
+    uint32_t    midiInKeyRel;
+    uint32_t    midiInKill;
+    uint32_t    midiInVolume;
+    uint32_t    midiInMute;
+    uint32_t    midiInSolo;
+    bool        midiOutL;
+    uint32_t    midiOutLplaying;
+    uint32_t    midiOutLmute;
+    uint32_t    midiOutLsolo;
+    // sample channel
+    string      samplePath;
+    int         key;
+    int         mode;
+    int         begin;
+    int         end;
+    float       boost;
+    int         recActive;
+    float       pitch;
+    uint32_t    midiInReadActions;
+    uint32_t    midiInPitch;
+    // midi channel
+    uint32_t    midiOut;
+    uint32_t    midiOutChan;
+
+    gVector<action_t> actions;
+
+#ifdef WITH_VST
+    gVector<plugin_t> plugins;
+#endif
+  };
+
+  struct column_t
+  {
+    int index;
+    int width;
+    gVector<int> channels;
+  };
+
+  string header;
+  string version;
+  int    versionMajor;
+  int    versionMinor;
+  int    versionPatch;
+  string name;
+  float  bpm;
+  int    bars;
+  int    beats;
+  int    quantize;
+  float  masterVolIn;
+  float  masterVolOut;
+  int    metronome;
+  int    lastTakeId;
+  int    samplerate;   // original samplerate when the patch was saved
+
+  gVector<column_t>  columns;
+  gVector<channel_t> channels;
+
+#ifdef WITH_VST
+  gVector<plugin_t> masterInPlugins;
+  gVector<plugin_t> masterOutPlugins;
+#endif
+
+  /* init
+   * Init Patch with default values. */
+
+  void init();
+
+  /* read/write
+   * Read/write patch to/from file. */
+
+  int  write(const string &file);
+  int  read (const string &file);
+
+private:
+
+  /* sanitize
+   * Internal sanity check. */
+
+  void sanitize();
+
+  /* setInvalid
+   * Helper function used to return invalid status while reading. */
+
+  int setInvalid();
+
+  /* readers */
+
+  bool readCommons (json_t *jContainer);
+  bool readChannels(json_t *jContainer);
+#ifdef WITH_VST
+  bool readPlugins (json_t *jContainer, gVector<plugin_t> *container, const char* key);
+#endif
+  bool readActions (json_t *jContainer, channel_t *channel);
+  bool readColumns (json_t *jContainer);
+
+  /* writers */
+
+  void writeCommons (json_t *jContainer);
+  void writeChannels(json_t *jContainer, gVector<channel_t> *channels);
 #ifdef WITH_VST
-       int         readPlugins();
+  void writePlugins (json_t *jContainer, gVector<plugin_t> *plugins, const char* key);
 #endif
+  void writeActions (json_t *jContainer, gVector<action_t> *actions);
+  void writeColumns (json_t *jContainer, gVector<column_t> *columns);
 };
 
 #endif
diff --git a/src/core/patch_DEPR_.cpp b/src/core/patch_DEPR_.cpp
new file mode 100644 (file)
index 0000000..0113591
--- /dev/null
@@ -0,0 +1,614 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * patch
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <stdint.h>
+#include "../utils/log.h"
+#include "../utils/utils.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/elems/ge_keyboard.h"
+#include "patch_DEPR_.h"
+#include "init.h"
+#include "recorder.h"
+#include "conf.h"
+#include "pluginHost.h"
+#include "wave.h"
+#include "mixer.h"
+#include "channel.h"
+
+
+extern Mixer                G_Mixer;
+extern Conf                 G_Conf;
+#ifdef WITH_VST
+extern PluginHost    G_PluginHost;
+#endif
+extern gdMainWindow *mainWin;
+
+
+int Patch_DEPR_::open(const char *file)
+{
+       fp = fopen(file, "r");
+       if (fp == NULL)
+               return PATCH_UNREADABLE;
+
+       if (getValue("header") != "GIADAPTC")
+               return PATCH_INVALID;
+
+       version = atof(getValue("versionf").c_str());
+       gLog("[patch_DEPR_] open patch version %f\n", version);
+
+       return PATCH_READ_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Patch_DEPR_::setDefault()
+{
+       name[0]    = '\0';
+  lastTakeId = 0;
+  samplerate = DEFAULT_SAMPLERATE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::close()
+{
+       return fclose(fp);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Patch_DEPR_::getName()
+{
+       std::string out = getValue("patchname");
+       strncpy(name, out.c_str(), MAX_PATCHNAME_LEN);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string Patch_DEPR_::getSamplePath(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "samplepath%d", c);
+       return getValue(tmp);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getPitch(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanPitch%d", c);
+       float out = atof(getValue(tmp).c_str());
+       if (out > 2.0f || out < 0.1f)
+               return 1.0f;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getNumChans()
+{
+       if (version == 0.0)      // backward compatibility with version < 0.6.1
+               return 32;
+       return atoi(getValue("channels").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getNumColumns()
+{
+       return atoi(getValue("columns").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getColumn(int c)
+{
+       if (version == 0.0)      // backward compatibility with version < 0.6.1
+               return 0;
+       char tmp[16];
+       sprintf(tmp, "chanColumn%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getIndex(int c)
+{
+       if (version == 0.0)      // backward compatibility with version < 0.6.1
+               return c;
+
+       char tmp[16];
+       sprintf(tmp, "chanIndex%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getVol(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanvol%d", c);
+       float out = atof(getValue(tmp).c_str());
+       if (out > 1.0f || out < 0.0f)
+               return DEFAULT_VOL;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getMode(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanmode%d", c);
+       int out = atoi(getValue(tmp).c_str());
+       if (out & (LOOP_ANY | SINGLE_ANY))
+               return out;
+       return DEFAULT_CHANMODE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getMute(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanMute%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getMute_s(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanMute_s%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getSolo(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanSolo%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getType(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanType%d", c);
+       int out = atoi(getValue(tmp).c_str());
+       if (out == 0)
+               return CHANNEL_SAMPLE;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getBegin(int c)
+{
+       char tmp[16];
+       if (version < 0.73f)
+               sprintf(tmp, "chanstart%d", c);
+       else
+               sprintf(tmp, "chanBegin%d", c);
+       int out = atoi(getValue(tmp).c_str());
+       if (out < 0)
+               return 0;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getEnd(int c, unsigned size)
+{
+       char tmp[16];
+       sprintf(tmp, "chanend%d", c);
+
+       /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0.
+        * good in theory, a disaster in practice. */
+
+       std::string val = getValue(tmp);
+       if (val == "")
+               return size;
+
+       unsigned out = atoi(val.c_str());
+       if (out <= 0 || out > size)
+               return size;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getBoost(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanBoost%d", c);
+       float out = atof(getValue(tmp).c_str());
+       if (out < 1.0f)
+               return DEFAULT_BOOST;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getPanLeft(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanPanLeft%d", c);
+       std::string val = getValue(tmp);
+       if (val == "")
+               return 1.0f;
+
+       float out = atof(val.c_str());
+       if (out < 0.0f || out > 1.0f)
+               return 1.0f;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getKey(int c)
+{
+       if (version == 0.0)      // backward compatibility with version < 0.6.1
+               return 0;
+       char tmp[16];
+       sprintf(tmp, "chanKey%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getPanRight(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanPanRight%d", c);
+       std::string val = getValue(tmp);
+       if (val == "")
+               return 1.0f;
+
+       float out = atof(val.c_str());
+       if (out < 0.0f || out > 1.0f)
+               return 1.0f;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Patch_DEPR_::getRecActive(int c)
+{
+       char tmp[16];
+       sprintf(tmp, "chanRecActive%d", c);
+       return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getOutVol()
+{
+       return atof(getValue("outVol").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getInVol()
+{
+       return atof(getValue("inVol").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch_DEPR_::getBpm()
+{
+       float out = atof(getValue("bpm").c_str());
+       if (out < 20.0f || out > 999.0f)
+               return DEFAULT_BPM;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getBars()
+{
+       int out = atoi(getValue("bars").c_str());
+       if (out <= 0 || out > 32)
+               return DEFAULT_BARS;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getBeats()
+{
+       int out = atoi(getValue("beats").c_str());
+       if (out <= 0 || out > 32)
+               return DEFAULT_BEATS;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getQuantize()
+{
+       int out = atoi(getValue("quantize").c_str());
+       if (out < 0 || out > 8)
+               return DEFAULT_QUANTIZE;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Patch_DEPR_::getMetronome()
+{
+       return atoi(getValue("metronome").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getLastTakeId()
+{
+       return atoi(getValue("lastTakeId").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::getSamplerate()
+{
+       int out = atoi(getValue("samplerate").c_str());
+       if (out <= 0)
+               return DEFAULT_SAMPLERATE;
+       return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+uint32_t Patch_DEPR_::getMidiValue(int i, const char *c)
+{
+       char tmp[32];
+       sprintf(tmp, "chanMidi%s%d", c, i);
+       return strtoul(getValue(tmp).c_str(), NULL, 10);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch_DEPR_::readRecs()
+{
+       gLog("[patch_DEPR_] Reading recs...\n");
+
+       unsigned numrecs = atoi(getValue("numrecs").c_str());
+
+       for (unsigned i=0; i<numrecs; i++) {
+               int frame, recPerFrame;
+
+               /* parsing 'dddddd d': [framenumber] [num. recs for that frame]  */
+
+               char tmpbuf[16];
+               sprintf(tmpbuf, "recframe%d", i);
+               sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame);
+
+//gLog("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
+
+               for (int k=0; k<recPerFrame; k++) {
+                       int      chan = 0;
+                       int      type = 0;
+                       float    fValue = 0.0f;
+                       int      iValue_fix = 0;
+                       uint32_t iValue = 0;
+
+                       /* reading info for each frame: %d|%d */
+
+                       char tmpbuf[16];
+                       sprintf(tmpbuf, "f%da%d", i, k);
+
+                       if (version < 0.61f)    // no float and int values
+                               sscanf(getValue(tmpbuf).c_str(), "%d|%d", &chan, &type);
+                       else
+                               if (version < 0.83f)  // iValues were stored as signed int (wrong)
+                                       sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%d", &chan, &type, &fValue, &iValue_fix);
+                               else
+                                       sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%u", &chan, &type, &fValue, &iValue);
+
+//gLog("  loading chan=%d, type=%d, fValue=%f, iValue=%u\n", chan, type, fValue, iValue);
+
+                       Channel *ch = G_Mixer.getChannelByIndex(chan);
+                       if (ch)
+                               if (ch->status & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) {
+                                       if (version < 0.83f)
+                                               recorder::rec(ch->index, type, frame, iValue_fix, fValue);
+                                       else
+                                               recorder::rec(ch->index, type, frame, iValue, fValue);
+                               }
+               }
+       }
+       return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+int Patch_DEPR_::readPlugins()
+{
+       gLog("[patch_DEPR_] Reading plugins...\n");
+
+       int globalOut = 1;
+
+       /* master plugins */
+
+       globalOut &= readMasterPlugins(PluginHost::MASTER_IN);
+       globalOut &= readMasterPlugins(PluginHost::MASTER_OUT);
+
+       /* channel plugins */
+
+       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+               Channel *ch = G_Mixer.channels.at(i);
+
+               char tmp[MAX_LINE_LEN];
+               sprintf(tmp, "chan%dPlugins", ch->index);
+               int np = atoi(getValue(tmp).c_str());
+
+               for (int j=0; j<np; j++) {
+                       sprintf(tmp, "chan%d_p%dpathfile", ch->index, j);
+                       Plugin *plugin = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, ch);
+                       if (plugin != NULL) {
+                               sprintf(tmp, "chan%d_p%dnumParams", ch->index, j);
+                               int nparam = atoi(getValue(tmp).c_str());
+                               Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
+                               sprintf(tmp, "chan%d_p%dbypass", ch->index, j);
+                               pPlugin->bypass = atoi(getValue(tmp).c_str());
+                               for (int k=0; k<nparam; k++) {
+                                       sprintf(tmp, "chan%d_p%dparam%dvalue", ch->index, j, k);
+                                       float pval = atof(getValue(tmp).c_str());
+                                       pPlugin->setParam(k, pval);
+                               }
+                               globalOut &= 1;
+                       }
+                       else
+                               globalOut &= 0;
+               }
+       }
+       return globalOut;
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef WITH_VST
+
+int Patch_DEPR_::readMasterPlugins(int type)
+{
+       int  nmp;
+       char chr;
+       int  res = 1;
+
+       if (type == PluginHost::MASTER_IN) {
+               chr = 'I';
+               nmp = atoi(getValue("masterIPlugins").c_str());
+       }
+       else {
+               chr = 'O';
+               nmp = atoi(getValue("masterOPlugins").c_str());
+       }
+
+       for (int i=0; i<nmp; i++) {
+               char tmp[MAX_LINE_LEN];
+               sprintf(tmp, "master%c_p%dpathfile", chr, i);
+               Plugin *p = G_PluginHost.addPlugin(getValue(tmp).c_str(), type);
+               if (p != NULL) {
+                       Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
+                       sprintf(tmp, "master%c_p%dbypass", chr, i);
+                       pPlugin->bypass = atoi(getValue(tmp).c_str());
+                       sprintf(tmp, "master%c_p%dnumParams", chr, i);
+                       int nparam = atoi(getValue(tmp).c_str());
+                       for (int j=0; j<nparam; j++) {
+                               sprintf(tmp, "master%c_p%dparam%dvalue", chr, i, j);
+                               float pval = atof(getValue(tmp).c_str());
+                               pPlugin->setParam(j, pval);
+                       }
+                       res &= 1;
+               }
+               else
+                       res &= 0;
+       }
+
+       return res;
+}
+
+#endif
diff --git a/src/core/patch_DEPR_.h b/src/core/patch_DEPR_.h
new file mode 100644 (file)
index 0000000..9c08bd8
--- /dev/null
@@ -0,0 +1,94 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * patch
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef __PATCH_DEPR_H__
+#define __PATCH_DEPR_H__
+
+#include <stdio.h>
+#include <string>
+#include <stdint.h>
+#include "dataStorageIni.h"
+#include "const.h"
+
+
+class Patch_DEPR_ : public DataStorageIni
+{
+private:
+
+       int readMasterPlugins(int type);
+
+public:
+
+       char  name[MAX_PATCHNAME_LEN];
+       float version;
+       int   lastTakeId;
+       int   samplerate;
+
+       int         open(const char *file);
+       void        setDefault();
+       int         close();
+
+       void                            getName       ();
+       int         getNumChans   ();
+       int                                     getNumColumns ();
+       std::string getSamplePath (int i);
+       float       getVol        (int i);
+       int         getMode       (int i);
+       int         getMute       (int i);
+       int         getMute_s     (int i);
+       int         getSolo       (int i);
+       int         getBegin      (int i);
+       int         getEnd        (int i, unsigned sampleSize);
+       float       getBoost      (int i);
+       float       getPanLeft    (int i);
+       float       getPanRight   (int i);
+       float       getPitch      (int i);
+       bool        getRecActive  (int i);
+       int         getColumn     (int i);
+       int         getIndex      (int i);
+       int         getType       (int i);
+       int         getKey        (int i);
+       uint32_t    getMidiValue  (int i, const char *c);
+       float       getOutVol     ();
+       float       getInVol      ();
+       float       getBpm        ();
+       int         getBars       ();
+       int         getBeats      ();
+       int         getQuantize   ();
+       bool        getMetronome  ();
+       int         getLastTakeId ();
+       int         getSamplerate ();
+
+       int         readRecs();
+#ifdef WITH_VST
+       int         readPlugins();
+#endif
+};
+
+#endif
index 620205074f8795be9878e1b0aad307df94948715..6b02b890243b2eb3673573342b8a77f3f58f0a77 100644 (file)
@@ -253,7 +253,7 @@ VstIntPtr PluginHost::gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 i
                /* 33 - product version */
 
                case audioMasterGetVendorVersion:
-                       return (int) VERSIONE_FLOAT * 100;
+                       return (int) (G_VERSION_MAJOR * 100) + (G_VERSION_MINOR * 10) + G_VERSION_PATCH;
 
 
                /* 37 - Plugin asks Host if it implements the feature text. */
@@ -308,7 +308,7 @@ VstIntPtr PluginHost::gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 i
 /* ------------------------------------------------------------------ */
 
 
-int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
+Plugin *PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
 
        Plugin *p    = new Plugin();
        bool success = true;
@@ -327,7 +327,7 @@ int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
 
        if (!success) {
                pStack->add(p);
-               return 0;
+               return NULL;
        }
 
        /* otherwise let's try to initialize it. */
@@ -338,7 +338,7 @@ int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
 
                if (!p->init(&PluginHost::HostCallback)) {
                        delete p;
-                       return 0;
+                       return NULL;
                }
 
                /* plugin setup */
@@ -364,7 +364,7 @@ int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
 
                p->resume();
 
-               return 1;
+               return p;
        }
 }
 
index 58907ad1c65b0f320950213cd7589ed483c2f3f4..71d4d847e12b94a3eb084815d314a5891b0a2a44 100644 (file)
@@ -90,7 +90,11 @@ public:
        static VstIntPtr VSTCALLBACK HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt);
        VstIntPtr gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt);
 
-       int addPlugin(const char *fname, int stackType, class Channel *ch=NULL);
+       /* addPlugin
+        * Add a new plugin to the stack. If the operation goes well, returns a pointer
+        * to the newly inserted plugin. */
+
+       Plugin *addPlugin(const char *fname, int stackType, class Channel *ch=NULL);
 
        void processEvents(float *buffer, class Channel *ch);
 
index 73d9b38f93ec0b56c9295eb30701302825a393a4..58324f2c35bfd86ad41e700f613746804c5e5827 100644 (file)
@@ -36,7 +36,7 @@
 #include "kernelAudio.h"
 #include "pluginHost.h"
 #include "kernelMidi.h"
-#include "patch.h"
+#include "patch_DEPR_.h"
 #include "conf.h"
 #include "channel.h"
 #include "sampleChannel.h"
@@ -49,9 +49,9 @@ extern PluginHost G_PluginHost;
 #endif
 
 
-extern Mixer G_Mixer;
-extern Patch f_patch;
-extern Conf     G_Conf;
+extern Mixer       G_Mixer;
+extern Patch_DEPR_ f_patch;
+extern Conf           G_Conf;
 
 
 namespace recorder
index 168b581024905cc2ea0f778a0315aecca05b574b..ad5866f0a79ae586d425aef5b8fe95c88a8291e2 100644 (file)
@@ -30,6 +30,7 @@
 #include <math.h>
 #include "../utils/log.h"
 #include "sampleChannel.h"
+#include "patch_DEPR_.h"
 #include "patch.h"
 #include "conf.h"
 #include "wave.h"
@@ -39,6 +40,7 @@
 #include "kernelMidi.h"
 
 
+extern Patch_DEPR_ G_Patch_DEPR_;
 extern Patch       G_Patch;
 extern Mixer       G_Mixer;
 extern Conf        G_Conf;
@@ -824,32 +826,32 @@ int SampleChannel::load(const char *file)
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::loadByPatch(const char *f, int i)
+int SampleChannel::readPatch_DEPR_(const char *f, int i)
 {
        int res = load(f);
 
-               volume      = G_Patch.getVol(i);
-               key         = G_Patch.getKey(i);
-               index       = G_Patch.getIndex(i);
-               mode        = G_Patch.getMode(i);
-               mute        = G_Patch.getMute(i);
-               mute_s      = G_Patch.getMute_s(i);
-               solo        = G_Patch.getSolo(i);
-               boost       = G_Patch.getBoost(i);
-               panLeft     = G_Patch.getPanLeft(i);
-               panRight    = G_Patch.getPanRight(i);
-               readActions = G_Patch.getRecActive(i);
+               volume      = G_Patch_DEPR_.getVol(i);
+               key         = G_Patch_DEPR_.getKey(i);
+               index       = G_Patch_DEPR_.getIndex(i);
+               mode        = G_Patch_DEPR_.getMode(i);
+               mute        = G_Patch_DEPR_.getMute(i);
+               mute_s      = G_Patch_DEPR_.getMute_s(i);
+               solo        = G_Patch_DEPR_.getSolo(i);
+               boost       = G_Patch_DEPR_.getBoost(i);
+               panLeft     = G_Patch_DEPR_.getPanLeft(i);
+               panRight    = G_Patch_DEPR_.getPanRight(i);
+               readActions = G_Patch_DEPR_.getRecActive(i);
                recStatus   = readActions ? REC_READING : REC_STOPPED;
 
-               readPatchMidiIn(i);
-               midiInReadActions = G_Patch.getMidiValue(i, "InReadActions");
-               midiInPitch       = G_Patch.getMidiValue(i, "InPitch");
-               readPatchMidiOut(i);
+               readPatchMidiIn_DEPR_(i);
+               midiInReadActions = G_Patch_DEPR_.getMidiValue(i, "InReadActions");
+               midiInPitch       = G_Patch_DEPR_.getMidiValue(i, "InPitch");
+               readPatchMidiOut_DEPR_(i);
 
        if (res == SAMPLE_LOADED_OK) {
-               setBegin(G_Patch.getBegin(i));
-               setEnd  (G_Patch.getEnd(i, wave->size));
-               setPitch(G_Patch.getPitch(i));
+               setBegin(G_Patch_DEPR_.getBegin(i));
+               setEnd  (G_Patch_DEPR_.getEnd(i, wave->size));
+               setPitch(G_Patch_DEPR_.getPitch(i));
        }
        else {
                // volume = DEFAULT_VOL;
@@ -872,6 +874,44 @@ int SampleChannel::loadByPatch(const char *f, int i)
 /* -------------------------------------------------------------------------- */
 
 
+int SampleChannel::readPatch(const string &basePath, int i)
+{
+       /* load channel's data first: if the sample is missing or wrong, the channel
+        * is not completely blank. */
+
+       Channel::readPatch("", i);
+
+       Patch::channel_t *pch = &G_Patch.channels.at(i);
+
+       mode              = pch->mode;
+       boost             = pch->boost;
+       readActions       = pch->recActive;
+       recStatus         = readActions ? REC_READING : REC_STOPPED;
+       midiInReadActions = pch->midiInReadActions;
+       midiInPitch       = pch->midiInPitch;
+
+       int res = load((basePath + pch->samplePath).c_str());
+       if (res == SAMPLE_LOADED_OK) {
+               setBegin(pch->begin);
+               setEnd  (pch->end);
+               setPitch(pch->pitch);
+       }
+       else {
+               if (res == SAMPLE_LEFT_EMPTY)
+                       status = STATUS_EMPTY;
+               else
+               if (res == SAMPLE_READ_ERROR)
+                       status = STATUS_MISSING;
+               sendMidiLplay();  // FIXME - why sending MIDI lightning if sample status is wrong?
+       }
+
+       return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 bool SampleChannel::canInputRec()
        {
        return wave == NULL;
@@ -953,29 +993,29 @@ void SampleChannel::start(int frame, bool doQuantize)
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::writePatch(FILE *fp, int i, bool isProject)
+int SampleChannel::writePatch(int i, bool isProject)
 {
-       Channel::writePatch(fp, i, isProject);
+       int pchIndex = Channel::writePatch(i, isProject);
+       Patch::channel_t *pch = &G_Patch.channels.at(pchIndex);
 
-       std::string path;
        if (wave != NULL) {
-               path = wave->pathfile;
+               pch->samplePath = wave->pathfile;
                if (isProject)
-                       path = gBasename(path);  // make it portable
+                       pch->samplePath = gBasename(wave->pathfile);  // make it portable
        }
-
-       fprintf(fp, "samplepath%d=%s\n",     i, path.c_str());
-       fprintf(fp, "chanKey%d=%d\n",        i, key);
-       //fprintf(fp, "columnIndex%d=%d\n",    i, index);
-       fprintf(fp, "chanmode%d=%d\n",       i, mode);
-       fprintf(fp, "chanBegin%d=%d\n",      i, begin);
-       fprintf(fp, "chanend%d=%d\n",        i, end);
-       fprintf(fp, "chanBoost%d=%f\n",      i, boost);
-       fprintf(fp, "chanRecActive%d=%d\n",  i, readActions);
-       fprintf(fp, "chanPitch%d=%f\n",      i, pitch);
-
-       fprintf(fp, "chanMidiInReadActions%d=%u\n", i, midiInReadActions);
-       fprintf(fp, "chanMidiInPitch%d=%u\n",       i, midiInPitch);
+       else
+               pch->samplePath = "";
+
+       pch->mode              = mode;
+       pch->begin             = begin;
+       pch->end               = end;
+       pch->boost             = boost;
+       pch->recActive         = readActions;
+       pch->pitch             = pitch;
+       pch->midiInReadActions = midiInReadActions;
+       pch->midiInPitch       = midiInPitch;
+
+       return 0;
 }
 
 
index 736063ad067f3d70514a51e4ea2f27c9bc9873f0..228a1b531295ed0b5280b5f1569325ca5340f506 100644 (file)
@@ -83,24 +83,25 @@ public:
        SampleChannel(int bufferSize);
        ~SampleChannel();
 
-       void  clear      ();
-       void  process    (float *buffer);
-       void  start      (int frame, bool doQuantize);
-       void  kill       (int frame);
-       void  empty      ();
-       void  stopBySeq  ();
-       void  stop       ();
-       void  rewind     ();
-       void  setMute    (bool internal);
-       void  unsetMute  (bool internal);
-       void  reset      (int frame);
-       int   load       (const char *file);
-       int   loadByPatch(const char *file, int i);
-       void  writePatch (FILE *fp, int i, bool isProject);
-       void  quantize   (int index, int localFrame, int globalFrame);
-       void  onZero     (int frame);
-       void  onBar      (int frame);
-       void  parseAction(recorder::action *a, int localFrame, int globalFrame);
+       void clear      ();
+       void process    (float *buffer);
+       void start      (int frame, bool doQuantize);
+       void kill       (int frame);
+       void empty      ();
+       void stopBySeq  ();
+       void stop       ();
+       void rewind     ();
+       void setMute    (bool internal);
+       void unsetMute  (bool internal);
+       void reset      (int frame);
+       int  load       (const char *file);
+       int  readPatch_DEPR_  (const char *file, int i);
+  int  readPatch  (const string &basePath, int i);
+       int  writePatch (int i, bool isProject);
+       void quantize   (int index, int localFrame, int globalFrame);
+       void onZero     (int frame);
+       void onBar      (int frame);
+       void parseAction(recorder::action *a, int localFrame, int globalFrame);
 
        /* fade methods
         * prepare channel for fade, mixer will take care of the process
index c2ec2c569176453995c0488c9d6e35d703d6f7ee..d7db772d6d720caca37ba61c15a881ce3edef282 100644 (file)
 #include "../core/sampleChannel.h"
 #include "../core/midiChannel.h"
 #include "../core/kernelMidi.h"
-#include "../core/patch.h"
+#include "../core/patch_DEPR_.h"
 #include "../core/conf.h"
 #include "glue.h"
 
 
 extern gdMainWindow *mainWin;
 extern Mixer                    G_Mixer;
-extern Patch                    G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern Conf                             G_Conf;
 extern bool                             G_audio_status;
 #ifdef WITH_VST
@@ -106,110 +106,6 @@ Channel *glue_addChannel(int column, int type)
 /* -------------------------------------------------------------------------- */
 
 
-int glue_loadPatch(const char *fname, const char *fpath, gProgress *status, bool isProject)
-{
-       /* update browser's status bar with % 0.1 */
-
-       status->show();
-       status->value(0.1f);
-       //Fl::check();
-       Fl::wait(0);
-
-       /* is it a valid patch? */
-
-       int res = G_Patch.open(fname);
-       if (res != PATCH_OPEN_OK)
-               return res;
-
-       /* close all other windows. This prevents segfault if plugin windows
-        * GUI are on. */
-
-       if (res)
-               gu_closeAllSubwindows();
-
-       /* reset the system. False(1): don't update the gui right now. False(2): do
-        * not create empty columns. */
-
-       glue_resetToInitState(false, false);
-
-       status->value(0.2f);  // progress status: % 0.2
-       //Fl::check();
-       Fl::wait(0);
-
-       /* mixerHandler will update the samples inside Mixer */
-
-       mh_loadPatch(isProject, fname);
-
-       /* take the patch name and update the main window's title */
-
-       G_Patch.getName();
-       gu_update_win_label(G_Patch.name);
-
-       status->value(0.4f);  // progress status: 0.4
-       //Fl::check();
-       Fl::wait(0);
-
-       G_Patch.readRecs();
-       status->value(0.6f);  // progress status: 0.6
-       //Fl::check();
-       Fl::wait(0);
-
-#ifdef WITH_VST
-       int resPlugins = G_Patch.readPlugins();
-       status->value(0.8f);  // progress status: 0.8
-       //Fl::check();
-       Fl::wait(0);
-#endif
-
-       /* this one is vital: let recorder recompute the actions' positions if
-        * the current samplerate != patch samplerate */
-
-       recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
-
-       /* update gui */
-
-       gu_updateControls();
-
-       status->value(1.0f);  // progress status: 1.0 (done)
-       //Fl::check();
-       Fl::wait(0);
-
-       /* save patchPath by taking the last dir of the broswer, in order to
-        * reuse it the next time */
-
-       G_Conf.setPath(G_Conf.patchPath, fpath);
-
-       gLog("[glue] patch %s loaded\n", fname);
-
-#ifdef WITH_VST
-       if (resPlugins != 1)
-               gdAlert("Some VST plugins were not loaded successfully.");
-#endif
-
-       return res;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int glue_savePatch(const char *fullpath, const char *name, bool isProject)
-{
-       if (G_Patch.write(fullpath, name, isProject) == 1) {
-               strcpy(G_Patch.name, name);
-               G_Patch.name[strlen(name)] = '\0';
-               gu_update_win_label(name);
-               gLog("[glue] patch saved as %s\n", fullpath);
-               return 1;
-       }
-       else
-               return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void glue_deleteChannel(Channel *ch)
 {
        int index = ch->index;
@@ -596,23 +492,20 @@ void glue_clearAllRecs()
 
 void glue_resetToInitState(bool resetGui, bool createColumns)
 {
-       G_Mixer.ready = false;
-
-       mh_clear();
-       mainWin->keyboard->clear();
-       if (createColumns)
-               mainWin->keyboard->init();
-       recorder::init();
-       G_Patch.setDefault();
+       G_Patch_DEPR_.setDefault();
+       G_Mixer.close();
        G_Mixer.init();
+       recorder::init();
 #ifdef WITH_VST
        G_PluginHost.freeAllStacks();
 #endif
 
+       mainWin->keyboard->clear();
+       if (createColumns)
+               mainWin->keyboard->init();
+
        if (resetGui)
                gu_updateControls();
-
-       G_Mixer.ready = true;
 }
 
 
@@ -976,55 +869,6 @@ int glue_stopInputRec(bool gui)
 /* -------------------------------------------------------------------------- */
 
 
-int glue_saveProject(const char *folderPath, const char *projName)
-{
-       if (gIsProject(folderPath)) {
-               gLog("[glue] the project folder already exists\n");
-               // don't exit
-       }
-       else if (!gMkdir(folderPath)) {
-               gLog("[glue] unable to make project directory!\n");
-               return 0;
-       }
-
-       /* copy all samples inside the folder. Takes and logical ones are saved
-        * via glue_saveSample() */
-
-       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
-
-               if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
-
-                       SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
-
-                       if (ch->wave == NULL)
-                               continue;
-
-                       /* update the new samplePath: everything now comes from the
-                        * project folder (folderPath) */
-
-                       char samplePath[PATH_MAX];
-                       sprintf(samplePath, "%s%s%s.%s", folderPath, gGetSlash().c_str(), ch->wave->basename().c_str(), ch->wave->extension().c_str());
-
-                       /* remove any existing file */
-
-                       if (gFileExists(samplePath))
-                               remove(samplePath);
-                       if (ch->save(samplePath))
-                               ch->wave->pathfile = samplePath;
-               }
-       }
-
-       char gptcPath[PATH_MAX];
-       sprintf(gptcPath, "%s%s%s.gptc", folderPath, gGetSlash().c_str(), gStripExt(projName).c_str());
-       glue_savePatch(gptcPath, projName, true); // true == it's a project
-
-       return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void glue_keyPress(Channel *ch, bool ctrl, bool shift)
 {
        if (ch->type == CHANNEL_SAMPLE)
index d018a6cf42b328d5eee2b259cc065da58e38a14a..8f085de389ff30a548a6c77c7f6b749ff12fc5ae 100644 (file)
@@ -49,10 +49,6 @@ void glue_deleteChannel(class Channel *ch);
 
 void glue_freeChannel(class Channel *ch);
 
-/** FIXME - nobody will call these via MIDI/keyb/mouse! */
-int glue_loadPatch(const char *fname, const char *fpath, class gProgress *status, bool isProject);
-int glue_savePatch(const char *fullpath, const char *name, bool isProject);
-
 /* keyPress / keyRelease
  * handle the key pressure, either via mouse/keyboard or MIDI. If gui
  * is true it means that the event comes from the main window (mouse,
@@ -156,10 +152,6 @@ void glue_setMute(class Channel *ch, bool gui=true);
 void glue_setSoloOn (class Channel *ch, bool gui=true);
 void glue_setSoloOff(class Channel *ch, bool gui=true);
 
-/** FIXME - nobody will call this via MIDI/keyb/mouse! */
-int glue_saveProject(const char *folderPath, const char *projName);
-
-
 /* beatsDivide/Multiply
  * shrinks or enlarges the number of beats by 2. */
 
diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp
new file mode 100644 (file)
index 0000000..5837227
--- /dev/null
@@ -0,0 +1,382 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../gui/elems/ge_column.h"
+#include "../gui/elems/ge_keyboard.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../core/mixer.h"
+#include "../core/mixerHandler.h"
+#include "../core/channel.h"
+#include "../core/pluginHost.h"
+#include "../core/conf.h"
+#include "../core/patch.h"
+#include "../core/patch_DEPR_.h" // TODO - remove, used only for DEPR calls
+#include "../core/sampleChannel.h"
+#include "../core/midiChannel.h"
+#include "../core/wave.h"
+#include "../utils/gui_utils.h"
+#include "glue.h" // TODO - remove, used only for DEPR calls
+#include "storage.h"
+
+
+using std::string;
+
+
+extern gdMainWindow *mainWin;
+extern Mixer                    G_Mixer;
+extern Patch         G_Patch;
+extern Conf          G_Conf;
+extern Patch_DEPR_   G_Patch_DEPR_; // TODO - remove, used only for DEPR calls
+#ifdef WITH_VST
+extern PluginHost               G_PluginHost;
+#endif
+
+
+int glue_savePatch(const string &fullPath, const string &name, bool isProject)
+{
+       G_Patch.init();
+
+       __glue_fillPatchGlobals__(name);
+       __glue_fillPatchChannels__(isProject);
+       __glue_fillPatchColumns__();
+
+       if (G_Patch.write(fullPath)) {
+               gu_update_win_label(name.c_str());
+               gLog("[glue] patch saved as %s\n", fullPath.c_str());
+               return 1;
+       }
+       return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_loadPatch(const string &fullPath, class gProgress *status, bool isProject)
+{
+       /* try to load the new JSON-based patch. If it fails, fall back to deprecated
+       * one. */
+
+       gLog("[glue] loading %s...\n", fullPath.c_str());
+
+       string fileToLoad = fullPath;  // patch file to read from
+       string basePath   = "";        // base path, in case of reading from a project
+       if (isProject) {
+               fileToLoad = fullPath + gGetSlash() + gStripExt(gBasename(fullPath)) + ".gptc";
+               basePath   = fullPath.c_str() + gGetSlash();
+       }
+
+       int res = G_Patch.read(fileToLoad);
+
+       if (res == PATCH_UNREADABLE) {
+               gLog("[glue] failed reading JSON-based patch. Trying with the deprecated method\n");
+               return glue_loadPatch__DEPR__(gBasename(fileToLoad).c_str(), fileToLoad.c_str(), status, isProject);
+       }
+       if (res != PATCH_READ_OK)
+               return res;
+
+       /* close all other windows. This prevents segfault if plugin
+        * windows GUIs are on. */
+
+       gu_closeAllSubwindows();
+
+       /* reset the system. False(1): don't update the gui right now. False(2): do
+        * not create empty columns. */
+
+       glue_resetToInitState(false, false);
+
+       __setProgressBar__(status, 0.1f);
+
+       /* Add common stuff, columns and channels. Also increment the progress bar
+        * by 0.8 / total_channels steps.  */
+
+       float steps = 0.8 / G_Patch.channels.size;
+       for (unsigned i=0; i<G_Patch.columns.size; i++) {
+               Patch::column_t *col = &G_Patch.columns.at(i);
+               mainWin->keyboard->addColumn(col->width);
+               for (unsigned k=0; k<G_Patch.channels.size; k++) {
+                       if (G_Patch.channels.at(k).column == col->index) {
+                               Channel *ch = glue_addChannel(G_Patch.channels.at(k).column, G_Patch.channels.at(k).type);
+                               ch->readPatch(basePath, k);
+                       }
+                       __setProgressBar__(status, steps);
+               }
+       }
+
+       /* fill Mixer */
+
+       mh_readPatch();
+
+       /* let recorder recompute the actions' positions if the current
+        * samplerate != patch samplerate */
+
+       recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
+
+       /* save patchPath by taking the last dir of the broswer, in order to
+        * reuse it the next time */
+
+       G_Conf.setPath(G_Conf.patchPath, gDirname(fullPath.c_str()).c_str());
+
+       /* refresh GUI */
+
+       gu_updateControls();
+       gu_update_win_label(G_Patch.name.c_str());
+
+       __setProgressBar__(status, 1.0f);
+
+       gLog("[glue] patch loaded successfully\n");
+
+       return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_loadPatch__DEPR__(const char *fname, const char *fpath, gProgress *status, bool isProject)
+{
+       /* update browser's status bar with % 0.1 */
+
+       status->show();
+       status->value(0.1f);
+       //Fl::check();
+       Fl::wait(0);
+
+       /* is it a valid patch? */
+
+       int res = G_Patch_DEPR_.open(fpath);
+       if (res != PATCH_READ_OK)
+               return res;
+
+       /* close all other windows. This prevents segfault if plugin windows
+        * GUI are on. */
+
+       if (res)
+               gu_closeAllSubwindows();
+
+       /* reset the system. False(1): don't update the gui right now. False(2): do
+        * not create empty columns. */
+
+       glue_resetToInitState(false, false);
+
+       status->value(0.2f);  // progress status: % 0.2
+       //Fl::check();
+       Fl::wait(0);
+
+       /* mixerHandler will update the samples inside Mixer */
+
+       mh_loadPatch_DEPR_(isProject, gDirname(fpath).c_str());
+
+       /* take the patch name and update the main window's title */
+
+       G_Patch_DEPR_.getName();
+       gu_update_win_label(G_Patch_DEPR_.name);
+
+       status->value(0.4f);  // progress status: 0.4
+       //Fl::check();
+       Fl::wait(0);
+
+       G_Patch_DEPR_.readRecs();
+       status->value(0.6f);  // progress status: 0.6
+       //Fl::check();
+       Fl::wait(0);
+
+#ifdef WITH_VST
+       int resPlugins = G_Patch_DEPR_.readPlugins();
+       status->value(0.8f);  // progress status: 0.8
+       //Fl::check();
+       Fl::wait(0);
+#endif
+
+       /* this one is vital: let recorder recompute the actions' positions if
+        * the current samplerate != patch samplerate */
+
+       recorder::updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate);
+
+       /* update gui */
+
+       gu_updateControls();
+
+       status->value(1.0f);  // progress status: 1.0 (done)
+       //Fl::check();
+       Fl::wait(0);
+
+       /* save patchPath by taking the last dir of the broswer, in order to
+        * reuse it the next time */
+
+       G_Conf.setPath(G_Conf.patchPath, gDirname(fpath).c_str());
+
+       gLog("[glue] patch %s loaded\n", fname);
+
+#ifdef WITH_VST
+       if (resPlugins != 1)
+               gdAlert("Some VST plugins were not loaded successfully.");
+#endif
+
+       return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void __glue_fillPatchGlobals__(const string &name)
+{
+       G_Patch.version      = G_VERSION_STR;
+       G_Patch.versionMajor = G_VERSION_MAJOR;
+       G_Patch.versionMinor = G_VERSION_MINOR;
+       G_Patch.versionPatch = G_VERSION_PATCH;
+       G_Patch.name         = name;
+       G_Patch.bpm          = G_Mixer.bpm;
+       G_Patch.bars         = G_Mixer.bars;
+       G_Patch.beats        = G_Mixer.beats;
+       G_Patch.quantize     = G_Mixer.quantize;
+       G_Patch.masterVolIn  = G_Mixer.inVol;
+  G_Patch.masterVolOut = G_Mixer.outVol;
+  G_Patch.metronome    = G_Mixer.metronome;
+
+#ifdef WITH_VST
+
+       __glue_fillPatchGlobalsPlugins__(&G_PluginHost.masterIn, &G_Patch.masterInPlugins);
+       __glue_fillPatchGlobalsPlugins__(&G_PluginHost.masterOut, &G_Patch.masterOutPlugins);
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+void __glue_fillPatchGlobalsPlugins__(gVector <Plugin *> *host, gVector<Patch::plugin_t> *patch)
+{
+       for (unsigned i=0; i<host->size; i++) {
+               Plugin *pl = host->at(i);
+               Patch::plugin_t ppl;
+               ppl.path = pl->pathfile;
+               ppl.bypass = pl->bypass;
+               int numParams = pl->getNumParams();
+               for (unsigned k=0; k<numParams; k++)
+                       ppl.params.add(pl->getParam(k));
+               patch->add(ppl);
+       }
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void __glue_fillPatchChannels__(bool isProject)
+{
+       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+               G_Mixer.channels.at(i)->writePatch(i, isProject);
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void __glue_fillPatchColumns__()
+{
+       for (unsigned i=0; i<mainWin->keyboard->getTotalColumns(); i++) {
+               gColumn *gCol = mainWin->keyboard->getColumn(i);
+               Patch::column_t pCol;
+               pCol.index = gCol->getIndex();
+               pCol.width = gCol->w();
+               for (unsigned k=0; k<gCol->countChannels(); k++) {
+                       Channel *colChannel = gCol->getChannel(k);
+                       for (unsigned j=0; j<G_Mixer.channels.size; j++) {
+                               Channel *mixerChannel = G_Mixer.channels.at(j);
+                               if (colChannel == mixerChannel) {
+                                       pCol.channels.add(mixerChannel->index);
+                                       break;
+                               }
+                       }
+               }
+               G_Patch.columns.add(pCol);
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void __setProgressBar__(class gProgress *status, float v)
+{
+       status->value(status->value() + v);
+       //Fl::check();
+       Fl::wait(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_saveProject(const string &folderPath, const string &projName)
+{
+       if (!gDirExists(folderPath.c_str()) && !gMkdir(folderPath.c_str())) {
+               gLog("[glue] unable to make project directory!\n");
+               return 0;
+       }
+
+       /* copy all samples inside the folder. Takes and logical ones are saved
+        * via glue_saveSample() */
+
+       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+
+               if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+                       continue;
+
+               SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+
+               if (ch->wave == NULL)
+                       continue;
+
+               /* update the new samplePath: everything now comes from the project
+                * folder (folderPath). Also remove any existing file. */
+
+               string samplePath = folderPath + gGetSlash() + ch->wave->basename() + "." + ch->wave->extension();
+
+               if (gFileExists(samplePath.c_str()))
+                       remove(samplePath.c_str());
+               if (ch->save(samplePath.c_str()))
+                       ch->wave->pathfile = samplePath;
+       }
+
+       string gptcPath = folderPath + gGetSlash() + gStripExt(projName.c_str()) + ".gptc";
+       glue_savePatch(gptcPath, projName, true); // true == it's a project
+
+       return 1;
+}
diff --git a/src/glue/storage.h b/src/glue/storage.h
new file mode 100644 (file)
index 0000000..23aee4e
--- /dev/null
@@ -0,0 +1,56 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GLUE_STORAGE_H
+#define GLUE_STORAGE_H
+
+
+#include "../core/patch.h"
+
+
+using std::string;
+
+
+int glue_loadPatch  (const string &fullPath, class gProgress *status, bool isProject);
+int glue_loadPatch__DEPR__(const char *fname, const char *fpath, class gProgress *status, bool isProject);
+int glue_savePatch  (const string &fullPath, const string &name, bool isProject);
+int glue_saveProject(const string &folderPath, const string &projName);
+
+static void __glue_fillPatchGlobals__(const string &name);
+static void __glue_fillPatchChannels__(bool isProject);
+static void __glue_fillPatchColumns__();
+
+#ifdef WITH_VST
+static void __glue_fillPatchGlobalsPlugins__(gVector <Plugin *> *host, gVector<Patch::plugin_t> *patch);
+#endif
+
+static void __setProgressBar__(class gProgress *status, float v);
+
+#endif
index e79025366e7da49eacb582d7db486c9b6f648504..2194bd397aec08b6769e7f024dcd2b2ce5e6d018 100644 (file)
@@ -1,10 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
  * gd_about
  *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
+#include <jansson.h>
 #include "../../core/conf.h"
 #include "../../core/const.h"
 #include "../../core/kernelAudio.h"
@@ -42,9 +43,11 @@ extern Conf G_Conf;
 
 gdAbout::gdAbout()
 #ifdef WITH_VST
-: gWindow(340, 405, "About Giada") {
+: gWindow(340, 405, "About Giada")
+{
 #else
-: gWindow(340, 320, "About Giada") {
+: gWindow(340, 320, "About Giada")
+{
 #endif
 
        if (G_Conf.aboutX)
@@ -67,17 +70,19 @@ gdAbout::gdAbout()
        char message[512];
        sprintf(
          message,
-         "Version " VERSIONE " (" __DATE__ ")\n\n"
+         "Version " G_VERSION_STR " (" __DATE__ ")\n\n"
                "Developed by Monocasual\n"
                "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
-               "RtMidi (%s), libsamplerate and libsndfile\n\n"
+               "RtMidi (%s), libsamplerate, Jansson (%s) \n"
+               "and libsndfile\n\n"
                "Released under the terms of the GNU General\n"
                "Public License (GPL v3)\n\n"
                "News, infos, contacts and documentation:\n"
                "www.giadamusic.com",
-               FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION, 
+               FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
                kernelAudio::getRtAudioVersion().c_str(),
-               kernelMidi::getRtMidiVersion().c_str());
+               kernelMidi::getRtMidiVersion().c_str(),
+               JANSSON_VERSION);
 
        int tw = 0;
        int th = 0;
@@ -103,24 +108,26 @@ gdAbout::gdAbout()
 }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
-gdAbout::~gdAbout() {
+gdAbout::~gdAbout()
+{
        G_Conf.aboutX = x();
        G_Conf.aboutY = y();
 }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
 void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
-void gdAbout::__cb_close() {
+void gdAbout::__cb_close()
+{
        do_callback();
 }
index bf7455f63f6f580473d24a8170d24e50b4ed5c05..fb3aebafca784112046f5ae49fb277ef59ba689d 100644 (file)
@@ -28,7 +28,6 @@
 
 
 #include "../../utils/gui_utils.h"
-#include "../../core/patch.h"
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
 #include "../../glue/glue.h"
index d415ae048f34da11dbef1c7b542adba1b05c2343..c3eec4a3acaab9abdf0d023852128c7e62ee7b76 100644 (file)
@@ -15,7 +15,7 @@
  * Public License as published by the Free Software Foundation, either
  * version 3 of the License, or (at your option) any later version.
  *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * Giada - Your Hardcore Loopmacghine 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.
 #include "../../core/pluginHost.h"
 #include "../../core/channel.h"
 #include "../../core/sampleChannel.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/patch.h"
 #include "../../core/conf.h"
 #include "../../glue/glue.h"
+#include "../../glue/storage.h"
 #include "../elems/ge_browser.h"
 #include "../elems/ge_channel.h"
 #include "../elems/ge_keyboard.h"
 #include "gd_warnings.h"
 
 
+using std::string;
+
+
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern Patch         G_Patch;
 extern Conf             G_Conf;
 extern Mixer         G_Mixer;
@@ -106,7 +112,7 @@ gdBrowser::gdBrowser(const char *title, const char *initPath, Channel *ch, int t
        else
        if (type == BROWSER_SAVE_PATCH) {
                ok->callback(cb_save_patch, (void*)this);
-               name->value(G_Patch.name[0] == '\0' ? "my_patch.gptc" : G_Patch.name);
+               name->value(G_Patch.name == "" ? "my_patch.gptc" : G_Patch.name.c_str());
                name->maximum_size(MAX_PATCHNAME_LEN+5); // +5 for ".gptc"
        }
        else
@@ -177,27 +183,8 @@ void gdBrowser::__cb_load_patch() {
        if (browser->text(browser->value()) == NULL)
                return;
 
-       /* patchFile is the file to open.
-        * For patches:  browser->get_selected_item()
-        * for projects: browser->get_selected_item() without extention +
-        *               patch name appended */
-
-       std::string patchFile = browser->get_selected_item();;
-       bool        isProject;
-
-       if (gIsProject(browser->get_selected_item())) {
-               std::string patchName = gGetProjectName(browser->get_selected_item());
-#if defined(__linux__) || defined(__APPLE__)
-               patchFile = patchFile+"/"+patchName+".gptc";
-#elif defined(_WIN32)
-               patchFile = patchFile+"\\"+patchName+".gptc";
-#endif
-               isProject = true;
-       }
-       else
-               isProject = false;
-
-       int res = glue_loadPatch(patchFile.c_str(),     browser->path_obj->value(),     status, isProject);
+       bool isProject = gIsProject(browser->get_selected_item());
+       int res = glue_loadPatch(browser->get_selected_item(), status, isProject);
 
        if (res == PATCH_UNREADABLE) {
                status->hide();
@@ -206,7 +193,8 @@ void gdBrowser::__cb_load_patch() {
                else
                        gdAlert("This patch is unreadable.");
        }
-       else if (res == PATCH_INVALID) {
+       else
+       if (res == PATCH_INVALID) {
                status->hide();
                if (isProject)
                        gdAlert("This project is not valid.");
@@ -230,7 +218,7 @@ void gdBrowser::__cb_save_sample() {
 
        /* bruteforce check extension. */
 
-       std::string filename = gStripExt(name->value());
+       string filename = gStripExt(name->value());
        char fullpath[PATH_MAX];
        sprintf(fullpath, "%s/%s.wav", where->value(), filename.c_str());
 
@@ -276,7 +264,7 @@ void gdBrowser::__cb_down() {
 
                if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) {
                        if (gIsProject(path)) {
-                               std::string tmp = browser->text(browser->value());
+                               string tmp = browser->text(browser->value());
                                tmp.erase(0, 4);
                                name->value(tmp.c_str());
                        }
@@ -304,26 +292,20 @@ void gdBrowser::__cb_up() {
 /* -------------------------------------------------------------------------- */
 
 
-void gdBrowser::__cb_save_patch() {
-
+void gdBrowser::__cb_save_patch()
+{
        if (strcmp(name->value(), "") == 0) {  /// FIXME glue business
                gdAlert("Please choose a file name.");
                return;
        }
 
-       /* if name->value() contains ".gptc" */
-
-       char ext[6] = ".gptc";
-       if (strstr(name->value(), ".gptc") != NULL)
-               ext[0] = '\0';
+       string fullpath = where->value() + gGetSlash() + gStripExt(name->value()) + ".gptc";
 
-       char fullpath[PATH_MAX];
-       sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext);
-       if (gFileExists(fullpath))
+       if (gFileExists(fullpath.c_str()))
                if (!gdConfirmWin("Warning", "File exists: overwrite?"))
                        return;
 
-       if (glue_savePatch(fullpath, name->value(), false)) // false == not a project
+  if (glue_savePatch(fullpath, name->value(), false)) // false == not a project
                do_callback();
        else
                gdAlert("Unable to save the patch!");
@@ -333,27 +315,16 @@ void gdBrowser::__cb_save_patch() {
 /* -------------------------------------------------------------------------- */
 
 
-void gdBrowser::__cb_save_project() {
-
+void gdBrowser::__cb_save_project()
+{
        if (strcmp(name->value(), "") == 0) {    /// FIXME glue business
                gdAlert("Please choose a project name.");
                return;
        }
 
-       /* check if name->value() contains ".gprj" */
-
-       char ext[6] = ".gprj";
-       if (strstr(name->value(), ".gprj") != NULL)
-               ext[0] = '\0';
-
-       char fullpath[PATH_MAX];
-#if defined(_WIN32)
-       sprintf(fullpath, "%s\\%s%s", where->value(), name->value(), ext);
-#else
-       sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext);
-#endif
+       string fullpath = where->value() + gGetSlash() + gStripExt(name->value()) + ".gprj";
 
-       if (gIsProject(fullpath) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
+       if (gIsProject(fullpath.c_str()) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
                return;
 
        if (glue_saveProject(fullpath, name->value()))
@@ -372,14 +343,14 @@ void gdBrowser::__cb_loadPlugin() {
        if (browser->text(browser->value()) == NULL)
                return;
 
-       int res = G_PluginHost.addPlugin(browser->get_selected_item(), stackType, ch);
+       Plugin *p = G_PluginHost.addPlugin(browser->get_selected_item(), stackType, ch);
 
        /* store the folder path inside G_Conf, in order to reuse it the
         * next time. */
 
        G_Conf.setPath(G_Conf.pluginPath, where->value());
 
-       if (res)
+       if (p != NULL)
                do_callback();
        else
                gdAlert("Unable to load the selected plugin!");
index 45f8b151a21f20488c5f68a6b3991a005513aee1..7e9c21358a720dd52b61776a9341269f6b729e3c 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "../../core/conf.h"
 #include "../../core/midiMapConf.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/kernelAudio.h"
 #include "../../core/kernelMidi.h"
 #include "../../utils/gui_utils.h"
@@ -41,9 +41,9 @@
 #include "gd_browser.h"
 
 
-extern Patch G_Patch;
-extern Conf     G_Conf;
-extern bool  G_audio_status;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern Conf           G_Conf;
+extern bool        G_audio_status;
 extern MidiMapConf G_MidiMap;
 
 
index 0289763cd821c431e84c0045ecad308a66f6f3f8..1fc0c67032df396fe1a60e314cc820159d60fe3d 100644 (file)
@@ -40,7 +40,7 @@
 #include "../../core/channel.h"
 #include "../../core/sampleChannel.h"
 #include "../../core/init.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/conf.h"
 #include "../../glue/glue.h"
 #include "../elems/ge_keyboard.h"
@@ -60,7 +60,7 @@
 
 
 extern Mixer                    G_Mixer;
-extern Patch                    G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern Conf                             G_Conf;
 extern gdMainWindow *mainWin;
 extern bool                                     G_quit;
index 945ece4e3526a404a13810b87b632362bd2c3805..a9a37d580020155eb3b6062b79c1998d3d951fbc 100644 (file)
@@ -30,7 +30,7 @@
 #include "../../core/pluginHost.h"
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/graphics.h"
 #include "../../core/channel.h"
 #include "../../core/wave.h"
@@ -57,7 +57,7 @@
 
 extern Mixer                G_Mixer;
 extern Conf                 G_Conf;
-extern Patch                G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern gdMainWindow *mainWin;
 
 
index 4eef502cc371a8deb3770574f2ef98271cb3df9f..1480b54375838d5a534b3bd543e00642bc8cf12a 100644 (file)
 
 
 #include "../../core/const.h"
+#include "../../utils/utils.h"
 #include "ge_channelButton.h"
 
 
+using std::string;
+
+
 gChannelButton::gChannelButton(int x, int y, int w, int h, const char *l)
   : gClick(x, y, w, h, l), key("") {}
 
@@ -38,7 +42,7 @@ gChannelButton::gChannelButton(int x, int y, int w, int h, const char *l)
 /* -------------------------------------------------------------------------- */
 
 
-void gChannelButton::setKey(const char *k)
+void gChannelButton::setKey(const string &k)
 {
   key = k;
 }
@@ -47,6 +51,21 @@ void gChannelButton::setKey(const char *k)
 /* -------------------------------------------------------------------------- */
 
 
+void gChannelButton::setKey(int k)
+{
+  if (k == 0)
+    key = "";
+  else {
+    // FIXME - this crap won't work with unicode/utf-8
+    char c = (char) k;
+    key = c;
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void gChannelButton::draw()
 {
   gClick::draw();
index a3bb22bbdaffb2c0b13124e0077a9d36dd340c37..86f673623342eeca2a4c4d6a41fc3839113132a0 100644 (file)
 #include "ge_mixed.h"
 
 
+using std::string;
+
+
 class gChannelButton : public gClick
 {
 private:
 
-       std::string key;
+       string key;
 
 public:
 
@@ -47,7 +50,8 @@ public:
        virtual int handle(int e) = 0;
 
        void draw();
-       void setKey(const char *k);
+       void setKey(const string &k);
+       void setKey(int k);
        void setPlayMode();
        void setEndingMode();
        void setDefaultMode(const char *l=0);
index 12c479c4f44db95df6fea400e9807f808903a6c1..de10451b3213799c7d7dcc0cf75ec9f485501339 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/channel.h"
 #include "../../core/sampleChannel.h"
 #include "../../core/midiChannel.h"
@@ -50,7 +50,7 @@
 
 extern Mixer                G_Mixer;
 extern Conf                 G_Conf;
-extern Patch                G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern gdMainWindow *mainWin;
 
 
@@ -71,7 +71,7 @@ gColumn::gColumn(int X, int Y, int W, int H, int index, gKeyboard *parent)
        end();
 
   resizer = new gResizerBar(x()+w(), y(), 16, h(), false);
-  resizer->setMinSize(140);
+  resizer->setMinSize(MIN_COLUMN_WIDTH);
   parent->add(resizer);
 
        addChannelBtn->callback(cb_addChannel, (void*)this);
@@ -93,6 +93,7 @@ gColumn::~gColumn()
 /* -------------------------------------------------------------------------- */
 
 
+
 int gColumn::handle(int e)
 {
        switch (e) {
@@ -194,28 +195,12 @@ void gColumn::cb_addChannel(Fl_Widget *v, void *p) { ((gColumn*)p)->__cb_addChan
 
 gChannel *gColumn::addChannel(class Channel *ch)
 {
+       int currentY = y() + children() * 24;
        gChannel *gch = NULL;
-
        if (ch->type == CHANNEL_SAMPLE)
-               gch = (gSampleChannel*) new gSampleChannel(
-                               x(),
-                               y() + children() * 24,
-                               600, // (1) see notes below
-                               20,
-                               (SampleChannel*) ch);
+               gch = (gSampleChannel*) new gSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch);
        else
-               gch = (gMidiChannel*) new gMidiChannel(
-                               x(),
-                               y() + children() * 24,
-                               w(),
-                               20,
-                               (MidiChannel*) ch);
-
-       /* (1) we create a new sample channel with a fake width, instead of w() (i.e.
-       the column width), in case the column is too narrow to display all widgets.
-       This workaround prevents the widgets to disappear if they have an initial
-       negative width. MidiChannel does not need such hack because it already fits
-       nicely in a collapsed column. */
+               gch = (gMidiChannel*) new gMidiChannel(x(),     currentY, w(), 20, (MidiChannel*) ch);
 
        add(gch);
   resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop
@@ -302,3 +287,16 @@ void gColumn::clear(bool full)
                }
        }
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel *gColumn::getChannel(int i)
+{
+  gChannel *gch = (gChannel*) child(i);
+  if (gch->type == CHANNEL_SAMPLE)
+    return ((gSampleChannel*) child(i))->ch;
+  else
+    return ((gMidiChannel*) child(i))->ch;
+}
index 84ebc8a1b9c179d5043361ed3bd0689e2b2deb56..f0e0c9cccb53b8cb96a2088badf6f1cba2947ed6 100644 (file)
@@ -80,6 +80,10 @@ public:
 
        void refreshChannels();
 
+  /* getChannel */
+
+  Channel *getChannel(int i);
+
        /* clear
         * remove all channels from the column. If full==true, delete also the
         * "add new channel" button. This method ovverrides the inherited one
@@ -92,6 +96,7 @@ public:
        inline int  getIndex()      { return index; }
        inline void setIndex(int i) { index = i; }
        inline bool isEmpty()       { return children() == 1; }
+  inline int  countChannels() { return children(); }
 };
 
 
index 24d47bd7ca6a2f2a48ddad4bf67df0e4f30285a5..bf0e1bdf1e923f815296310cb04d599731f05462 100644 (file)
@@ -30,7 +30,7 @@
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
 #include "../../core/const.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/channel.h"
 #include "../../core/sampleChannel.h"
 #include "../../glue/glue.h"
@@ -46,7 +46,7 @@
 
 extern Mixer                G_Mixer;
 extern Conf                 G_Conf;
-extern Patch                G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern gdMainWindow *mainWin;
 
 
@@ -160,6 +160,10 @@ void gKeyboard::organizeColumns()
 
        addColumnBtn->position(columns.last()->x() + columns.last()->w() + 16, y());
 
+       /* recompute col indexes */
+
+       refreshColIndexes();
+
        redraw();
 }
 
@@ -167,7 +171,10 @@ void gKeyboard::organizeColumns()
 /* -------------------------------------------------------------------------- */
 
 
-void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) { ((gKeyboard*)p)->__cb_addColumn(); }
+void gKeyboard::cb_addColumn(Fl_Widget *v, void *p)
+{
+       ((gKeyboard*)p)->__cb_addColumn(DEFAULT_COLUMN_WIDTH);
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -175,7 +182,7 @@ void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) { ((gKeyboard*)p)->__cb_addC
 
 gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build)
 {
-       gColumn *col = getColumn(colIndex);
+       gColumn *col = getColumnByIndex(colIndex);
 
        /* no column with index 'colIndex' found? Just create it and set its index
        to 'colIndex'. */
@@ -205,7 +212,7 @@ void gKeyboard::refreshColumns()
 /* -------------------------------------------------------------------------- */
 
 
-gColumn *gKeyboard::getColumn(int index)
+gColumn *gKeyboard::getColumnByIndex(int index)
 {
        for (unsigned i=0; i<columns.size; i++)
                if (columns.at(i)->getIndex() == index)
@@ -331,36 +338,60 @@ void gKeyboard::printChannelMessage(int res)
                gdAlert("Unknown error.");
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 
-void gKeyboard::__cb_addColumn()
+void gKeyboard::__cb_addColumn(int width)
 {
        int colx;
        int colxw;
-       int colw = 380;
+       int gap = 16;
        if (columns.size == 0) {
                colx  = x() - xposition();  // mind the offset with xposition()
-               colxw = colx + colw;
+               colxw = colx + width;
        }
        else {
                gColumn *prev = columns.last();
-               colx  = prev->x()+prev->w() + 16;
-               colxw = colx + colw;
+               colx  = prev->x()+prev->w() + gap;
+               colxw = colx + width;
        }
 
        /* add gColumn to gKeyboard and to columns vector */
 
-       gColumn *gc = new gColumn(colx, y(), colw-20, 2000, indexColumn, this);
+       gColumn *gc = new gColumn(colx, y(), width, 2000, indexColumn, this);
   add(gc);
        columns.add(gc);
        indexColumn++;
 
        /* move addColumn button */
 
-       addColumnBtn->position(colxw-4, y());
+       addColumnBtn->position(colxw + gap, y());
        redraw();
 
-       gLog("[gKeyboard::__cb_addColumn] new column added (index = %d), total count=%d, addColumn(x)=%d\n",
-               gc->getIndex(), columns.size, addColumnBtn->x());
+       gLog("[gKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n",
+               gc->getIndex(), width, columns.size, addColumnBtn->x());
+
+       /* recompute col indexes */
+
+       refreshColIndexes();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::addColumn(int width)
+{
+       __cb_addColumn(width);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::refreshColIndexes()
+{
+       for (unsigned i=0; i<columns.size; i++)
+               columns.at(i)->setIndex(i);
 }
index be79c39ea617450d4b387ea2c7ad9480df1039d9..47575b1c79d908358713dab3da2ac97a00c93db6 100644 (file)
@@ -37,6 +37,7 @@
 #include <FL/Fl_Box.H>
 #include <FL/Fl_Menu_Button.H>
 #include "../elems/ge_column.h"
+#include "../../core/const.h"
 #include "../../utils/utils.h"
 
 
@@ -44,8 +45,14 @@ class gKeyboard : public Fl_Scroll
 {
 private:
 
+       /* refreshColIndexes
+        * Recompute all column indexes in order to avoid any gaps between them.
+        * Indexes must always be contiguous! */
+        
+       void refreshColIndexes();
+
        static void cb_addColumn  (Fl_Widget *v, void *p);
-       inline void __cb_addColumn();
+       inline void __cb_addColumn(int width=DEFAULT_COLUMN_WIDTH);
 
        bool bckspcPressed;
        bool endPressed;
@@ -78,10 +85,16 @@ public:
        /* addChannel
         * add a new channel to gChannels. Used by callbacks and during
         * patch loading. Requires Channel (and not gChannel). If build is
-        * set to true, also generate the corresponding column.*/
+        * set to true, also generate the corresponding column if column (index) does
+        * not exist yet. */
 
        gChannel *addChannel(int column, class Channel *ch, bool build=false);
 
+       /* addColumn
+        * add a new column to the top of the stack. */
+
+       void addColumn(int width=380);
+
        /* deleteChannel
         * delete a channel from gChannels<> where gChannel->ch == ch and remove
         * it from the stack. */
@@ -109,10 +122,15 @@ public:
 
        void refreshColumns();
 
-       /* getColumn
+       /* getColumnByIndex
         * return the column with index 'index', or NULL if not found. */
 
-       gColumn *getColumn(int index);
+       gColumn *getColumnByIndex(int index);
+
+       /* getColumn
+        * return the column with from columns->at(i). */
+
+       inline gColumn *getColumn(int i) { return columns.at(i); }
 
        /* clear
         * delete all channels and groups. */
@@ -138,7 +156,7 @@ public:
         * return the width in pixel of i-th column. Warning: 'i' is the i-th column
         * in the column array, NOT the index. */
 
-       inline int getColumnWidth(int i) { return getColumn(i)->w(); }
+       inline int getColumnWidth(int i) { return getColumnByIndex(i)->w(); }
 };
 
 
index 68ba4e70111e7e6a60fdcfd6fb8921bacc8d9070..7a372ba8f8c7430009fbbc69781c35cf8de772a8 100644 (file)
@@ -30,7 +30,7 @@
 #include "../../core/pluginHost.h"
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/graphics.h"
 #include "../../core/channel.h"
 #include "../../core/wave.h"
@@ -59,7 +59,7 @@
 
 extern Mixer                G_Mixer;
 extern Conf                 G_Conf;
-extern Patch                G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern gdMainWindow *mainWin;
 
 
@@ -105,6 +105,7 @@ gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch)
        solo->callback(cb_solo, (void*)this);
 
        mainButton->callback(cb_openMenu, (void*)this);
+
        vol->callback(cb_changeVol, (void*)this);
 
        ch->guiChannel = this;
@@ -278,6 +279,8 @@ void gMidiChannel::update()
        mute->value(ch->mute);
        solo->value(ch->solo);
 
+       mainButton->setKey(ch->key);
+
 #ifdef WITH_VST
        fx->full = ch->plugins.size > 0;
 #endif
index ff678d980c6b7436674e84c9e49abe220dd38af4..a68f3df899a60814e634ae0dd8faa9e1c83b9565 100644 (file)
@@ -30,7 +30,7 @@
 #include "../../core/pluginHost.h"
 #include "../../core/mixer.h"
 #include "../../core/conf.h"
-#include "../../core/patch.h"
+#include "../../core/patch_DEPR_.h"
 #include "../../core/graphics.h"
 #include "../../core/channel.h"
 #include "../../core/wave.h"
@@ -58,7 +58,7 @@
 
 extern Mixer                G_Mixer;
 extern Conf                 G_Conf;
-extern Patch                G_Patch;
+extern Patch_DEPR_   G_Patch_DEPR_;
 extern gdMainWindow *mainWin;
 
 
@@ -108,6 +108,7 @@ gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *
        solo->callback(cb_solo, (void*)this);
 
        mainButton->callback(cb_openMenu, (void*)this);
+
        vol->callback(cb_changeVol, (void*)this);
 
        ch->guiChannel = this;
@@ -452,8 +453,11 @@ void gSampleChannel::update()
        mute->value(ch->mute);
        solo->value(ch->solo);
 
+       mainButton->setKey(ch->key);
+
 #ifdef WITH_VST
        fx->full = ch->plugins.size > 0;
+       fx->redraw();
 #endif
 }
 
@@ -532,6 +536,13 @@ void gSampleChannel::resize(int X, int Y, int W, int H)
                mainButton->size(w() - BREAK_DELTA, mainButton->h());
                mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
                solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
+
+               /* The followings are useless on manual resizing, but useful when a channel
+                * is added from a patch with a small width. */
+
+         modeBox->hide();
+               if (readActions)
+      readActions->hide();
        }
        else
        if (w() < BREAK_MODE_BOX) {
@@ -548,9 +559,8 @@ void gSampleChannel::resize(int X, int Y, int W, int H)
     modeBox->show();
     mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h());
     modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
-               if (readActions) {
+               if (readActions)
       readActions->hide();
-               }
        }
        else {
                if (readActions) {
index 0aad47093744c0ee53019086d93c7b7acd03f477..9ba701a9800959fb7c608e7b97ce08cc461b2dbc 100644 (file)
@@ -82,7 +82,7 @@ public:
 
        void addActionButton();
        void delActionButton(bool force=false);
-
+       
        class gModeBox *modeBox;
        class gClick     *readActions;
 
index a70c61616f450e7e24032c130673b8f90e4350e7..d7829df4e25e1b2c1c5a9152b07894d1118ef6c4 100644 (file)
@@ -1,8 +1,8 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
  *
@@ -22,7 +22,7 @@
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
 #include <pthread.h>
@@ -31,6 +31,7 @@
 #endif
 #include "core/init.h"
 #include "core/const.h"
+#include "core/patch_DEPR_.h"
 #include "core/patch.h"
 #include "core/conf.h"
 #include "core/midiMapConf.h"
@@ -52,9 +53,10 @@ Mixer         G_Mixer;
 bool          G_quit;
 bool           G_audio_status;
 bool          G_midiStatus;
-Patch        G_Patch;
-Conf                 G_Conf;
-MidiMapConf       G_MidiMap;
+Patch_DEPR_   G_Patch_DEPR_;
+Patch         G_Patch;
+Conf          G_Conf;
+MidiMapConf   G_MidiMap;
 gdMainWindow *mainWin;
 
 #ifdef WITH_VST
index 7625576aa0f3e960d6ea941f1824dcbb761daf2b..872aee58d98cba85afce9101fcf068b3ada6dc3a 100644 (file)
@@ -28,7 +28,7 @@
 
 
 #include "../core/mixer.h"
-#include "../core/patch.h"
+#include "../core/patch_DEPR_.h"
 #include "../core/recorder.h"
 #include "../core/wave.h"
 #include "../core/pluginHost.h"
@@ -48,7 +48,7 @@
 extern Mixer          G_Mixer;
 extern unsigned      G_beats;
 extern bool                 G_audio_status;
-extern Patch         G_patch;
+extern Patch_DEPR_   G_patch;
 extern Conf          G_conf;
 extern uint32_t      G_time;
 extern gdMainWindow *mainWin;
@@ -98,8 +98,9 @@ int gu_getBlinker()
 
 void gu_updateControls()
 {
-       for (unsigned i=0; i<G_Mixer.channels.size; i++)
+       for (unsigned i=0; i<G_Mixer.channels.size; i++) {
                G_Mixer.channels.at(i)->guiChannel->update();
+       }
 
        mainWin->inOut->setOutVol(G_Mixer.outVol);
        mainWin->inOut->setInVol(G_Mixer.inVol);
@@ -124,7 +125,7 @@ void gu_updateControls()
 
 void gu_update_win_label(const char *c)
 {
-       std::string out = VERSIONE_STR;
+       std::string out = G_APP_NAME;
        out += " - ";
        out += c;
        mainWin->copy_label(out.c_str());
diff --git a/src/utils/gvector.h b/src/utils/gvector.h
new file mode 100644 (file)
index 0000000..e36ef90
--- /dev/null
@@ -0,0 +1,146 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gvector
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GVECTOR_H
+#define GVECTOR_H
+
+
+#include "log.h"
+
+
+/* gVector
+ * lightweight template class. */
+
+template <class T> class gVector
+{
+public:
+
+       /* gVector()
+        * default constructor, no parameters */
+
+       gVector() : size(0), s(NULL) {}
+
+       /* gVector(const &)
+        * copy-constructor, when gVector a = b (where b is another gVector).
+        * Default constructor doesn't copy referenced objects, so we need
+        * to re-allocate the internal stack for the copied object */
+
+       gVector(const gVector &other)
+       {
+               s = new T[other.size];
+               for (unsigned i=0; i<other.size; i++)
+                       s[i] = other.s[i];
+               size = other.size;
+       }
+
+
+       ~gVector()
+       {
+               /// FIXME empty s with clear()?!?
+       }
+
+
+       void add(const T &item)
+       {
+               T *tmp = new T[size+1];  /// TODO: chunk increment (size+N), N ~= 16
+               for (unsigned i=0; i<size; i++)
+                       tmp[i] = s[i];
+               tmp[size] = item;
+               delete[] s;
+               s = tmp;
+               size++;
+       }
+
+
+       int del(const T &item)
+       {
+               for (unsigned i=0; i<size; i++)
+                       if (s[i] == item)
+                               return del(i);
+               return -1;
+       }
+
+
+       int del(unsigned p)
+       {
+               if (p > size-1) gLog("[vector] del() outside! requested=%d, size=%d\n", p, size);
+               T *tmp = new T[size-1];
+               unsigned i=0;
+               unsigned j=0;
+               while (i<size) {
+                       if (i != p) {
+                               tmp[j] = s[i];
+                               j++;
+                       }
+                       i++;
+               }
+               delete[] s;
+               s = tmp;
+               size -= 1;
+               return size;
+       }
+
+
+       void clear()
+       {
+               if (size > 0) {
+                       delete [] s;
+                       s = NULL;
+                       size = 0;
+               }
+       }
+
+
+       void swap(unsigned x, unsigned y)
+       {
+               T tmp = s[x];
+               s[x] = s[y];
+               s[y] = tmp;
+       }
+
+
+       T &at(unsigned p)
+       {
+               if (p > size-1) gLog("[vector] at() outside! requested=%d, size=%d\n", p, size);
+               return s[p];
+       }
+
+
+       T &last()
+       {
+               return s[size-1];
+       }
+
+
+       unsigned size;
+       T *s;                           // stack (array of T)
+};
+
+
+#endif
index c0b8c1a52046e6c7498bc4881ccf2912643004c8..a45199db65f39525cd25b2244fa3047e03226a75 100644 (file)
@@ -50,6 +50,8 @@
 #endif
 
 
+using std::string;
+
 
 bool gFileExists(const char *filename) {
        FILE *fh = fopen(filename, "rb");
@@ -90,7 +92,7 @@ bool gIsDir(const char *path)
                 * FIXME - consider native functions CFBundle... */
 
                if (ret) {
-                       std::string tmp = path;
+                       string tmp = path;
                        tmp += "/Contents/Info.plist";
                        if (gFileExists(tmp.c_str()))
                                ret = false;
@@ -137,17 +139,17 @@ bool gMkdir(const char *path)
 
 /* -------------------------------------------------------------------------- */
 
-
-std::string gBasename(const char *path)
+/* TODO - avoid this shit, just wrap the other call */
+string gBasename(const char *path)
 {
-       std::string out = path;
+       string out = path;
        out.erase(0, out.find_last_of(gGetSlash().c_str())+1);
        return out;
 }
 
-std::string gBasename(const std::string &s)
+string gBasename(const string &s)
 {
-       std::string out = s;
+       string out = s;
        out.erase(0, out.find_last_of(gGetSlash().c_str())+1);
        return out;
 }
@@ -156,9 +158,9 @@ std::string gBasename(const std::string &s)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gDirname(const char *path)
+string gDirname(const char *path)
 {
-       std::string out = path;
+       string out = path;
        out.erase(out.find_last_of(gGetSlash().c_str()));
        return out;
 }
@@ -167,7 +169,7 @@ std::string gDirname(const char *path)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gGetCurrentPath()
+string gGetCurrentPath()
 {
  char buf[PATH_MAX];
 #if defined(__WIN32)
@@ -184,7 +186,7 @@ std::string gGetCurrentPath()
 /* -------------------------------------------------------------------------- */
 
 
-std::string gGetExt(const char *file)
+string gGetExt(const char *file)
 {
        int len = strlen(file);
        int pos = len;
@@ -195,7 +197,7 @@ std::string gGetExt(const char *file)
        }
        if (pos==0)
                return "";
-       std::string out = file;
+       string out = file;
        return out.substr(pos+1, len);
 }
 
@@ -203,7 +205,7 @@ std::string gGetExt(const char *file)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gStripExt(const char *file)
+string gStripExt(const char *file)
 {
        int len = strlen(file);
        int pos = -1;
@@ -212,11 +214,17 @@ std::string gStripExt(const char *file)
                        pos = i;
                        break;
                }
-       std::string out = file;
+       string out = file;
        return pos == -1 ? out : out.substr(0, pos);
 }
 
 
+string gStripExt(const string &s)
+{
+       return s.substr(0, s.find_last_of("."));
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -244,9 +252,9 @@ bool gIsPatch(const char *path)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gGetProjectName(const char *path)
+string gGetProjectName(const char *path)
 {
-       std::string out;
+       string out;
        out = gStripExt(path);
 
        int i = out.size();
@@ -268,7 +276,7 @@ std::string gGetProjectName(const char *path)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gGetSlash() // TODO - create SLASH macro or constant
+string gGetSlash()
 {
 #if defined(_WIN32)
        return "\\";
@@ -281,7 +289,7 @@ std::string gGetSlash() // TODO - create SLASH macro or constant
 /* -------------------------------------------------------------------------- */
 
 
-std::string gItoa(int i)
+string gItoa(int i)
 {
        std::stringstream out;
        out << i;
@@ -292,14 +300,14 @@ std::string gItoa(int i)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gTrim(const char *f)
+string gTrim(const char *f)
 {
-       std::string out = f;
+       string out = f;
        return gTrim(out);
 }
 
 
-std::string gTrim(const std::string &s)
+string gTrim(const string &s)
 {
        std::size_t first = s.find_first_not_of(" \n\t");
        std::size_t last  = s.find_last_not_of(" \n\t");
@@ -310,10 +318,10 @@ std::string gTrim(const std::string &s)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gReplace(std::string in, const std::string& search, const std::string& replace)
+string gReplace(string in, const string& search, const string& replace)
 {
        size_t pos = 0;
-       while ((pos = in.find(search, pos)) != std::string::npos) {
+       while ((pos = in.find(search, pos)) != string::npos) {
                in.replace(pos, search.length(), replace);
                pos += replace.length();
        }
@@ -324,9 +332,9 @@ std::string gReplace(std::string in, const std::string& search, const std::strin
 /* -------------------------------------------------------------------------- */
 
 
-std::string gStripFileUrl(const char *f)
+string gStripFileUrl(const char *f)
 {
-       std::string out = f;
+       string out = f;
        out = gReplace(out, "file://", "");
        out = gReplace(out, "%20", " ");
        return out;
@@ -336,7 +344,7 @@ std::string gStripFileUrl(const char *f)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gGetHomePath()
+string gGetHomePath()
 {
        char path[PATH_MAX];
 
@@ -362,17 +370,17 @@ std::string gGetHomePath()
 
 #endif
 
-       return std::string(path);
+       return string(path);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gSplit(std::string in, std::string sep, gVector<std::string> *v)
+void gSplit(string in, string sep, gVector<string> *v)
 {
-       std::string full  = in;
-       std::string token = "";
+       string full  = in;
+       string token = "";
        size_t curr = 0;
        size_t next = -1;
        do {
@@ -382,5 +390,5 @@ void gSplit(std::string in, std::string sep, gVector<std::string> *v)
                if (token != "")
                        v->add(token);
        }
-       while (next != std::string::npos);
+       while (next != string::npos);
 }
index 8f832adecac55641227233819823f028711d4cad..631657794eadf7aa1bcfaf4f4c92ad5c8781a947 100644 (file)
 #include <string>
 #include <cstdio>
 #include "log.h"
+#include "gvector.h"
 
 
-/* gVector
- * lightweight template class. */
-
-template <class T> class gVector
-{
-public:
-
-       /* gVector()
-        * default constructor, no parameters */
-
-       gVector() : size(0), s(NULL) {}
-
-       /* gVector(const &)
-        * copy-constructor, when gVector a = b (where b is gVector).
-        * Default constructor doesn't copy referenced ojbects, so we need
-        * to re-allocate the internal stack for the copied object */
-
-       gVector(const gVector &other)
-       {
-               s = new T[other.size];
-               for (unsigned i=0; i<other.size; i++)
-                       s[i] = other.s[i];
-               size = other.size;
-       }
-
-
-       ~gVector()
-       {
-               /// FIXME empty s with clear()?!?
-       }
-
-
-       void add(const T &item)
-       {
-               T *tmp = new T[size+1];  /// TODO: chunk increment (size+N), N ~= 16
-               for (unsigned i=0; i<size; i++)
-                       tmp[i] = s[i];
-               tmp[size] = item;
-               delete[] s;
-               s = tmp;
-               size++;
-       }
-
-
-       int del(const T &item)
-       {
-               for (unsigned i=0; i<size; i++)
-                       if (s[i] == item)
-                               return del(i);
-               return -1;
-       }
-
-
-       int del(unsigned p)
-       {
-               if (p > size-1) gLog("[vector] del() outside! requested=%d, size=%d\n", p, size);
-               T *tmp = new T[size-1];
-               unsigned i=0;
-               unsigned j=0;
-               while (i<size) {
-                       if (i != p) {
-                               tmp[j] = s[i];
-                               j++;
-                       }
-                       i++;
-               }
-               delete[] s;
-               s = tmp;
-               size -= 1;
-               return size;
-       }
-
-
-       void clear()
-       {
-               if (size > 0) {
-                       delete [] s;
-                       s = NULL;
-                       size = 0;
-               }
-       }
-
-
-       void swap(unsigned x, unsigned y)
-       {
-               T tmp = s[x];
-               s[x] = s[y];
-               s[y] = tmp;
-       }
-
-
-       T &at(unsigned p)
-       {
-               if (p > size-1) gLog("[vector] at() outside! requested=%d, size=%d\n", p, size);
-               return s[p];
-       }
-
-
-       T &last()
-       {
-               return s[size-1];
-       }
-
-
-       unsigned size;
-       T *s;                           // stack (array of T)
-};
-
-
-/* ------------------------------------------------------------------ */
+using std::string;
 
 
 bool gFileExists(const char *path);
@@ -160,32 +52,33 @@ bool gIsPatch(const char *path);
 
 bool gMkdir(const char *path);
 
-std::string gBasename(const char *path);
-std::string gBasename(const std::string &s);
+string gBasename(const char *path);
+string gBasename(const string &s);
 
-std::string gReplace(std::string in, const std::string& search, const std::string& replace);
+string gReplace(string in, const string& search, const string& replace);
 
-std::string gDirname(const char *path);
+string gDirname(const char *path);
 
-std::string gTrim(const char *path);
-std::string gTrim(const std::string &s);
+string gTrim(const char *path);
+string gTrim(const string &s);
 
-std::string gGetCurrentPath();
+string gGetCurrentPath();
 
-std::string gGetHomePath();
+string gGetHomePath();
 
-std::string gStripFileUrl(const char *path);
+string gStripFileUrl(const char *path);
 
-std::string gGetExt(const char *path);
+string gGetExt(const char *path);
 
-std::string gStripExt(const char *path);
+string gStripExt(const char *path);
+string gStripExt(const string &s);
 
-std::string gGetProjectName(const char *path);
+string gGetProjectName(const char *path); // TODO - useless!
 
-std::string gGetSlash();
+string gGetSlash();
 
-std::string gItoa(int i);
+string gItoa(int i);
 
-void gSplit(std::string in, std::string sep, gVector<std::string> *v);
+void gSplit(string in, string sep, gVector<string> *v);
 
 #endif
diff --git a/tests/patch.cpp b/tests/patch.cpp
new file mode 100644 (file)
index 0000000..a36decf
--- /dev/null
@@ -0,0 +1,229 @@
+#include "../src/core/patch.h"
+#include "../src/core/const.h"
+#include "catch.hpp"
+
+
+using std::string;
+
+
+TEST_CASE("Test Patch class")
+{
+  Patch patch;
+  string filename = "./test-patch.json";
+
+  SECTION("test write")
+  {
+    Patch::action_t  action1;
+    Patch::action_t  action2;
+    Patch::channel_t channel1;
+    Patch::channel_t channel2;
+    Patch::column_t  column;
+#ifdef WITH_VST
+    Patch::plugin_t  plugin1;
+    Patch::plugin_t  plugin2;
+    Patch::plugin_t  plugin3;
+#endif
+
+    action1.type   = 0;
+    action1.frame  = 50000;
+    action1.fValue = 0.3f;
+    action1.iValue = 1000;
+    action2.type   = 2;
+    action2.frame  = 589;
+    action2.fValue = 1.0f;
+    action2.iValue = 130;
+    channel1.actions.add(action1);
+    channel1.actions.add(action2);
+
+#ifdef WITH_VST
+    plugin1.path   = "/path/to/plugin1";
+    plugin1.bypass = false;
+    plugin1.params.add(0.0f);
+    plugin1.params.add(0.1f);
+    plugin1.params.add(0.2f);
+    channel1.plugins.add(plugin1);
+
+    plugin2.path   = "/another/path/to/plugin2";
+    plugin2.bypass = true;
+    plugin2.params.add(0.6f);
+    plugin2.params.add(0.6f);
+    plugin2.params.add(0.6f);
+    plugin2.params.add(0.0f);
+    plugin2.params.add(1.0f);
+    plugin2.params.add(1.0f);
+    plugin2.params.add(0.333f);
+    channel1.plugins.add(plugin2);
+#endif
+
+    channel1.type              = CHANNEL_SAMPLE;
+    channel1.index             = 666;
+    channel1.column            = 0;
+    channel1.mute              = 0;
+    channel1.mute_s            = 0;
+    channel1.solo              = 0;
+    channel1.volume            = 1.0f;
+    channel1.panLeft           = 0.5f;
+    channel1.panRight          = 0.5f;
+    channel1.midiIn            = true;
+    channel1.midiInKeyPress    = UINT32_MAX;  // check maximum value
+    channel1.midiInKeyRel      = 1;
+    channel1.midiInKill        = 2;
+    channel1.midiInVolume      = 3;
+    channel1.midiInMute        = 4;
+    channel1.midiInSolo        = 5;
+    channel1.midiOutL          = true;
+    channel1.midiOutLplaying   = 7;
+    channel1.midiOutLmute      = 8;
+    channel1.midiOutLsolo      = 9;
+    channel1.samplePath        = "/tmp/test.wav";
+    channel1.key               = 666;
+    channel1.mode              = 0;
+    channel1.begin             = 0;
+    channel1.end               = 0;
+    channel1.boost             = 0;
+    channel1.recActive         = 0;
+    channel1.pitch             = 1.2f;
+    channel1.midiInReadActions = 0;
+    channel1.midiInPitch       = 0;
+    channel1.midiOut           = 0;
+    channel1.midiOutChan       = 5;
+    patch.channels.add(channel1);
+
+    column.index = 0;
+    column.width = 500;
+    patch.columns.add(column);
+
+    patch.header       = "GPTCH";
+    patch.version      = "1.0";
+    patch.versionMajor = 6;
+    patch.versionMinor = 6;
+    patch.versionPatch = 6;
+    patch.name         = "test patch";
+    patch.bpm          = 100.0f;
+    patch.bars         = 4;
+    patch.beats        = 23;
+    patch.quantize     = 1;
+    patch.masterVolIn  = 1.0f;
+    patch.masterVolOut = 0.7f;
+    patch.metronome    = 0;
+    patch.lastTakeId   = 0;
+    patch.samplerate   = 44100;
+
+#ifdef WITH_VST
+
+    patch.masterInPlugins.add(plugin1);
+    patch.masterOutPlugins.add(plugin2);
+
+#endif
+
+    REQUIRE(patch.write(filename) == 1);
+  }
+
+  SECTION("test read")
+  {
+    REQUIRE(patch.read(filename) == PATCH_READ_OK);
+    REQUIRE(patch.header == "GPTCH");
+    REQUIRE(patch.version == "1.0");
+    REQUIRE(patch.versionMajor == 6);
+    REQUIRE(patch.versionMinor == 6);
+    REQUIRE(patch.versionPatch == 6);
+    REQUIRE(patch.name == "test patch");
+    REQUIRE(patch.bpm == Approx(100.0f));
+    REQUIRE(patch.bars == 4);
+    REQUIRE(patch.beats == 23);
+    REQUIRE(patch.quantize == 1);
+    REQUIRE(patch.masterVolIn == Approx(1.0f));
+    REQUIRE(patch.masterVolOut == Approx(0.7f));
+    REQUIRE(patch.metronome == 0);
+    REQUIRE(patch.lastTakeId == 0);
+    REQUIRE(patch.samplerate == 44100);
+
+    Patch::column_t column0 = patch.columns.at(0);
+    REQUIRE(column0.index == 0);
+    REQUIRE(column0.width == 500);
+
+    Patch::channel_t channel0 = patch.channels.at(0);
+    REQUIRE(channel0.type == CHANNEL_SAMPLE);
+    REQUIRE(channel0.index == 666);
+    REQUIRE(channel0.column == 0);
+    REQUIRE(channel0.mute == 0);
+    REQUIRE(channel0.mute_s == 0);
+    REQUIRE(channel0.solo == 0);
+    REQUIRE(channel0.volume == Approx(1.0f));
+    REQUIRE(channel0.panLeft == Approx(0.5f));
+    REQUIRE(channel0.panRight == Approx(0.5f));
+    REQUIRE(channel0.midiIn == true);
+    REQUIRE(channel0.midiInKeyPress == UINT32_MAX);
+    REQUIRE(channel0.midiInKeyRel == 1);
+    REQUIRE(channel0.midiInKill == 2);
+    REQUIRE(channel0.midiInVolume == 3);
+    REQUIRE(channel0.midiInMute == 4);
+    REQUIRE(channel0.midiInSolo == 5);
+    REQUIRE(channel0.midiOutL == true);
+    REQUIRE(channel0.midiOutLplaying == 7);
+    REQUIRE(channel0.midiOutLmute == 8);
+    REQUIRE(channel0.midiOutLsolo == 9);
+    REQUIRE(channel0.samplePath == "/tmp/test.wav");
+    REQUIRE(channel0.key == 666);
+    REQUIRE(channel0.mode == 0);
+    REQUIRE(channel0.begin == 0);
+    REQUIRE(channel0.end == 0);
+    REQUIRE(channel0.boost == 0);
+    REQUIRE(channel0.recActive == 0);
+    REQUIRE(channel0.pitch == Approx(1.2f));
+    REQUIRE(channel0.midiInReadActions == 0);
+    REQUIRE(channel0.midiInPitch == 0);
+    REQUIRE(channel0.midiOut == 0);
+    REQUIRE(channel0.midiOutChan == 5);
+
+    Patch::action_t action0 = channel0.actions.at(0);
+    REQUIRE(action0.type == 0);
+    REQUIRE(action0.frame == 50000);
+    REQUIRE(action0.fValue == Approx(0.3f));
+    REQUIRE(action0.iValue == 1000);
+
+    Patch::action_t action1 = channel0.actions.at(1);
+    REQUIRE(action1.type == 2);
+    REQUIRE(action1.frame == 589);
+    REQUIRE(action1.fValue == Approx(1.0f));
+    REQUIRE(action1.iValue == 130);
+
+#ifdef WITH_VST
+    Patch::plugin_t plugin0 = channel0.plugins.at(0);
+    REQUIRE(plugin0.path   == "/path/to/plugin1");
+    REQUIRE(plugin0.bypass == false);
+    REQUIRE(plugin0.params.at(0) == Approx(0.0f));
+    REQUIRE(plugin0.params.at(1) == Approx(0.1f));
+    REQUIRE(plugin0.params.at(2) == Approx(0.2f));
+
+    Patch::plugin_t plugin1 = channel0.plugins.at(1);
+    REQUIRE(plugin1.path == "/another/path/to/plugin2");
+    REQUIRE(plugin1.bypass == true);
+    REQUIRE(plugin1.params.at(0) == Approx(0.6f));
+    REQUIRE(plugin1.params.at(1) == Approx(0.6f));
+    REQUIRE(plugin1.params.at(2) == Approx(0.6f));
+    REQUIRE(plugin1.params.at(3) == Approx(0.0f));
+    REQUIRE(plugin1.params.at(4) == Approx(1.0f));
+    REQUIRE(plugin1.params.at(5) == Approx(1.0f));
+    REQUIRE(plugin1.params.at(6) == Approx(0.333f));
+
+    Patch::plugin_t masterPlugin0 = patch.masterInPlugins.at(0);
+    REQUIRE(masterPlugin0.path   == "/path/to/plugin1");
+    REQUIRE(masterPlugin0.bypass == false);
+    REQUIRE(masterPlugin0.params.at(0) == Approx(0.0f));
+    REQUIRE(masterPlugin0.params.at(1) == Approx(0.1f));
+    REQUIRE(masterPlugin0.params.at(2) == Approx(0.2f));
+
+    Patch::plugin_t masterPlugin1 = patch.masterOutPlugins.at(0);
+    REQUIRE(masterPlugin1.path == "/another/path/to/plugin2");
+    REQUIRE(masterPlugin1.bypass == true);
+    REQUIRE(masterPlugin1.params.at(0) == Approx(0.6f));
+    REQUIRE(masterPlugin1.params.at(1) == Approx(0.6f));
+    REQUIRE(masterPlugin1.params.at(2) == Approx(0.6f));
+    REQUIRE(masterPlugin1.params.at(3) == Approx(0.0f));
+    REQUIRE(masterPlugin1.params.at(4) == Approx(1.0f));
+    REQUIRE(masterPlugin1.params.at(5) == Approx(1.0f));
+    REQUIRE(masterPlugin1.params.at(6) == Approx(0.333f));
+#endif
+  }
+}