New upstream version 0.14.0~dfsg1
authorJaromír Mikeš <mira.mikes@seznam.cz>
Mon, 26 Jun 2017 09:43:19 +0000 (11:43 +0200)
committerJaromír Mikeš <mira.mikes@seznam.cz>
Mon, 26 Jun 2017 09:43:19 +0000 (11:43 +0200)
277 files changed:
ChangeLog
Makefile.am
README.md
configure.ac
src/core/channel.cpp
src/core/channel.h
src/core/clock.cpp [new file with mode: 0644]
src/core/clock.h [new file with mode: 0644]
src/core/conf.cpp
src/core/conf.h
src/core/const.h
src/core/dataStorageIni.cpp [deleted file]
src/core/dataStorageIni.h [deleted file]
src/core/dataStorageJson.cpp [deleted file]
src/core/dataStorageJson.h [deleted file]
src/core/graphics.cpp
src/core/graphics.h
src/core/init.cpp
src/core/init.h
src/core/kernelAudio.cpp
src/core/kernelAudio.h
src/core/kernelMidi.cpp
src/core/kernelMidi.h
src/core/midiChannel.cpp
src/core/midiChannel.h
src/core/midiMapConf.cpp
src/core/midiMapConf.h
src/core/mixer.cpp
src/core/mixer.h
src/core/mixerHandler.cpp
src/core/mixerHandler.h
src/core/patch.cpp
src/core/patch.h
src/core/patch_DEPR_.cpp [deleted file]
src/core/patch_DEPR_.h [deleted file]
src/core/plugin.cpp
src/core/plugin.h
src/core/pluginHost.cpp
src/core/pluginHost.h
src/core/recorder.cpp
src/core/recorder.h
src/core/sampleChannel.cpp
src/core/sampleChannel.h
src/core/storager.cpp [new file with mode: 0644]
src/core/storager.h [new file with mode: 0644]
src/core/wave.cpp
src/core/wave.h
src/core/waveFx.cpp
src/core/waveFx.h
src/glue/channel.cpp
src/glue/channel.h
src/glue/io.cpp
src/glue/io.h
src/glue/main.cpp
src/glue/main.h
src/glue/plugin.cpp
src/glue/plugin.h
src/glue/recorder.cpp [new file with mode: 0644]
src/glue/recorder.h [new file with mode: 0644]
src/glue/storage.cpp
src/glue/storage.h
src/glue/transport.cpp [new file with mode: 0644]
src/glue/transport.h [new file with mode: 0644]
src/gui/dialogs/browser/browserBase.cpp [new file with mode: 0644]
src/gui/dialogs/browser/browserBase.h [new file with mode: 0644]
src/gui/dialogs/browser/browserLoad.cpp [new file with mode: 0644]
src/gui/dialogs/browser/browserLoad.h [new file with mode: 0644]
src/gui/dialogs/browser/browserSave.cpp [new file with mode: 0644]
src/gui/dialogs/browser/browserSave.h [new file with mode: 0644]
src/gui/dialogs/gd_about.cpp
src/gui/dialogs/gd_about.h
src/gui/dialogs/gd_actionEditor.cpp
src/gui/dialogs/gd_actionEditor.h
src/gui/dialogs/gd_beatsInput.cpp
src/gui/dialogs/gd_beatsInput.h
src/gui/dialogs/gd_bpmInput.cpp
src/gui/dialogs/gd_bpmInput.h
src/gui/dialogs/gd_browser.cpp [deleted file]
src/gui/dialogs/gd_browser.h [deleted file]
src/gui/dialogs/gd_config.cpp
src/gui/dialogs/gd_config.h
src/gui/dialogs/gd_devInfo.cpp
src/gui/dialogs/gd_devInfo.h
src/gui/dialogs/gd_editor.cpp [deleted file]
src/gui/dialogs/gd_editor.h [deleted file]
src/gui/dialogs/gd_keyGrabber.cpp
src/gui/dialogs/gd_keyGrabber.h
src/gui/dialogs/gd_mainWindow.cpp
src/gui/dialogs/gd_mainWindow.h
src/gui/dialogs/gd_pluginChooser.cpp
src/gui/dialogs/gd_pluginChooser.h
src/gui/dialogs/gd_pluginList.cpp
src/gui/dialogs/gd_pluginList.h
src/gui/dialogs/gd_pluginWindow.cpp
src/gui/dialogs/gd_pluginWindow.h
src/gui/dialogs/gd_pluginWindowGUI.cpp
src/gui/dialogs/gd_pluginWindowGUI.h
src/gui/dialogs/gd_warnings.cpp
src/gui/dialogs/gd_warnings.h
src/gui/dialogs/midiIO/midiInputBase.cpp
src/gui/dialogs/midiIO/midiInputBase.h
src/gui/dialogs/midiIO/midiInputChannel.cpp
src/gui/dialogs/midiIO/midiInputChannel.h
src/gui/dialogs/midiIO/midiInputMaster.cpp
src/gui/dialogs/midiIO/midiInputMaster.h
src/gui/dialogs/midiIO/midiOutputBase.cpp
src/gui/dialogs/midiIO/midiOutputBase.h
src/gui/dialogs/midiIO/midiOutputMidiCh.cpp
src/gui/dialogs/midiIO/midiOutputMidiCh.h
src/gui/dialogs/midiIO/midiOutputSampleCh.cpp
src/gui/dialogs/midiIO/midiOutputSampleCh.h
src/gui/dialogs/sampleEditor.cpp [new file with mode: 0644]
src/gui/dialogs/sampleEditor.h [new file with mode: 0644]
src/gui/dialogs/window.cpp [new file with mode: 0644]
src/gui/dialogs/window.h [new file with mode: 0644]
src/gui/elems/actionEditor.cpp [deleted file]
src/gui/elems/actionEditor.h [deleted file]
src/gui/elems/actionEditor/action.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/action.h [new file with mode: 0644]
src/gui/elems/actionEditor/actionEditor.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/actionEditor.h [new file with mode: 0644]
src/gui/elems/actionEditor/baseActionEditor.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/baseActionEditor.h [new file with mode: 0644]
src/gui/elems/actionEditor/basePianoItem.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/basePianoItem.h [new file with mode: 0644]
src/gui/elems/actionEditor/envelopeEditor.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/envelopeEditor.h [new file with mode: 0644]
src/gui/elems/actionEditor/gridTool.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/gridTool.h [new file with mode: 0644]
src/gui/elems/actionEditor/muteEditor.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/muteEditor.h [new file with mode: 0644]
src/gui/elems/actionEditor/noteEditor.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/noteEditor.h [new file with mode: 0644]
src/gui/elems/actionEditor/pianoItem.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/pianoItem.h [new file with mode: 0644]
src/gui/elems/actionEditor/pianoItemOrphaned.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/pianoItemOrphaned.h [new file with mode: 0644]
src/gui/elems/actionEditor/pianoRoll.cpp [new file with mode: 0644]
src/gui/elems/actionEditor/pianoRoll.h [new file with mode: 0644]
src/gui/elems/baseActionEditor.cpp [deleted file]
src/gui/elems/baseActionEditor.h [deleted file]
src/gui/elems/basics/baseButton.cpp [new file with mode: 0644]
src/gui/elems/basics/baseButton.h [new file with mode: 0644]
src/gui/elems/basics/box.cpp [new file with mode: 0644]
src/gui/elems/basics/box.h [new file with mode: 0644]
src/gui/elems/basics/boxtypes.cpp
src/gui/elems/basics/boxtypes.h
src/gui/elems/basics/button.cpp [new file with mode: 0644]
src/gui/elems/basics/button.h [new file with mode: 0644]
src/gui/elems/basics/check.cpp [new file with mode: 0644]
src/gui/elems/basics/check.h [new file with mode: 0644]
src/gui/elems/basics/choice.cpp [new file with mode: 0644]
src/gui/elems/basics/choice.h [new file with mode: 0644]
src/gui/elems/basics/dial.cpp [new file with mode: 0644]
src/gui/elems/basics/dial.h [new file with mode: 0644]
src/gui/elems/basics/idButton.cpp [new file with mode: 0644]
src/gui/elems/basics/idButton.h [new file with mode: 0644]
src/gui/elems/basics/input.cpp [new file with mode: 0644]
src/gui/elems/basics/input.h [new file with mode: 0644]
src/gui/elems/basics/liquidScroll.cpp [new file with mode: 0644]
src/gui/elems/basics/liquidScroll.h [new file with mode: 0644]
src/gui/elems/basics/progress.cpp [new file with mode: 0644]
src/gui/elems/basics/progress.h [new file with mode: 0644]
src/gui/elems/basics/radio.cpp [new file with mode: 0644]
src/gui/elems/basics/radio.h [new file with mode: 0644]
src/gui/elems/basics/resizerBar.cpp [new file with mode: 0644]
src/gui/elems/basics/resizerBar.h [new file with mode: 0644]
src/gui/elems/basics/scroll.cpp
src/gui/elems/basics/scroll.h
src/gui/elems/basics/slider.cpp [new file with mode: 0644]
src/gui/elems/basics/slider.h [new file with mode: 0644]
src/gui/elems/basics/statusButton.cpp [new file with mode: 0644]
src/gui/elems/basics/statusButton.h [new file with mode: 0644]
src/gui/elems/browser.cpp
src/gui/elems/browser.h
src/gui/elems/config/tabAudio.cpp [new file with mode: 0644]
src/gui/elems/config/tabAudio.h [new file with mode: 0644]
src/gui/elems/config/tabBehaviors.cpp [new file with mode: 0644]
src/gui/elems/config/tabBehaviors.h [new file with mode: 0644]
src/gui/elems/config/tabMidi.cpp [new file with mode: 0644]
src/gui/elems/config/tabMidi.h [new file with mode: 0644]
src/gui/elems/config/tabMisc.cpp [new file with mode: 0644]
src/gui/elems/config/tabMisc.h [new file with mode: 0644]
src/gui/elems/config/tabPlugins.cpp [new file with mode: 0644]
src/gui/elems/config/tabPlugins.h [new file with mode: 0644]
src/gui/elems/envelopeEditor.cpp [deleted file]
src/gui/elems/envelopeEditor.h [deleted file]
src/gui/elems/ge_mixed.cpp [deleted file]
src/gui/elems/ge_mixed.h [deleted file]
src/gui/elems/ge_pluginBrowser.cpp [deleted file]
src/gui/elems/ge_pluginBrowser.h [deleted file]
src/gui/elems/ge_waveTools.cpp [deleted file]
src/gui/elems/ge_waveTools.h [deleted file]
src/gui/elems/ge_waveform.cpp [deleted file]
src/gui/elems/ge_waveform.h [deleted file]
src/gui/elems/ge_window.cpp [deleted file]
src/gui/elems/ge_window.h [deleted file]
src/gui/elems/mainWindow/beatMeter.cpp
src/gui/elems/mainWindow/beatMeter.h
src/gui/elems/mainWindow/keyboard/channel.cpp
src/gui/elems/mainWindow/keyboard/channel.h
src/gui/elems/mainWindow/keyboard/channelButton.cpp
src/gui/elems/mainWindow/keyboard/channelButton.h
src/gui/elems/mainWindow/keyboard/channelMode.cpp
src/gui/elems/mainWindow/keyboard/channelMode.h
src/gui/elems/mainWindow/keyboard/channelStatus.cpp
src/gui/elems/mainWindow/keyboard/channelStatus.h
src/gui/elems/mainWindow/keyboard/column.cpp
src/gui/elems/mainWindow/keyboard/column.h
src/gui/elems/mainWindow/keyboard/keyboard.cpp
src/gui/elems/mainWindow/keyboard/keyboard.h
src/gui/elems/mainWindow/keyboard/midiChannel.cpp
src/gui/elems/mainWindow/keyboard/midiChannel.h
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/keyboard/sampleChannel.h
src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp [new file with mode: 0644]
src/gui/elems/mainWindow/keyboard/sampleChannelButton.h [new file with mode: 0644]
src/gui/elems/mainWindow/mainIO.cpp
src/gui/elems/mainWindow/mainIO.h
src/gui/elems/mainWindow/mainMenu.cpp
src/gui/elems/mainWindow/mainMenu.h
src/gui/elems/mainWindow/mainTimer.cpp
src/gui/elems/mainWindow/mainTimer.h
src/gui/elems/mainWindow/mainTransport.cpp
src/gui/elems/mainWindow/mainTransport.h
src/gui/elems/midiLearner.cpp
src/gui/elems/midiLearner.h
src/gui/elems/muteEditor.cpp [deleted file]
src/gui/elems/muteEditor.h [deleted file]
src/gui/elems/noteEditor.cpp [deleted file]
src/gui/elems/noteEditor.h [deleted file]
src/gui/elems/pianoItem.cpp [deleted file]
src/gui/elems/pianoItem.h [deleted file]
src/gui/elems/pianoRoll.cpp [deleted file]
src/gui/elems/pianoRoll.h [deleted file]
src/gui/elems/pluginBrowser.cpp [new file with mode: 0644]
src/gui/elems/pluginBrowser.h [new file with mode: 0644]
src/gui/elems/sampleEditor/boostTool.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/boostTool.h [new file with mode: 0644]
src/gui/elems/sampleEditor/panTool.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/panTool.h [new file with mode: 0644]
src/gui/elems/sampleEditor/pitchTool.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/pitchTool.h [new file with mode: 0644]
src/gui/elems/sampleEditor/rangeTool.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/rangeTool.h [new file with mode: 0644]
src/gui/elems/sampleEditor/volumeTool.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/volumeTool.h [new file with mode: 0644]
src/gui/elems/sampleEditor/waveTools.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/waveTools.h [new file with mode: 0644]
src/gui/elems/sampleEditor/waveform.cpp [new file with mode: 0644]
src/gui/elems/sampleEditor/waveform.h [new file with mode: 0644]
src/gui/elems/soundMeter.cpp [new file with mode: 0644]
src/gui/elems/soundMeter.h [new file with mode: 0644]
src/main.cpp
src/utils/cocoa.h
src/utils/cocoa.mm
src/utils/deps.cpp [new file with mode: 0644]
src/utils/deps.h [new file with mode: 0644]
src/utils/fs.cpp
src/utils/fs.h
src/utils/gui.cpp
src/utils/gui.h
src/utils/log.cpp
src/utils/log.h
src/utils/math.cpp [new file with mode: 0644]
src/utils/math.h [new file with mode: 0644]
src/utils/string.cpp
src/utils/string.h
tests/catch.hpp [deleted file]
tests/conf.cpp
tests/main.cpp
tests/midiMapConf.cpp
tests/patch.cpp
tests/pluginHost.cpp
tests/recorder.cpp [new file with mode: 0644]
tests/utils.cpp
tests/wave.cpp

index 65e29a57c530123e36374c5137b3b5bd7df2b67f..4cd4cf1c4ce5938229d76e974be2d87b743bff0f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 --------------------------------------------------------------------------------
 
 
+0.14.0 --- 2017 . 05 . 29
+- Sample Editor reorganized and refactored 
+- Removed support for old ini-based patch files
+- Improved and simplified pan algorithm
+- Ability to toggle input monitoring while recording audio
+- Lots of code refactoring
+- Convert all .h headers to C++ headers
+- Update Libsndfile to version 1.0.28
+- Fix crash when recording audio
+- Fix wrong file path when exporting samples
+- Fix a bug that prevented begin/end handles to work in Sample Editor
+- Fix Sample Editor's grid value not being stored properly on close
+
+
+0.13.4 --- 2017 . 04 . 23
+- Removed support for old ini-based MIDImap files
+- Initial support for channel-based MIDI filtering
+- New Orphaned MIDI events in Piano Roll editor
+- Improve action filtering in Piano Roll editor
+- Lots of code refactoring
+- New test suite for Action Recorder
+- Fix obscure bug when overdubbing actions and a null loop occurs
+- Fix "clear all actions" menu refresh when removing items on Piano Roll
+
+
+0.13.3 --- 2017 . 03 . 25
+- Strip VST folder from Git repository
+- Fix 'Close' button's position inside MIDI input window
+- Update RtMidi to version 2.1.1
+- Improve 'free channel' function (GitHub #105)
+- New 'Clock' structure for timing operations
+- New Jack implementation with BPM sync and Rewind (GitHub #89)
+- Fix missing tracker reset on 'free channel' function (GitHub #99)
+
+
 0.13.2 --- 2017 . 01 . 14
 - MIDI learn for plugins parameters
 - Toggle hidden files in File Browser
index edb15a427c06b2b7651f94da28ac350aec2addb5..91c8b70c03bf0541673d596f75a044620f4a14be 100644 (file)
@@ -35,18 +35,16 @@ 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/dataStorageIni.h                   \
-src/core/dataStorageIni.cpp            \
-src/core/dataStorageJson.h                \
-src/core/dataStorageJson.cpp           \
+src/core/storager.h                       \
+src/core/storager.cpp                  \
+src/core/clock.h                       \
+src/core/clock.cpp                     \
 src/glue/main.h                        \
 src/glue/main.cpp                      \
 src/glue/io.h                          \
@@ -57,6 +55,12 @@ src/glue/channel.h                     \
 src/glue/channel.cpp                   \
 src/glue/plugin.h                      \
 src/glue/plugin.cpp                    \
+src/glue/transport.h                   \
+src/glue/transport.cpp                 \
+src/glue/recorder.h                    \
+src/glue/recorder.cpp                  \
+src/gui/dialogs/window.h                                \
+src/gui/dialogs/window.cpp             \
 src/gui/dialogs/gd_keyGrabber.h        \
 src/gui/dialogs/gd_keyGrabber.cpp      \
 src/gui/dialogs/gd_about.h             \
@@ -69,8 +73,6 @@ src/gui/dialogs/gd_warnings.h          \
 src/gui/dialogs/gd_warnings.cpp        \
 src/gui/dialogs/gd_bpmInput.h          \
 src/gui/dialogs/gd_bpmInput.cpp        \
-src/gui/dialogs/gd_browser.h           \
-src/gui/dialogs/gd_browser.cpp         \
 src/gui/dialogs/gd_config.h                             \
 src/gui/dialogs/gd_config.cpp          \
 src/gui/dialogs/gd_devInfo.h           \
@@ -79,91 +81,157 @@ src/gui/dialogs/gd_pluginList.h           \
 src/gui/dialogs/gd_pluginList.cpp      \
 src/gui/dialogs/gd_pluginWindow.h           \
 src/gui/dialogs/gd_pluginWindow.cpp    \
-src/gui/dialogs/gd_editor.h            \
-src/gui/dialogs/gd_editor.cpp          \
+src/gui/dialogs/sampleEditor.h         \
+src/gui/dialogs/sampleEditor.cpp       \
 src/gui/dialogs/gd_pluginWindowGUI.h   \
 src/gui/dialogs/gd_pluginWindowGUI.cpp \
 src/gui/dialogs/gd_actionEditor.h           \
 src/gui/dialogs/gd_actionEditor.cpp    \
 src/gui/dialogs/gd_pluginChooser.h     \
 src/gui/dialogs/gd_pluginChooser.cpp   \
-src/gui/dialogs/midiIO/midiOutputBase.h    \
-src/gui/dialogs/midiIO/midiOutputBase.cpp  \
-src/gui/dialogs/midiIO/midiOutputSampleCh.h    \
+src/gui/dialogs/browser/browserBase.h           \
+src/gui/dialogs/browser/browserBase.cpp         \
+src/gui/dialogs/browser/browserLoad.h           \
+src/gui/dialogs/browser/browserLoad.cpp         \
+src/gui/dialogs/browser/browserSave.h           \
+src/gui/dialogs/browser/browserSave.cpp         \
+src/gui/dialogs/midiIO/midiOutputBase.h          \
+src/gui/dialogs/midiIO/midiOutputBase.cpp        \
+src/gui/dialogs/midiIO/midiOutputSampleCh.h      \
 src/gui/dialogs/midiIO/midiOutputSampleCh.cpp    \
-src/gui/dialogs/midiIO/midiOutputMidiCh.h  \
-src/gui/dialogs/midiIO/midiOutputMidiCh.cpp  \
-src/gui/dialogs/midiIO/midiInputBase.h     \
-src/gui/dialogs/midiIO/midiInputBase.cpp   \
-src/gui/dialogs/midiIO/midiInputChannel.h     \
-src/gui/dialogs/midiIO/midiInputChannel.cpp     \
-src/gui/dialogs/midiIO/midiInputMaster.h   \
-src/gui/dialogs/midiIO/midiInputMaster.cpp   \
-src/gui/elems/midiLearner.h            \
-src/gui/elems/midiLearner.cpp          \
-src/gui/elems/ge_mixed.h               \
-src/gui/elems/ge_mixed.cpp             \
-src/gui/elems/ge_waveform.h            \
-src/gui/elems/ge_waveform.cpp          \
-src/gui/elems/browser.h                             \
-src/gui/elems/browser.cpp              \
-src/gui/elems/baseActionEditor.h       \
-src/gui/elems/baseActionEditor.cpp     \
-src/gui/elems/envelopeEditor.h         \
-src/gui/elems/envelopeEditor.cpp       \
-src/gui/elems/pianoRoll.h              \
-src/gui/elems/pianoRoll.cpp            \
-src/gui/elems/noteEditor.h                      \
-src/gui/elems/noteEditor.cpp           \
-src/gui/elems/pianoItem.h              \
-src/gui/elems/pianoItem.cpp            \
-src/gui/elems/muteEditor.h             \
-src/gui/elems/muteEditor.cpp           \
-src/gui/elems/actionEditor.h           \
-src/gui/elems/actionEditor.cpp         \
-src/gui/elems/ge_window.h                               \
-src/gui/elems/ge_window.cpp            \
-src/gui/elems/ge_waveTools.h           \
-src/gui/elems/ge_waveTools.cpp         \
-src/gui/elems/ge_pluginBrowser.h       \
-src/gui/elems/ge_pluginBrowser.cpp     \
-src/gui/elems/mainWindow/mainIO.h      \
-src/gui/elems/mainWindow/mainIO.cpp    \
-src/gui/elems/mainWindow/mainMenu.h    \
-src/gui/elems/mainWindow/mainMenu.cpp  \
-src/gui/elems/mainWindow/mainTimer.h   \
-src/gui/elems/mainWindow/mainTimer.cpp \
+src/gui/dialogs/midiIO/midiOutputMidiCh.h        \
+src/gui/dialogs/midiIO/midiOutputMidiCh.cpp      \
+src/gui/dialogs/midiIO/midiInputBase.h           \
+src/gui/dialogs/midiIO/midiInputBase.cpp         \
+src/gui/dialogs/midiIO/midiInputChannel.h        \
+src/gui/dialogs/midiIO/midiInputChannel.cpp      \
+src/gui/dialogs/midiIO/midiInputMaster.h         \
+src/gui/dialogs/midiIO/midiInputMaster.cpp       \
+src/gui/elems/midiLearner.h        \
+src/gui/elems/midiLearner.cpp      \
+src/gui/elems/browser.h                         \
+src/gui/elems/browser.cpp          \
+src/gui/elems/soundMeter.h                  \
+src/gui/elems/soundMeter.cpp       \
+src/gui/elems/pluginBrowser.h      \
+src/gui/elems/pluginBrowser.cpp    \
+src/gui/elems/sampleEditor/waveform.h               \
+src/gui/elems/sampleEditor/waveform.cpp             \
+src/gui/elems/sampleEditor/waveTools.h              \
+src/gui/elems/sampleEditor/waveTools.cpp            \
+src/gui/elems/sampleEditor/volumeTool.h             \
+src/gui/elems/sampleEditor/volumeTool.cpp           \
+src/gui/elems/sampleEditor/boostTool.h              \
+src/gui/elems/sampleEditor/boostTool.cpp            \
+src/gui/elems/sampleEditor/panTool.h                \
+src/gui/elems/sampleEditor/panTool.cpp              \
+src/gui/elems/sampleEditor/pitchTool.h              \
+src/gui/elems/sampleEditor/pitchTool.cpp            \
+src/gui/elems/sampleEditor/rangeTool.h              \
+src/gui/elems/sampleEditor/rangeTool.cpp            \
+src/gui/elems/actionEditor/baseActionEditor.h       \
+src/gui/elems/actionEditor/baseActionEditor.cpp     \
+src/gui/elems/actionEditor/envelopeEditor.h         \
+src/gui/elems/actionEditor/envelopeEditor.cpp       \
+src/gui/elems/actionEditor/pianoRoll.h              \
+src/gui/elems/actionEditor/pianoRoll.cpp            \
+src/gui/elems/actionEditor/noteEditor.h                          \
+src/gui/elems/actionEditor/noteEditor.cpp           \
+src/gui/elems/actionEditor/basePianoItem.h          \
+src/gui/elems/actionEditor/basePianoItem.cpp        \
+src/gui/elems/actionEditor/pianoItem.h              \
+src/gui/elems/actionEditor/pianoItem.cpp            \
+src/gui/elems/actionEditor/pianoItemOrphaned.h      \
+src/gui/elems/actionEditor/pianoItemOrphaned.cpp    \
+src/gui/elems/actionEditor/muteEditor.h             \
+src/gui/elems/actionEditor/muteEditor.cpp           \
+src/gui/elems/actionEditor/actionEditor.h           \
+src/gui/elems/actionEditor/actionEditor.cpp         \
+src/gui/elems/actionEditor/action.h                 \
+src/gui/elems/actionEditor/action.cpp               \
+src/gui/elems/actionEditor/gridTool.h               \
+src/gui/elems/actionEditor/gridTool.cpp             \
+src/gui/elems/mainWindow/mainIO.h          \
+src/gui/elems/mainWindow/mainIO.cpp        \
+src/gui/elems/mainWindow/mainMenu.h        \
+src/gui/elems/mainWindow/mainMenu.cpp      \
+src/gui/elems/mainWindow/mainTimer.h       \
+src/gui/elems/mainWindow/mainTimer.cpp     \
 src/gui/elems/mainWindow/mainTransport.h   \
 src/gui/elems/mainWindow/mainTransport.cpp \
-src/gui/elems/mainWindow/beatMeter.h   \
-src/gui/elems/mainWindow/beatMeter.cpp \
-src/gui/elems/mainWindow/keyboard/channelMode.h             \
-src/gui/elems/mainWindow/keyboard/channelMode.cpp           \
+src/gui/elems/mainWindow/beatMeter.h       \
+src/gui/elems/mainWindow/beatMeter.cpp     \
+src/gui/elems/mainWindow/keyboard/channelMode.h            \
+src/gui/elems/mainWindow/keyboard/channelMode.cpp          \
 src/gui/elems/mainWindow/keyboard/channelButton.h          \
 src/gui/elems/mainWindow/keyboard/channelButton.cpp        \
-src/gui/elems/mainWindow/keyboard/channelStatus.h              \
-src/gui/elems/mainWindow/keyboard/channelStatus.cpp            \
-src/gui/elems/mainWindow/keyboard/keyboard.h            \
-src/gui/elems/mainWindow/keyboard/keyboard.cpp          \
-src/gui/elems/mainWindow/keyboard/column.h              \
-src/gui/elems/mainWindow/keyboard/column.cpp            \
+src/gui/elems/mainWindow/keyboard/channelStatus.h          \
+src/gui/elems/mainWindow/keyboard/channelStatus.cpp        \
+src/gui/elems/mainWindow/keyboard/keyboard.h               \
+src/gui/elems/mainWindow/keyboard/keyboard.cpp             \
+src/gui/elems/mainWindow/keyboard/column.h                 \
+src/gui/elems/mainWindow/keyboard/column.cpp               \
 src/gui/elems/mainWindow/keyboard/sampleChannel.h          \
 src/gui/elems/mainWindow/keyboard/sampleChannel.cpp        \
 src/gui/elems/mainWindow/keyboard/midiChannel.h            \
 src/gui/elems/mainWindow/keyboard/midiChannel.cpp          \
 src/gui/elems/mainWindow/keyboard/channel.h                \
 src/gui/elems/mainWindow/keyboard/channel.cpp              \
+src/gui/elems/mainWindow/keyboard/sampleChannelButton.h    \
+src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp  \
+src/gui/elems/config/tabMisc.h         \
+src/gui/elems/config/tabMisc.cpp       \
+src/gui/elems/config/tabMidi.h         \
+src/gui/elems/config/tabMidi.cpp       \
+src/gui/elems/config/tabAudio.h        \
+src/gui/elems/config/tabAudio.cpp      \
+src/gui/elems/config/tabBehaviors.h    \
+src/gui/elems/config/tabBehaviors.cpp  \
+src/gui/elems/config/tabPlugins.h      \
+src/gui/elems/config/tabPlugins.cpp    \
 src/gui/elems/basics/scroll.h          \
 src/gui/elems/basics/scroll.cpp        \
 src/gui/elems/basics/boxtypes.h        \
 src/gui/elems/basics/boxtypes.cpp      \
+src/gui/elems/basics/baseButton.h      \
+src/gui/elems/basics/baseButton.cpp    \
+src/gui/elems/basics/statusButton.h    \
+src/gui/elems/basics/statusButton.cpp  \
+src/gui/elems/basics/button.h          \
+src/gui/elems/basics/button.cpp        \
+src/gui/elems/basics/idButton.h        \
+src/gui/elems/basics/idButton.cpp      \
+src/gui/elems/basics/resizerBar.h      \
+src/gui/elems/basics/resizerBar.cpp    \
+src/gui/elems/basics/input.h           \
+src/gui/elems/basics/input.cpp         \
+src/gui/elems/basics/liquidScroll.h    \
+src/gui/elems/basics/liquidScroll.cpp  \
+src/gui/elems/basics/choice.h          \
+src/gui/elems/basics/choice.cpp        \
+src/gui/elems/basics/dial.h            \
+src/gui/elems/basics/dial.cpp          \
+src/gui/elems/basics/box.h             \
+src/gui/elems/basics/box.cpp           \
+src/gui/elems/basics/slider.h          \
+src/gui/elems/basics/slider.cpp        \
+src/gui/elems/basics/progress.h        \
+src/gui/elems/basics/progress.cpp      \
+src/gui/elems/basics/check.h           \
+src/gui/elems/basics/check.cpp         \
+src/gui/elems/basics/radio.h           \
+src/gui/elems/basics/radio.cpp         \
 src/utils/log.h                        \
 src/utils/log.cpp                      \
+src/utils/math.h                       \
+src/utils/math.cpp                     \
 src/utils/gui.h                        \
 src/utils/gui.cpp                      \
 src/utils/gvector.h                    \
 src/utils/fs.h                         \
 src/utils/fs.cpp                       \
+src/utils/deps.h                       \
+src/utils/deps.cpp                     \
 src/utils/string.h                     \
 src/utils/string.cpp
 
@@ -183,6 +251,7 @@ endif
 # via AM_CONDITIONAL inside configure.ac.
 # Note: CPPFLAGS = C preprocessor flags, CXXFLAGS = C++ compiler flags.
 
+# TODO add -DNDEBUG for production code
 giada_CXXFLAGS = -std=c++11 -Wall -Werror
 giada_CPPFLAGS =
 
@@ -241,7 +310,7 @@ giada_CPPFLAGS +=                \
 -D__WINDOWS_DS__
 giada_LDADD = -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
   -lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \
-  -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -lpthreadGC2 -ljansson \
+  -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -ljansson \
   -limm32 -lglu32 -lshell32 -lversion -lopengl32 -loleaut32 -lshlwapi -lcomdlg32
 giada_LDFLAGS = -mwindows -static
 giada_SOURCES += resource.rc
@@ -254,7 +323,7 @@ if OSX
 # -ObjC++: Juce requires to build some Objective C code
 # -Wno-unknown-pragmas: shut up Juce even more
 giada_SOURCES += src/utils/cocoa.mm src/utils/cocoa.h
-giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas
+giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas -Wno-auto-var-id
 giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
        -lsamplerate -ljansson
 giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \
@@ -279,13 +348,14 @@ tests/patch.cpp              \
 tests/midiMapConf.cpp        \
 tests/pluginHost.cpp         \
 tests/utils.cpp              \
+tests/recorder.cpp           \
 src/core/conf.cpp            \
 src/core/wave.cpp            \
 src/core/midiMapConf.cpp     \
 src/core/patch.cpp           \
 src/core/plugin.cpp          \
-src/core/dataStorageIni.cpp  \
-src/core/dataStorageJson.cpp \
+src/core/storager.cpp        \
+src/core/recorder.cpp        \
 src/utils/fs.cpp             \
 src/utils/string.cpp         \
 src/utils/log.cpp
index a35a25d604ca8aaa091bde76ed4c35bf3a7f5332..4b36cda7ce6f1fdd3333d9f41b471d6241af867c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@ http://www.giadamusic.com/forum
 
 ## Copyright
 
-Giada is Copyright (C) 2010-2016 by Giovanni A. Zuliani | Monocasual
+Giada is Copyright (C) 2010-2017 by Giovanni A. Zuliani | Monocasual
 
 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.
 
index 16fd0c34e808a171354a6d42b55df12791def72d..1833327e6e36740a04aa36cb951a6eff05f93693 100644 (file)
@@ -55,6 +55,18 @@ AC_ARG_ENABLE(
        [AM_CONDITIONAL(WITH_VST, false)]
 )
 
+
+# ------------------------------------------------------------------------------
+
+# --debug. Enable debug compilation
+
+AC_ARG_ENABLE(
+       [debug],
+       AS_HELP_STRING([--enable-debug], [enable debug mode (asserts, ...)]),
+  [],
+       [AC_DEFINE(NDEBUG)]
+)
+
 # ------------------------------------------------------------------------------
 
 # test if files needed for Travis CI are present. If so, define a new macro
@@ -108,7 +120,7 @@ AC_LANG_POP
 
 AC_LANG_PUSH([C++])
 AC_CHECK_HEADER(
-       [RtMidi.h],
+       [rtmidi/RtMidi.h],
        [],
        [AC_MSG_ERROR([library 'rtMidi' not found!])]
 )
index 95ad99a1df993ce9f80ff6f548880117dfcefebb..f519e4a4ec6bbd20a3142dd2659a0e50e6f4c842 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
+#include <cstring>
 #include "../utils/log.h"
 #include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "const.h"
 #include "channel.h"
 #include "pluginHost.h"
 #include "plugin.h"
 #include "kernelMidi.h"
-#include "patch_DEPR_.h"
 #include "patch.h"
+#include "clock.h"
 #include "wave.h"
 #include "mixer.h"
 #include "mixerHandler.h"
 #include "midiMapConf.h"
 
 
-extern Recorder   G_Recorder;
-extern KernelMidi G_KernelMidi;
+using std::string;
+using namespace giada::m;
 
 
-Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf)
-#if defined(WITH_VST)
-: pluginHost(nullptr),
-#else
-:
-#endif
-  midiMapConf    (midiMapConf),
-  bufferSize     (bufferSize),
+Channel::Channel(int type, int status, int bufferSize)
+: bufferSize     (bufferSize),
+  midiFilter     (-1),
+  pan            (0.5f),
   type           (type),
-       status         (status),
-       key            (0),
-  volume         (DEFAULT_VOL),
+  status         (status),
+  key            (0),
+  volume         (G_DEFAULT_VOL),
   volume_i       (1.0f),
   volume_d       (0.0f),
-  panLeft        (1.0f),
-  panRight       (1.0f),
   mute_i         (false),
   mute_s         (false),
   mute           (false),
@@ -90,7 +85,7 @@ Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf)
   vChan = (float *) malloc(bufferSize * sizeof(float));
        if (!vChan)
                gu_log("[Channel::allocVchan] unable to alloc memory for vChan\n");
-       memset(vChan, 0, bufferSize * sizeof(float));
+       std::memset(vChan, 0, bufferSize * sizeof(float));
 }
 
 
@@ -114,8 +109,7 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex)
   volume          = src->volume;
   volume_i        = src->volume_i;
   volume_d        = src->volume_d;
-  panLeft         = src->panLeft;
-  panRight        = src->panRight;
+  pan             = src->pan;
   mute_i          = src->mute_i;
   mute_s          = src->mute_s;
   mute            = src->mute;
@@ -139,17 +133,17 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex)
 
 #ifdef WITH_VST
   for (unsigned i=0; i<src->plugins.size(); i++)
-    pluginHost->clonePlugin(src->plugins.at(i), PluginHost::CHANNEL,
+    pluginHost::clonePlugin(src->plugins.at(i), pluginHost::CHANNEL,
       pluginMutex, this);
 #endif
 
   /* clone actions */
 
-  for (unsigned i=0; i<G_Recorder.global.size(); i++) {
-    for (unsigned k=0; k<G_Recorder.global.at(i).size(); k++) {
-      Recorder::action *a = G_Recorder.global.at(i).at(k);
+  for (unsigned i=0; i<recorder::global.size(); i++) {
+    for (unsigned k=0; k<recorder::global.at(i).size(); k++) {
+      recorder::action *a = recorder::global.at(i).at(k);
       if (a->chan == src->index) {
-        G_Recorder.rec(index, a->type, a->frame, a->iValue, a->fValue);
+        recorder::rec(index, a->type, a->frame, a->iValue, a->fValue);
         hasActions = true;
       }
     }
@@ -160,7 +154,7 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex)
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg)
+void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t &msg)
 {
        gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
                learn, msg.channel, msg.value, msg.offset);
@@ -174,30 +168,7 @@ void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg
         * send it. */
 
        out |= msg.value | (msg.channel << 24);
-       G_KernelMidi.send(out);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::readPatchMidiIn_DEPR_(int i, Patch_DEPR_ &patch)
-{
-       midiIn         = patch.getMidiValue(i, "In");
-       midiInKeyPress = patch.getMidiValue(i, "InKeyPress");
-       midiInKeyRel   = patch.getMidiValue(i, "InKeyRel");
-  midiInKill     = patch.getMidiValue(i, "InKill");
-  midiInVolume   = patch.getMidiValue(i, "InVolume");
-  midiInMute     = patch.getMidiValue(i, "InMute");
-  midiInSolo     = patch.getMidiValue(i, "InSolo");
-}
-
-void Channel::readPatchMidiOut_DEPR_(int i, Patch_DEPR_ &patch)
-{
-       midiOutL        = patch.getMidiValue(i, "OutL");
-       midiOutLplaying = patch.getMidiValue(i, "OutLplaying");
-       midiOutLmute    = patch.getMidiValue(i, "OutLmute");
-       midiOutLsolo    = patch.getMidiValue(i, "OutLsolo");
+       kernelMidi::send(out);
 }
 
 
@@ -213,9 +184,9 @@ bool Channel::isPlaying()
 /* -------------------------------------------------------------------------- */
 
 
-int Channel::writePatch(int i, bool isProject, Patch *patch)
+int Channel::writePatch(int i, bool isProject)
 {
-       Patch::channel_t pch;
+       patch::channel_t pch;
        pch.type            = type;
        pch.key             = key;
        pch.index           = index;
@@ -224,8 +195,7 @@ int Channel::writePatch(int i, bool isProject, Patch *patch)
        pch.mute_s          = mute_s;
        pch.solo            = solo;
        pch.volume          = volume;
-       pch.panLeft         = panLeft;
-       pch.panRight        = panRight;
+       pch.pan             = pan;
        pch.midiIn          = midiIn;
        pch.midiInKeyPress  = midiInKeyPress;
        pch.midiInKeyRel    = midiInKeyRel;
@@ -239,11 +209,11 @@ int Channel::writePatch(int i, bool isProject, Patch *patch)
        pch.midiOutLmute    = midiOutLmute;
        pch.midiOutLsolo    = midiOutLsolo;
 
-       for (unsigned i=0; i<G_Recorder.global.size(); i++) {
-               for (unsigned k=0; k<G_Recorder.global.at(i).size(); k++) {
-                       Recorder::action *action = G_Recorder.global.at(i).at(k);
+       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;
+                               patch::action_t pac;
                                pac.type   = action->type;
                    pac.frame  = action->frame;
                    pac.fValue = action->fValue;
@@ -255,10 +225,10 @@ int Channel::writePatch(int i, bool isProject, Patch *patch)
 
 #ifdef WITH_VST
 
-       unsigned numPlugs = pluginHost->countPlugins(PluginHost::CHANNEL, this);
+       unsigned numPlugs = pluginHost::countPlugins(pluginHost::CHANNEL, this);
        for (unsigned i=0; i<numPlugs; i++) {
-               Plugin *pPlugin = pluginHost->getPluginByIndex(i, PluginHost::CHANNEL, this);
-               Patch::plugin_t pp;
+               Plugin *pPlugin = pluginHost::getPluginByIndex(i, pluginHost::CHANNEL, this);
+               patch::plugin_t pp;
                pp.path   = pPlugin->getUniqueId();
     pp.bypass = pPlugin->isBypassed();
                for (int k=0; k<pPlugin->getNumParameters(); k++)
@@ -270,20 +240,20 @@ int Channel::writePatch(int i, bool isProject, Patch *patch)
 
 #endif
 
-       patch->channels.push_back(pch);
+       patch::channels.push_back(pch);
 
-       return patch->channels.size() - 1;
+       return patch::channels.size() - 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Channel::readPatch(const string &path, int i, Patch *patch,
-    pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
+int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex,
+  int samplerate, int rsmpQuality)
 {
        int ret = 1;
-       Patch::channel_t *pch = &patch->channels.at(i);
+       patch::channel_t *pch = &patch::channels.at(i);
        key             = pch->key;
        type            = pch->type;
        index           = pch->index;
@@ -291,8 +261,7 @@ int Channel::readPatch(const string &path, int i, Patch *patch,
        mute_s          = pch->mute_s;
        solo            = pch->solo;
        volume          = pch->volume;
-       panLeft         = pch->panLeft;
-       panRight        = pch->panRight;
+       pan             = pch->pan;
        midiIn          = pch->midiIn;
        midiInKeyPress  = pch->midiInKeyPress;
        midiInKeyRel    = pch->midiInKeyRel;
@@ -306,16 +275,16 @@ int Channel::readPatch(const string &path, int i, Patch *patch,
        midiOutLsolo    = pch->midiOutLsolo;
 
        for (unsigned k=0; k<pch->actions.size(); k++) {
-               Patch::action_t *ac = &pch->actions.at(k);
-               G_Recorder.rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
+               patch::action_t *ac = &pch->actions.at(k);
+               recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
     hasActions = true;
        }
 
 #ifdef WITH_VST
 
        for (unsigned k=0; k<pch->plugins.size(); k++) {
-               Patch::plugin_t *ppl = &pch->plugins.at(k);
-               Plugin *plugin = pluginHost->addPlugin(ppl->path, PluginHost::CHANNEL,
+               patch::plugin_t *ppl = &pch->plugins.at(k);
+               Plugin *plugin = pluginHost::addPlugin(ppl->path, pluginHost::CHANNEL,
       pluginMutex, this);
     if (plugin == nullptr) {
       ret &= 0;
@@ -344,9 +313,9 @@ void Channel::sendMidiLmute()
        if (!midiOutL || midiOutLmute == 0x0)
                return;
        if (mute)
-               sendMidiLmessage(midiOutLsolo, midiMapConf->muteOn);
+               sendMidiLmessage(midiOutLsolo, midimap::muteOn);
        else
-               sendMidiLmessage(midiOutLsolo, midiMapConf->muteOff);
+               sendMidiLmessage(midiOutLsolo, midimap::muteOff);
 }
 
 
@@ -358,9 +327,9 @@ void Channel::sendMidiLsolo()
        if (!midiOutL || midiOutLsolo == 0x0)
                return;
        if (solo)
-               sendMidiLmessage(midiOutLsolo, midiMapConf->soloOn);
+               sendMidiLmessage(midiOutLsolo, midimap::soloOn);
        else
-               sendMidiLmessage(midiOutLsolo, midiMapConf->soloOff);
+               sendMidiLmessage(midiOutLsolo, midimap::soloOff);
 }
 
 
@@ -373,16 +342,16 @@ void Channel::sendMidiLplay()
                return;
        switch (status) {
                case STATUS_OFF:
-                       sendMidiLmessage(midiOutLplaying, midiMapConf->stopped);
+                       sendMidiLmessage(midiOutLplaying, midimap::stopped);
                        break;
                case STATUS_PLAY:
-                       sendMidiLmessage(midiOutLplaying, midiMapConf->playing);
+                       sendMidiLmessage(midiOutLplaying, midimap::playing);
                        break;
                case STATUS_WAIT:
-                       sendMidiLmessage(midiOutLplaying, midiMapConf->waiting);
+                       sendMidiLmessage(midiOutLplaying, midimap::waiting);
                        break;
                case STATUS_ENDING:
-                       sendMidiLmessage(midiOutLplaying, midiMapConf->stopping);
+                       sendMidiLmessage(midiOutLplaying, midimap::stopping);
        }
 }
 
@@ -397,29 +366,56 @@ void Channel::receiveMidi(uint32_t msg)
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef WITH_VST
+void Channel::setPan(float v)
+{
+  if (v > 1.0f)
+    pan = 1.0f;
+  else 
+  if (v < 0.0f)
+    pan = 0.0f;
+  else
+    pan = v;
+}
 
-juce::MidiBuffer &Channel::getPluginMidiEvents()
+
+float Channel::getPan()
 {
-  return midiBuffer;
+  return pan;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::clearMidiBuffer()
+float Channel::calcPanning(int ch)
 {
-  midiBuffer.clear();
+  if (pan == 0.5f) // center: nothing to do
+    return 1.0;
+  if (ch == 0)
+    return 1.0 - pan;
+  else  // channel 1
+    return pan; 
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::setPluginHost(PluginHost *pluginHost)
+#ifdef WITH_VST
+
+juce::MidiBuffer &Channel::getPluginMidiEvents()
 {
-  this->pluginHost = pluginHost;
+  return midiBuffer;
 }
 
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::clearMidiBuffer()
+{
+  midiBuffer.clear();
+}
+
+
 #endif
index 2df3d14804367fc5940ef9e4749b5589451f5038..96491ad23fca486e87c168c4023b4f883c31d823 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef CHANNEL_H
-#define CHANNEL_H
+#ifndef G_CHANNEL_H
+#define G_CHANNEL_H
 
 
 #include <vector>
+#include <string>
 #include <pthread.h>
 #include "midiMapConf.h"
 #include "recorder.h"
@@ -41,8 +40,9 @@
 #endif
 
 
-using std::vector;
-using std::string;
+class Plugin;
+class MidiMapConf;
+class geChannel;
 
 
 class Channel
@@ -51,11 +51,6 @@ protected:
 
 #ifdef WITH_VST
 
-       /* pluginHost
-        * Pointer to PluginHost class, which manages and processes plugins. */
-
-       class PluginHost *pluginHost;
-
        /* MidiBuffer contains MIDI events. When ready, events are sent to
         * each plugin in the channel. This is available for any kind of
         * channel, but it makes sense only for MIDI channels. */
@@ -64,25 +59,33 @@ protected:
 
 #endif
 
-       /* MidiMapConf
-        * Pointer to MidiMapConf. It deals with Midi lightning operations. */
-
-       class MidiMapConf *midiMapConf;
-
        /* bufferSize
         * size of every buffer in this channel (vChan, pChan) */
 
        int bufferSize;
 
+  /* midiFilter
+  Which MIDI channel should be filtered out when receiving MIDI messages. -1
+  means 'all'. */
+
+  int midiFilter;
+
+       float pan;
+
        /* sendMidiLMessage
-        * compose a MIDI message by merging bytes from MidiMap conf class, and send
-        * it to KernelMidi. */
+       Composes a MIDI message by merging bytes from MidiMap conf class, and sends it 
+       to KernelMidi. */
 
-       void sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg);
+       void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t &msg);
+
+       /* calcPanning
+       Given an audio channel (stereo: 0 or 1) computes the current panning value. */
+
+       float calcPanning(int ch);
 
 public:
 
-       Channel(int type, int status, int bufferSize, class MidiMapConf *midiMapConf);
+       Channel(int type, int status, int bufferSize);
 
        virtual ~Channel();
 
@@ -94,10 +97,8 @@ public:
        /* readPatch
         * Fill channel with data from patch. */
 
-       virtual int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
-                       int samplerate, int rsmpQuality) = 0;
-       virtual int readPatch(const string &basePath, int i, class Patch *patch,
-                       pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
+       virtual int readPatch(const std::string &basePath, int i,
+    pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
 
        /* process
        Merges vChannels into buffer, plus plugin processing (if any). Warning:
@@ -146,7 +147,7 @@ public:
         * mixer::channels, used by recorder. LocalFrame = frame within the current
    * buffer.  */
 
-       virtual void quantize(int index, int localFrame, class Mixer *m) = 0;
+       virtual void quantize(int index, int localFrame) = 0;
 
        /* onZero
         * action to do when frame goes to zero, i.e. sequencer restart. */
@@ -166,8 +167,8 @@ public:
 
         // TODO - quantize is useless!
 
-       virtual void parseAction(Recorder::action *a, int localFrame, int globalFrame,
-                       int quantize, bool mixerIsRunning) = 0;
+       virtual void parseAction(giada::m::recorder::action *a, int localFrame,
+    int globalFrame, int quantize, bool mixerIsRunning) = 0;
 
        /* rewind
         * rewind channel when rewind button is pressed. */
@@ -190,7 +191,7 @@ public:
         * Fill a patch with channel values. Returns the index of the last
         * Patch::channel_t added. */
 
-       virtual int writePatch(int i, bool isProject, class Patch *patch);
+       virtual int writePatch(int i, bool isProject);
 
        /* receiveMidi
         * Receives and processes midi messages from external devices. */
@@ -206,8 +207,6 @@ public:
        float   volume;                // global volume
        float   volume_i;              // internal volume
        float   volume_d;              // delta volume (for envelope)
-       float   panLeft;
-       float   panRight;
        bool    mute_i;                // internal mute
        bool      mute_s;                // previous mute status after being solo'd
        bool    mute;                  // global mute
@@ -217,7 +216,7 @@ public:
        bool    armed;                                                     // armed for recording
        int       recStatus;             // status of recordings (waiting, ending, ...)
        float  *vChan;                 // virtual channel
-  class   geChannel *guiChannel; // pointer to a gChannel object, part of the GUI
+  geChannel *guiChannel;         // pointer to a gChannel object, part of the GUI
 
        // TODO - midi structs, please
 
@@ -241,7 +240,7 @@ public:
   uint32_t midiOutLsolo;
 
 #ifdef WITH_VST
-  vector <class Plugin *> plugins;
+  std::vector <Plugin *> plugins;
 #endif
 
 
@@ -252,13 +251,6 @@ public:
 
        bool isPlaying();
 
-       /* readPatchMidiIn
-        * read from patch all midi-related parameters such as keypress, mute
-        * and so on. */
-
-       void readPatchMidiIn_DEPR_(int i, class Patch_DEPR_ &patch);
-       void readPatchMidiOut_DEPR_(int i, class Patch_DEPR_ &patch);
-
        /* sendMidiL*
         * send MIDI lightning events to a physical device. */
 
@@ -266,13 +258,10 @@ public:
        void sendMidiLsolo();
        void sendMidiLplay();
 
-#ifdef WITH_VST
-
-       /* SetPluginHost
-        * A neat trick to avoid duplicated constructors (with and without pointer
-        * to PluginHost). */
+       void setPan(float v);
+       float getPan();
 
-       void setPluginHost(class PluginHost *pluginHost);
+#ifdef WITH_VST
 
        /* getPluginMidiEvents
         * Return a reference to midiBuffer stack. This is available for any kind of
diff --git a/src/core/clock.cpp b/src/core/clock.cpp
new file mode 100644 (file)
index 0000000..fce225a
--- /dev/null
@@ -0,0 +1,440 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cassert>
+#include "../glue/transport.h"
+#include "../glue/main.h"
+#include "conf.h"
+#include "const.h"
+#include "kernelAudio.h"
+#include "kernelMidi.h"
+#include "clock.h"
+
+
+namespace giada {
+namespace m {
+namespace clock
+{
+namespace
+{
+bool  running  = false;
+float bpm      = G_DEFAULT_BPM;
+int   bars     = G_DEFAULT_BARS;
+int   beats    = G_DEFAULT_BEATS;
+int   quantize = G_DEFAULT_QUANTIZE;
+int   quanto   = 1;           // quantizer step
+int   framesPerBar      = 0;  // frames in one bar
+int   framesPerBeat     = 0;  // frames in one beat
+int   framesInSequencer = 0;  // frames in the whole sequencer
+int   totalFrames  = 0;       // frames in the selected range (e.g. 4/4)
+int   currentFrame = 0;
+int   currentBeat  = 0;
+
+int midiTCrate    = 0;      // send MTC data every midiTCrate frames
+int midiTCframes  = 0;
+int midiTCseconds = 0;
+int midiTCminutes = 0;
+int midiTChours   = 0;
+
+#ifdef __linux__
+kernelAudio::JackState jackStatePrev;
+#endif
+
+
+void updateQuanto()
+{
+  if (quantize != 0)
+    quanto = framesPerBeat / quantize;
+  if (quanto % 2 != 0)
+    quanto++;
+}
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init(int sampleRate, float midiTCfps)
+{
+  midiTCrate = (sampleRate / midiTCfps) * 2;  // stereo values
+  updateFrameBars();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isRunning()
+{
+  return running;
+}
+
+
+bool quantoHasPassed()
+{
+  return currentFrame % (quanto) == 0;
+}
+
+
+bool isOnBar()
+{
+  /* A bar cannot occur at frame 0. That's the first beat. */
+  return currentFrame % framesPerBar == 0 && currentFrame != 0;
+}
+
+
+bool isOnBeat()
+{
+  /* Skip frame 0: it is intended as 'first beat'. */
+  /* TODO - this is wrong! */
+  return currentFrame % framesPerBeat == 0 && currentFrame > 0;
+}
+
+
+bool isOnFirstBeat()
+{
+  return currentFrame == 0;
+}
+
+
+void start()
+{
+  running = true;
+       if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
+               kernelMidi::send(MIDI_START, -1, -1);
+               kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
+       }
+}
+
+
+void stop()
+{
+  running = false;
+  if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+       kernelMidi::send(MIDI_STOP, -1, -1);
+}
+
+
+void setBpm(float b)
+{
+  if (b < G_MIN_BPM)
+    b = G_MIN_BPM;
+  bpm = b;
+       updateFrameBars();
+}
+
+
+void setBars(int newBars)
+{
+  /* Bars cannot be greater than beats and must be a sub multiple of beats. If
+  not, approximate to the nearest (and greater) value available. */
+
+  if (newBars > beats)
+    bars = beats;
+  else if (newBars <= 0)
+    bars = 1;
+  else if (beats % newBars != 0) {
+    bars = newBars + (beats % newBars);
+    if (beats % bars != 0) // it could be an odd value, let's check it (and avoid it)
+      bars = bars - (beats % bars);
+  }
+  else
+    bars = newBars;
+}
+
+
+void setBeats(int b)
+{
+  if (b > G_MAX_BEATS)
+               beats = G_MAX_BEATS;
+       else if (b < 1)
+    beats = 1;
+       else
+    beats = b;
+}
+
+
+void setQuantize(int q)
+{
+  quantize = q;
+  updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void incrCurrentFrame()
+{
+  currentFrame += 2;
+  if (currentFrame > totalFrames) {
+               currentFrame = 0;
+               currentBeat  = 0;
+       }
+  else
+  if (isOnBeat())
+    currentBeat++;
+}
+
+
+void rewind()
+{
+  currentFrame = 0;
+  currentBeat = 0;
+  sendMIDIrewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void updateFrameBars()
+{
+       /* seconds ....... total time of play (in seconds) of the whole
+        *                 sequencer. 60 / bpm == how many seconds lasts one bpm
+        * totalFrames ... loop length in frames, x2 because it's stereo
+        * framesPerBar .. n. of frames within a bar
+        * framesPerBeat . n. of frames within a beat
+   * framesInSeq ... number of frames in the whole sequencer */
+
+       float seconds     = (60.0f / bpm) * beats;
+       totalFrames       = conf::samplerate * seconds * 2;
+       framesPerBar      = totalFrames / bars;
+       framesPerBeat     = totalFrames / beats;
+       framesInSequencer = framesPerBeat * G_MAX_BEATS;
+
+       /* big troubles if frames are odd. */
+
+       if (totalFrames % 2 != 0)
+               totalFrames--;
+       if (framesPerBar % 2 != 0)
+               framesPerBar--;
+       if (framesPerBeat % 2 != 0)
+               framesPerBeat--;
+
+  updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendMIDIsync()
+{
+  /* TODO - only Master (_M) is implemented so far. */
+
+       if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
+               if (currentFrame % (framesPerBeat/24) == 0)
+                       kernelMidi::send(MIDI_CLOCK, -1, -1);
+    return;
+  }
+
+       if (conf::midiSync == MIDI_SYNC_MTC_M) {
+
+               /* check if a new timecode frame has passed. If so, send MIDI TC
+                * quarter frames. 8 quarter frames, divided in two branches:
+                * 1-4 and 5-8. We check timecode frame's parity: if even, send
+                * range 1-4, if odd send 5-8. */
+
+               if (currentFrame % midiTCrate != 0)  // no timecode frame passed
+      return;
+
+               /* frame low nibble
+                * frame high nibble
+                * seconds low nibble
+                * seconds high nibble */
+
+               if (midiTCframes % 2 == 0) {
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F)  | 0x00, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4)    | 0x10, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4)   | 0x30, -1);
+               }
+
+               /* minutes low nibble
+                * minutes high nibble
+                * hours low nibble
+                * hours high nibble SMPTE frame rate */
+
+               else {
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4)   | 0x50, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F)   | 0x60, -1);
+                       kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4)     | 0x70, -1);
+               }
+
+               midiTCframes++;
+
+               /* check if total timecode frames are greater than timecode fps:
+                * if so, a second has passed */
+
+               if (midiTCframes > conf::midiTCfps) {
+                       midiTCframes = 0;
+                       midiTCseconds++;
+                       if (midiTCseconds >= 60) {
+                               midiTCminutes++;
+                               midiTCseconds = 0;
+                               if (midiTCminutes >= 60) {
+                                       midiTChours++;
+                                       midiTCminutes = 0;
+                               }
+                       }
+                       //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+               }
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendMIDIrewind()
+{
+       midiTCframes  = 0;
+       midiTCseconds = 0;
+       midiTCminutes = 0;
+       midiTChours   = 0;
+
+       /* For cueing the slave to a particular start point, Quarter Frame
+        * messages are not used. Instead, an MTC Full Frame message should
+        * be sent. The Full Frame is a SysEx message that encodes the entire
+        * SMPTE time in one message */
+
+       if (conf::midiSync == MIDI_SYNC_MTC_M) {
+               kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00);  // send msg on channel 0
+               kernelMidi::send(0x01, 0x01, 0x00);        // hours 0
+               kernelMidi::send(0x00, 0x00, 0x00);        // mins, secs, frames 0
+               kernelMidi::send(MIDI_EOX, -1, -1);        // end of sysex
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef __linux__
+
+void recvJackSync()
+{
+  kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
+
+  if (jackState.running != jackStatePrev.running) {
+    if (jackState.running) {
+      if (!isRunning())
+        glue_startSeq(false); // not from UI
+    }
+    else {
+      if (isRunning())
+        glue_stopSeq(false); // not from UI
+    }
+  }
+  if (jackState.bpm != jackStatePrev.bpm)
+    if (jackState.bpm > 1.0f)  // 0 bpm if Jack does not send that info
+      glue_setBpm(jackState.bpm);
+
+  if (jackState.frame == 0 && jackState.frame != jackStatePrev.frame)
+    glue_rewindSeq(false, false);  // not from UI, don't notify jack (avoid loop)
+
+  jackStatePrev = jackState;
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int getCurrentFrame()
+{
+  return currentFrame;
+}
+
+
+int getTotalFrames()
+{
+  return totalFrames;
+}
+
+
+int getCurrentBeat()
+{
+  return currentBeat;
+}
+
+
+int getQuantize()
+{
+  return quantize;
+}
+
+
+float getBpm()
+{
+  return bpm;
+}
+
+
+int getBeats()
+{
+  return beats;
+}
+
+
+int getBars()
+{
+  return bars;
+}
+
+
+int getQuanto()
+{
+  return quanto;
+}
+
+
+int getFramesPerBar()
+{
+  return framesPerBar;
+}
+
+
+int getFramesPerBeat()
+{
+  return framesPerBeat;
+}
+
+
+int getFramesInSequencer()
+{
+  return framesInSequencer;
+}
+
+
+}}}; // giada::m::clock::
diff --git a/src/core/clock.h b/src/core/clock.h
new file mode 100644 (file)
index 0000000..fd39f46
--- /dev/null
@@ -0,0 +1,100 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_CLOCK_H
+#define G_CLOCK_H
+
+
+class Conf;
+class KernelMidi;
+class KernelAudio;
+
+
+namespace giada {
+namespace m {
+namespace clock
+{
+void init(int sampleRate, float midiTCfps);
+
+/* sendMIDIsync
+Generates MIDI sync output data. */
+
+void sendMIDIsync();
+
+/* sendMIDIrewind
+Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */
+
+void sendMIDIrewind();
+
+#ifdef __linux__
+void recvJackSync();
+#endif
+
+float getBpm();
+int getBeats();
+int getBars();
+int getCurrentFrame();
+int getCurrentBeat();
+int getFramesPerBar();
+int getFramesPerBeat();
+int getTotalFrames();
+int getFramesInSequencer();
+int getQuantize();
+int getQuanto();
+
+/* incrCurrentFrame
+Increases current frame of a stereo step (+2). */
+
+void incrCurrentFrame();
+
+/* quantoHasPassed
+Tells whether a quanto unit has passed yet. */
+
+bool quantoHasPassed();
+
+/* updateFrameBars
+Updates bpm, frames, beats and so on. */
+
+void updateFrameBars();
+
+void setBpm(float b);
+void setBars(int b);
+void setBeats(int b);
+void setQuantize(int q);
+
+bool isRunning();
+bool isOnBeat();
+bool isOnBar();
+bool isOnFirstBeat();
+
+void rewind();
+void start();
+void stop();
+}}}; // giada::m::clock::
+
+
+#endif
index 1a690ace4700b82dbd1d6cbc8f1b4a2dca024857..01e777a73b53377f0f562fa8fb4ffd8201206710 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * conf
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include <string>
-#include "conf.h"
-#include "const.h"
 #include "../utils/fs.h"
 #include "../utils/log.h"
+#include "storager.h"
+#include "const.h"
+#include "conf.h"
 
 
 using std::string;
 
 
-Conf::Conf()
+namespace giada {
+namespace m {
+namespace conf
 {
-       /* Initialize confFilePath, i.e. the configuration file. In windows it is in
-        * the same dir of the .exe, while in Linux and OS X in ~/.giada */
-
-#if defined(__linux__) || defined(__APPLE__)
+namespace
+{
+string confFilePath = "";
+string confDirPath = "";
 
-       confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
-       confDirPath  = gu_getHomePath() + G_SLASH;
 
-#elif defined(_WIN32)
+/* -------------------------------------------------------------------------- */
 
-       confFilePath = CONF_FILENAME;
-       confDirPath  = "";
+/* sanitize
+Avoids funky values from config file. */
 
+void sanitize()
+{
+       if (!(soundSystem & G_SYS_API_ANY)) soundSystem = G_DEFAULT_SOUNDSYS;
+       if (soundDeviceOut < 0) soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+       if (soundDeviceIn < -1) soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
+       if (channelsOut < 0) channelsOut = 0;
+       if (channelsIn < 0)  channelsIn  = 0;
+       if (buffersize < 8 || buffersize > 4096) buffersize = G_DEFAULT_BUFSIZE;
+       if (delayComp < 0) delayComp = G_DEFAULT_DELAYCOMP;
+       if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_SYSTEM;
+       if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
+       if (midiPortIn < -1) midiPortIn = G_DEFAULT_MIDI_PORT_IN;
+       if (browserX < 0) browserX = 0;
+       if (browserY < 0) browserY = 0;
+       if (browserW < 396) browserW = 396;
+       if (browserH < 302) browserH = 302;
+       if (actionEditorX < 0) actionEditorX = 0;
+       if (actionEditorY < 0) actionEditorY = 0;
+       if (actionEditorW < 640) actionEditorW = 640;
+       if (actionEditorH < 176) actionEditorH = 176;
+       if (actionEditorZoom < 100) actionEditorZoom = 100;
+       if (actionEditorGridVal < 0) actionEditorGridVal = 0;
+       if (actionEditorGridOn < 0) actionEditorGridOn = 0;
+       if (pianoRollH <= 0) pianoRollH = 422;
+  if (sampleEditorX < 0) sampleEditorX = 0;
+       if (sampleEditorY < 0) sampleEditorY = 0;
+       if (sampleEditorW < 500) sampleEditorW = 500;
+       if (sampleEditorH < 292) sampleEditorH = 292;
+       if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
+       if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
+  if (midiInputX < 0) midiInputX = 0;
+  if (midiInputY < 0) midiInputY = 0;
+  if (midiInputW < G_DEFAULT_MIDI_INPUT_UI_W) midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
+  if (midiInputH < G_DEFAULT_MIDI_INPUT_UI_H) midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
+       if (configX < 0) configX = 0;
+       if (configY < 0) configY = 0;
+       if (pluginListX < 0) pluginListX = 0;
+       if (pluginListY < 0) pluginListY = 0;
+#ifdef WITH_VST
+       if (pluginChooserW < 640) pluginChooserW = 640;
+       if (pluginChooserH < 480) pluginChooserW = 480;
 #endif
+  if (bpmX < 0) bpmX = 0;
+       if (bpmY < 0) bpmY = 0;
+       if (beatsX < 0) beatsX = 0;
+       if (beatsY < 0) beatsY = 0;
+       if (aboutX < 0) aboutX = 0;
+       if (aboutY < 0) aboutY = 0;
+       if (samplerate < 8000) samplerate = G_DEFAULT_SAMPLERATE;
+       if (rsmpQuality < 0 || rsmpQuality > 4) rsmpQuality = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Conf::createConfigFolder()
+/* createConfigFolder
+Creates local folder where to put the configuration file. Path differs from OS
+to OS. */
+
+int createConfigFolder()
 {
 #if defined(__linux__) || defined(__APPLE__)
 
        if (gu_dirExists(confDirPath))
                return 1;
 
-       gu_log("[Conf::createConfigFolder] .giada folder not present. Updating...\n");
+       gu_log("[conf::createConfigFolder] .giada folder not present. Updating...\n");
 
        if (gu_mkdir(confDirPath)) {
-               gu_log("[Conf::createConfigFolder] status: ok\n");
+               gu_log("[conf::createConfigFolder] status: ok\n");
                return 1;
        }
        else {
-               gu_log("[Conf::createConfigFolder] status: error!\n");
+               gu_log("[conf::createConfigFolder] status: error!\n");
                return 0;
        }
 
@@ -84,199 +136,247 @@ int Conf::createConfigFolder()
 #endif
 }
 
+}; // {anonymous}
+
 
 /* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+string header = "GIADACFG";
+
+int  logMode        = LOG_MODE_MUTE;
+int  soundSystem    = G_DEFAULT_SOUNDSYS;
+int  soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+int  soundDeviceIn  = G_DEFAULT_SOUNDDEV_IN;
+int  channelsOut    = 0;
+int  channelsIn     = 0;
+int  samplerate     = G_DEFAULT_SAMPLERATE;
+int  buffersize     = G_DEFAULT_BUFSIZE;
+int  delayComp      = G_DEFAULT_DELAYCOMP;
+bool limitOutput    = false;
+int  rsmpQuality    = 0;
+
+int    midiSystem  = 0;
+int    midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
+int    midiPortIn  = G_DEFAULT_MIDI_PORT_IN;
+bool   noNoteOff   = false;
+string midiMapPath = "";
+string lastFileMap = "";
+int    midiSync    = MIDI_SYNC_NONE;
+float  midiTCfps   = 25.0f;
+
+uint32_t midiInRewind     = 0x0;
+uint32_t midiInStartStop  = 0x0;
+uint32_t midiInActionRec  = 0x0;
+uint32_t midiInInputRec   = 0x0;
+uint32_t midiInVolumeIn   = 0x0;
+uint32_t midiInVolumeOut  = 0x0;
+uint32_t midiInBeatDouble = 0x0;
+uint32_t midiInBeatHalf   = 0x0;
+uint32_t midiInMetronome  = 0x0;
+
+bool recsStopOnChanHalt    = false;
+bool chansStopOnSeqHalt    = false;
+bool treatRecsAsLoops      = false;
+bool resizeRecordings      = true;
+bool inputMonitorDefaultOn = false;
+
+string pluginPath = "";
+string patchPath  = "";
+string samplePath = "";
+
+int mainWindowX = 0;
+int mainWindowY = 0;
+int mainWindowW = GUI_WIDTH;
+int mainWindowH = GUI_HEIGHT;
+
+int browserX         = 0;
+int browserY         = 0;
+int browserW         = 640;
+int browserH         = 480;
+int browserPosition  = 0;
+int browserLastValue = 0;
+string browserLastPath = "";
+
+int actionEditorX       = 0;
+int actionEditorY       = 0;
+int actionEditorW       = 640;
+int actionEditorH       = 480;
+int actionEditorZoom    = 100;
+int actionEditorGridVal = 1;
+int actionEditorGridOn  = false;
+
+int sampleEditorX = 0;
+int sampleEditorY = 0;
+int sampleEditorW = 640;
+int sampleEditorH = 480;
+int sampleEditorGridVal = 1;
+int sampleEditorGridOn  = false;
+
+int midiInputX = 0;
+int midiInputY = 0;
+int midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
+int midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
+
+int pianoRollY = -1;
+int pianoRollH = 422;
+
+int pluginListX = 0;
+int pluginListY = 0;
+
+int configX = 0;
+int configY = 0;
+
+int bpmX = 0;
+int bpmY = 0;
+
+int beatsX = 0;
+int beatsY = 0;
+
+int aboutX = 0;
+int aboutY = 0;
 
+#ifdef WITH_VST
+
+int pluginChooserX   = 0;
+int pluginChooserY   = 0;
+int pluginChooserW   = 640;
+int pluginChooserH   = 480;
+int pluginSortMethod = 0;
+
+#endif
 
-void Conf::init()
+
+/* -------------------------------------------------------------------------- */
+
+
+void init()
 {
-       header  = "GIADACFG";
-       logMode = LOG_MODE_MUTE;
-
-       soundSystem    = DEFAULT_SOUNDSYS;
-       soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
-       soundDeviceIn  = DEFAULT_SOUNDDEV_IN;
-       samplerate     = DEFAULT_SAMPLERATE;
-       buffersize     = DEFAULT_BUFSIZE;
-       delayComp      = DEFAULT_DELAYCOMP;
-       limitOutput    = false;
-       rsmpQuality    = 0;
-
-       midiPortIn  = DEFAULT_MIDI_PORT_IN;
-       noNoteOff   = false;
-       midiMapPath = "";
-       midiPortOut = DEFAULT_MIDI_PORT_OUT;
-       midiSync    = MIDI_SYNC_NONE;
-       midiTCfps   = 25.0f;
-
-       midiInRewind     = 0x0;
-       midiInStartStop  = 0x0;
-       midiInActionRec  = 0x0;
-       midiInInputRec   = 0x0;
-       midiInVolumeIn   = 0x0;
-       midiInVolumeOut  = 0x0;
-       midiInBeatDouble = 0x0;
-       midiInBeatHalf   = 0x0;
-       midiInMetronome  = 0x0;
-
-       pluginPath  = "";
-       patchPath   = "";
-       samplePath  = "";
-
-       recsStopOnChanHalt = false;
-       chansStopOnSeqHalt = false;
-       treatRecsAsLoops   = false;
-
-       resizeRecordings = true;
-
-       mainWindowX = 0;
-       mainWindowY = 0;
-       mainWindowW = GUI_WIDTH;
-       mainWindowH = GUI_HEIGHT;
-
-       browserX         = 0;
-       browserY         = 0;
-       browserW         = 640;
-       browserH         = 480;
-       browserPosition  = 0;
-       browserLastValue = 0;
-
-       actionEditorX       = 0;
-       actionEditorY       = 0;
-       actionEditorW       = 640;
-       actionEditorH       = 480;
-       actionEditorZoom    = 100;
-       actionEditorGridOn  = false;
-       actionEditorGridVal = 1;
-
-       sampleEditorX = 0;
-       sampleEditorY = 0;
-       sampleEditorW = 640;
-       sampleEditorH = 480;
-
-  midiInputX = 0;
-  midiInputY = 0;
-  midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
-  midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
-
-       pianoRollY = -1;
-       pianoRollH = 422;
-
-       #ifdef WITH_VST
-
-       pluginChooserX   = 0;
-       pluginChooserY   = 0;
-       pluginChooserW   = 640;
-       pluginChooserH   = 480;
-       pluginSortMethod = 0;
-
-       #endif
+       /* Initialize confFilePath, i.e. the configuration file. In windows it is in
+        * the same dir of the .exe, while in Linux and OS X in ~/.giada */
+
+#if defined(__linux__) || defined(__APPLE__)
+
+       confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
+       confDirPath  = gu_getHomePath() + G_SLASH;
+
+#elif defined(_WIN32)
+
+       confFilePath = CONF_FILENAME;
+       confDirPath  = "";
+
+#endif
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int Conf::read()
+int read()
 {
        init();
 
-       jRoot = json_load_file(confFilePath.c_str(), 0, &jError);
+  json_error_t jError;
+       json_t *jRoot = json_load_file(confFilePath.c_str(), 0, &jError);
   if (!jRoot) {
-    gu_log("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text);
+    gu_log("[conf::read] unable to read configuration file! Error on line %d: %s\n",
+      jError.line, jError.text);
     return 0;
   }
 
-  if (!checkObject(jRoot, "root element")) {
+  if (!storager::checkObject(jRoot, "root element")) {
                json_decref(jRoot);
     return 0;
        }
 
-       if (!setString(jRoot, CONF_KEY_HEADER, header)) return 0;
-       if (!setInt(jRoot, CONF_KEY_LOG_MODE, logMode)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SOUND_SYSTEM, soundSystem)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SOUND_DEVICE_OUT, soundDeviceOut)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SOUND_DEVICE_IN, soundDeviceIn)) return 0;
-       if (!setInt(jRoot, CONF_KEY_CHANNELS_OUT, channelsOut)) return 0;
-       if (!setInt(jRoot, CONF_KEY_CHANNELS_IN, channelsIn)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLERATE, samplerate)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BUFFER_SIZE, buffersize)) return 0;
-       if (!setInt(jRoot, CONF_KEY_DELAY_COMPENSATION, delayComp)) return 0;
-       if (!setBool(jRoot, CONF_KEY_LIMIT_OUTPUT, limitOutput)) return 0;
-       if (!setInt(jRoot, CONF_KEY_RESAMPLE_QUALITY, rsmpQuality)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MIDI_SYSTEM, midiSystem)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MIDI_PORT_OUT, midiPortOut)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MIDI_PORT_IN, midiPortIn)) return 0;
-       if (!setBool(jRoot, CONF_KEY_NO_NOTE_OFF, noNoteOff)) return 0;
-       if (!setString(jRoot, CONF_KEY_MIDIMAP_PATH, midiMapPath)) return 0;
-       if (!setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
-       if (!setFloat(jRoot, CONF_KEY_MIDI_TC_FPS, midiTCfps)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_START_STOP, midiInStartStop)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, midiInActionRec)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_INPUT_REC, midiInInputRec)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_METRONOME, midiInMetronome)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_IN, midiInVolumeIn)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_OUT, midiInVolumeOut)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_DOUBLE, midiInBeatDouble)) return 0;
-       if (!setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_HALF, midiInBeatHalf)) return 0;
-       if (!setBool(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, recsStopOnChanHalt)) return 0;
-       if (!setBool(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, chansStopOnSeqHalt)) return 0;
-       if (!setBool(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, treatRecsAsLoops)) return 0;
-       if (!setBool(jRoot, CONF_KEY_RESIZE_RECORDINGS, resizeRecordings)) return 0;
-       if (!setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0;
-       if (!setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0;
-       if (!setString(jRoot, CONF_KEY_SAMPLES_PATH, samplePath)) return 0;
-
-       if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_X, mainWindowX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_Y, mainWindowY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_W, mainWindowW)) return 0;
-       if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_H, mainWindowH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_X, browserX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_Y, browserY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_W, browserW)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_H, browserH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_POSITION, browserPosition)) return 0;
-       if (!setString(jRoot, CONF_KEY_BROWSER_LAST_PATH, browserLastPath)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BROWSER_LAST_VALUE, browserLastValue)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_X, actionEditorX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_Y, actionEditorY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_W, actionEditorW)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_H, actionEditorH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_ZOOM, actionEditorZoom)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_VAL, actionEditorGridVal)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_ON, actionEditorGridOn)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_X, sampleEditorX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_Y, sampleEditorY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_W, sampleEditorW)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_H, sampleEditorH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_VAL, sampleEditorGridVal)) return 0;
-       if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, sampleEditorGridOn)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PIANO_ROLL_Y, pianoRollY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PIANO_ROLL_H, pianoRollH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_LIST_X, pluginListX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_LIST_Y, pluginListY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_CONFIG_X, configX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_CONFIG_Y, configY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BPM_X, bpmX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BPM_Y, bpmY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BEATS_X, beatsX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_BEATS_Y, beatsY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ABOUT_X, aboutX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_ABOUT_Y, aboutY)) return 0;
-  if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_X, midiInputX)) return 0;
-  if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_Y, midiInputY)) return 0;
-  if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_W, midiInputW)) return 0;
-  if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_H, midiInputH)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_HEADER, header)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_LOG_MODE, logMode)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SOUND_SYSTEM, soundSystem)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SOUND_DEVICE_OUT, soundDeviceOut)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SOUND_DEVICE_IN, soundDeviceIn)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_CHANNELS_OUT, channelsOut)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_CHANNELS_IN, channelsIn)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLERATE, samplerate)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BUFFER_SIZE, buffersize)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_DELAY_COMPENSATION, delayComp)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_LIMIT_OUTPUT, limitOutput)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_RESAMPLE_QUALITY, rsmpQuality)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYSTEM, midiSystem)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_OUT, midiPortOut)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_IN, midiPortIn)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_NO_NOTE_OFF, noNoteOff)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_MIDIMAP_PATH, midiMapPath)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
+       if (!storager::setFloat(jRoot, CONF_KEY_MIDI_TC_FPS, midiTCfps)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_START_STOP, midiInStartStop)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, midiInActionRec)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_INPUT_REC, midiInInputRec)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_METRONOME, midiInMetronome)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_IN, midiInVolumeIn)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_OUT, midiInVolumeOut)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_DOUBLE, midiInBeatDouble)) return 0;
+       if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_HALF, midiInBeatHalf)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, recsStopOnChanHalt)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, chansStopOnSeqHalt)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, treatRecsAsLoops)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_RESIZE_RECORDINGS, resizeRecordings)) return 0;
+       if (!storager::setBool(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON, inputMonitorDefaultOn)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_SAMPLES_PATH, samplePath)) return 0;
+
+       if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_X, mainWindowX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_Y, mainWindowY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_W, mainWindowW)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_H, mainWindowH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_X, browserX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_Y, browserY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_W, browserW)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_H, browserH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_POSITION, browserPosition)) return 0;
+       if (!storager::setString(jRoot, CONF_KEY_BROWSER_LAST_PATH, browserLastPath)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BROWSER_LAST_VALUE, browserLastValue)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_X, actionEditorX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_Y, actionEditorY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_W, actionEditorW)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_H, actionEditorH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_ZOOM, actionEditorZoom)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_VAL, actionEditorGridVal)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_ON, actionEditorGridOn)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_X, sampleEditorX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_Y, sampleEditorY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_W, sampleEditorW)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_H, sampleEditorH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_VAL, sampleEditorGridVal)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, sampleEditorGridOn)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_Y, pianoRollY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_H, pianoRollH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_X, pluginListX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_Y, pluginListY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_CONFIG_X, configX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_CONFIG_Y, configY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BPM_X, bpmX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BPM_Y, bpmY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BEATS_X, beatsX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_BEATS_Y, beatsY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ABOUT_X, aboutX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_ABOUT_Y, aboutY)) return 0;
+  if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_X, midiInputX)) return 0;
+  if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_Y, midiInputY)) return 0;
+  if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_W, midiInputW)) return 0;
+  if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_H, midiInputH)) return 0;
 
 #ifdef WITH_VST
 
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_X, pluginChooserX)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_Y, pluginChooserY)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_W, pluginChooserW)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_H, pluginChooserH)) return 0;
-       if (!setInt(jRoot, CONF_KEY_PLUGIN_SORT_METHOD, pluginSortMethod)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_X, pluginChooserX)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_Y, pluginChooserY)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_W, pluginChooserW)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_H, pluginChooserH)) return 0;
+       if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_SORT_METHOD, pluginSortMethod)) return 0;
 
 #endif
 
@@ -291,12 +391,12 @@ int Conf::read()
 /* -------------------------------------------------------------------------- */
 
 
-int Conf::write()
+int write()
 {
        if (!createConfigFolder())
                return 0;
 
-       jRoot = json_object();
+       json_t *jRoot = json_object();
 
        json_object_set_new(jRoot, CONF_KEY_HEADER,                    json_string(header.c_str()));
        json_object_set_new(jRoot, CONF_KEY_LOG_MODE,                  json_integer(logMode));
@@ -331,6 +431,7 @@ int Conf::write()
        json_object_set_new(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT,    json_boolean(chansStopOnSeqHalt));
        json_object_set_new(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS,       json_boolean(treatRecsAsLoops));
        json_object_set_new(jRoot, CONF_KEY_RESIZE_RECORDINGS,         json_boolean(resizeRecordings));
+       json_object_set_new(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON,  json_boolean(inputMonitorDefaultOn));
        json_object_set_new(jRoot, CONF_KEY_PLUGINS_PATH,              json_string(pluginPath.c_str()));
        json_object_set_new(jRoot, CONF_KEY_PATCHES_PATH,              json_string(patchPath.c_str()));
        json_object_set_new(jRoot, CONF_KEY_SAMPLES_PATH,              json_string(samplePath.c_str()));
@@ -386,64 +487,9 @@ int Conf::write()
 #endif
 
   if (json_dump_file(jRoot, confFilePath.c_str(), JSON_INDENT(2)) != 0) {
-    gu_log("[Conf::write] unable to write configuration file!\n");
+    gu_log("[conf::write] unable to write configuration file!\n");
     return 0;
   }
   return 1;
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Conf::sanitize()
-{
-       if (!(soundSystem & SYS_API_ANY)) soundSystem = DEFAULT_SOUNDSYS;
-       if (soundDeviceOut < 0) soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
-       if (soundDeviceIn < -1) soundDeviceIn = DEFAULT_SOUNDDEV_IN;
-       if (channelsOut < 0) channelsOut = 0;
-       if (channelsIn < 0)  channelsIn  = 0;
-       if (buffersize < 8 || buffersize > 4096) buffersize = DEFAULT_BUFSIZE;
-       if (delayComp < 0) delayComp = DEFAULT_DELAYCOMP;
-       if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_SYSTEM;
-       if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_PORT_OUT;
-       if (midiPortIn < -1) midiPortIn = DEFAULT_MIDI_PORT_IN;
-       if (browserX < 0) browserX = 0;
-       if (browserY < 0) browserY = 0;
-       if (browserW < 396) browserW = 396;
-       if (browserH < 302) browserH = 302;
-       if (actionEditorX < 0) actionEditorX = 0;
-       if (actionEditorY < 0) actionEditorY = 0;
-       if (actionEditorW < 640) actionEditorW = 640;
-       if (actionEditorH < 176) actionEditorH = 176;
-       if (actionEditorZoom < 100) actionEditorZoom = 100;
-       if (actionEditorGridVal < 0) actionEditorGridVal = 0;
-       if (actionEditorGridOn < 0) actionEditorGridOn = 0;
-       if (pianoRollH <= 0) pianoRollH = 422;
-  if (sampleEditorX < 0) sampleEditorX = 0;
-       if (sampleEditorY < 0) sampleEditorY = 0;
-       if (sampleEditorW < 500) sampleEditorW = 500;
-       if (sampleEditorH < 292) sampleEditorH = 292;
-       if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
-       if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
-  if (midiInputX < 0) midiInputX = 0;
-  if (midiInputY < 0) midiInputY = 0;
-  if (midiInputW < G_DEFAULT_MIDI_INPUT_UI_W) midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
-  if (midiInputH < G_DEFAULT_MIDI_INPUT_UI_H) midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
-       if (configX < 0) configX = 0;
-       if (configY < 0) configY = 0;
-       if (pluginListX < 0) pluginListX = 0;
-       if (pluginListY < 0) pluginListY = 0;
-#ifdef WITH_VST
-       if (pluginChooserW < 640) pluginChooserW = 640;
-       if (pluginChooserH < 480) pluginChooserW = 480;
-#endif
-  if (bpmX < 0) bpmX = 0;
-       if (bpmY < 0) bpmY = 0;
-       if (beatsX < 0) beatsX = 0;
-       if (beatsY < 0) beatsY = 0;
-       if (aboutX < 0) aboutX = 0;
-       if (aboutY < 0) aboutY = 0;
-       if (samplerate < 8000) samplerate = DEFAULT_SAMPLERATE;
-       if (rsmpQuality < 0 || rsmpQuality > 4) rsmpQuality = 0;
-}
+}}}; // giada::m::conf::
index 81901bc642d617d248c41f9aa2016440914cb4c5..138579d11330b5dfd1368e24205b75261f1298c5 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * conf
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __CONF_H__
-#define __CONF_H__
+#ifndef G_CONF_H
+#define G_CONF_H
 
 
 #include <string>
-#include <cstdio>
-#include "dataStorageJson.h"
-
 
-using std::string;
 
-
-class Conf : public DataStorageJson
+namespace giada {
+namespace m {
+namespace conf
 {
-private:
-
-       string confFilePath;
-       string confDirPath;
-
-       /* init
-   * Init Conf with default values. */
-
-  void init();
-
-       /* sanitize
-        * Avoid funky values from config file. */
-
-       void sanitize();
-
-       /* createConfigFolder
-        * Create local folder where to put the configuration file. Path differs from
-        * OS to OS. */
-
-       int createConfigFolder();
-
-public:
-
-       Conf();
-
-       string header;
-
-       int  logMode;
-       int  soundSystem;
-       int  soundDeviceOut;
-       int  soundDeviceIn;
-       int  channelsOut;
-       int  channelsIn;
-       int  samplerate;
-       int  buffersize;
-       int  delayComp;
-       bool limitOutput;
-       int  rsmpQuality;
-
-       int    midiSystem;
-       int    midiPortOut;
-       int    midiPortIn;
-       bool   noNoteOff;
-       string midiMapPath;
-       string lastFileMap;
-       int    midiSync;  // see const.h
-       float  midiTCfps;
-
-       uint32_t midiInRewind;
-       uint32_t midiInStartStop;
-       uint32_t midiInActionRec;
-       uint32_t midiInInputRec;
-       uint32_t midiInMetronome;
-       uint32_t midiInVolumeIn;
-       uint32_t midiInVolumeOut;
-       uint32_t midiInBeatDouble;
-       uint32_t midiInBeatHalf;
-
-       bool recsStopOnChanHalt;
-       bool chansStopOnSeqHalt;
-       bool treatRecsAsLoops;
-       bool resizeRecordings;
-
-       string pluginPath;
-       string patchPath;
-       string samplePath;
-
-       int mainWindowX, mainWindowY, mainWindowW, mainWindowH;
-
-       int browserX, browserY, browserW, browserH, browserPosition, browserLastValue;
-       string browserLastPath;
-
-       int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom;
-       int actionEditorGridVal;
-       int actionEditorGridOn;
-
-       int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH;
-  int sampleEditorGridVal;
-  int sampleEditorGridOn;
-
-  int midiInputX, midiInputY, midiInputW, midiInputH;
-
-       int pianoRollY, pianoRollH;
-       int pluginListX, pluginListY;
-       int configX, configY;
-       int bpmX, bpmY;
-       int beatsX, beatsY;
-       int aboutX, aboutY;
+void init();
+int read();
+int write();
+
+extern std::string header;
+
+extern int  logMode;
+extern int  soundSystem;
+extern int  soundDeviceOut;
+extern int  soundDeviceIn;
+extern int  channelsOut;
+extern int  channelsIn;
+extern int  samplerate;
+extern int  buffersize;
+extern int  delayComp;
+extern bool limitOutput;
+extern int  rsmpQuality;
+
+extern int  midiSystem;
+extern int  midiPortOut;
+extern int  midiPortIn;
+extern bool noNoteOff;
+extern std::string midiMapPath;
+extern std::string lastFileMap;
+extern int   midiSync;  // see const.h
+extern float midiTCfps;
+
+extern uint32_t midiInRewind;
+extern uint32_t midiInStartStop;
+extern uint32_t midiInActionRec;
+extern uint32_t midiInInputRec;
+extern uint32_t midiInMetronome;
+extern uint32_t midiInVolumeIn;
+extern uint32_t midiInVolumeOut;
+extern uint32_t midiInBeatDouble;
+extern uint32_t midiInBeatHalf;
+
+extern bool recsStopOnChanHalt;
+extern bool chansStopOnSeqHalt;
+extern bool treatRecsAsLoops;
+extern bool resizeRecordings;
+extern bool inputMonitorDefaultOn;
+
+extern std::string pluginPath;
+extern std::string patchPath;
+extern std::string samplePath;
+
+extern int mainWindowX, mainWindowY, mainWindowW, mainWindowH;
+
+extern int browserX, browserY, browserW, browserH, browserPosition, browserLastValue;
+extern std::string browserLastPath;
+
+extern int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom;
+extern int actionEditorGridVal;
+extern int actionEditorGridOn;
+
+extern int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH;
+extern int sampleEditorGridVal;
+extern int sampleEditorGridOn;
+
+extern int midiInputX, midiInputY, midiInputW, midiInputH;
+
+extern int pianoRollY, pianoRollH;
+extern int pluginListX, pluginListY;
+extern int configX, configY;
+extern int bpmX, bpmY;
+extern int beatsX, beatsY;
+extern int aboutX, aboutY;
 
 #ifdef WITH_VST
 
-       int pluginChooserX, pluginChooserY, pluginChooserW, pluginChooserH;
-       int pluginSortMethod;
+extern int pluginChooserX, pluginChooserY, pluginChooserW, pluginChooserH;
+extern int pluginSortMethod;
 
 #endif
-
-       int  read();
-       int  write();
-};
+}}}; // giada::m::conf::
 
 #endif
index 99fa701ae396add4c9f5e938103c7cee1a2561c6..8365854fdb297e6753f09d91e4cee292bdea7ed8 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * const.h
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef CONST_H
-#define CONST_H
+#ifndef G_CONST_H
+#define G_CONST_H
 
 
 /* -- version --------------------------------------------------------------- */
-#define G_VERSION_STR   "0.13.2"
 #define G_APP_NAME      "Giada"
+#define G_VERSION_STR   "0.14.0"
 #define G_VERSION_MAJOR 0
-#define G_VERSION_MINOR 13
-#define G_VERSION_PATCH 2
+#define G_VERSION_MINOR 14
+#define G_VERSION_PATCH 0
 
-#define CONF_FILENAME          "giada.conf"
+#define CONF_FILENAME "giada.conf"
 
 #ifndef BUILD_DATE
        #define BUILD_DATE __DATE__
 
 
 /* -- MIN/MAX values -------------------------------------------------------- */
-#define MAX_BEATS                                 32
-#define MAX_BARS                                  32
-#define MAX_PATCHNAME_LEN       32
-#define DB_MIN_SCALE              60.0f
-#define MIN_COLUMN_WIDTH   140
+#define G_MIN_BPM           20.0f
+#define G_MAX_BPM           999.0f
+#define G_MAX_BEATS                              32
+#define G_MAX_BARS                               32
+#define G_MAX_QUANTIZE      8
+#define G_MAX_PATCHNAME_LEN    32
+#define G_DB_MIN_SCALE      60.0f
+#define G_MIN_COLUMN_WIDTH  140
+#define G_MAX_BOOST_DB      20.0f
+#define G_MAX_PITCH         4.0f
 
 
 
 /* -- kernel audio ---------------------------------------------------------- */
-#define SYS_API_NONE           0x00  // 0000 0000
-#define SYS_API_JACK           0x01  // 0000 0001
-#define SYS_API_ALSA           0x02  // 0000 0010
-#define SYS_API_DS                     0x04  // 0000 0100
-#define SYS_API_ASIO           0x08  // 0000 1000
-#define SYS_API_CORE           0x10  // 0001 0000
-#define SYS_API_PULSE   0x20  // 0010 0000
-#define SYS_API_WASAPI  0x40  // 0100 0000
-#define SYS_API_ANY     0x7F  // 0111 1111
-
-#define KERNEL_OK                                       0
-#define KERNEL_UNDERRUN          -1
-#define KERNEL_CRITICAL          -2
+#define G_SYS_API_NONE         0x00  // 0000 0000
+#define G_SYS_API_JACK         0x01  // 0000 0001
+#define G_SYS_API_ALSA         0x02  // 0000 0010
+#define G_SYS_API_DS                   0x04  // 0000 0100
+#define G_SYS_API_ASIO         0x08  // 0000 1000
+#define G_SYS_API_CORE         0x10  // 0001 0000
+#define G_SYS_API_PULSE   0x20  // 0010 0000
+#define G_SYS_API_WASAPI  0x40  // 0100 0000
+#define G_SYS_API_ANY     0x7F  // 0111 1111
 
 
 
 /* -- kernel midi ----------------------------------------------------------- */
-#define MIDI_API_JACK          0x01  // 0000 0001
-#define MIDI_API_ALSA          0x02  // 0000 0010
+#define G_MIDI_API_JACK                0x01  // 0000 0001
+#define G_MIDI_API_ALSA                0x02  // 0000 0010
 
 
 
 /* -- default system -------------------------------------------------------- */
 #if defined(__linux__)
-       #define DEFAULT_SOUNDSYS        SYS_API_NONE
+       #define G_DEFAULT_SOUNDSYS      G_SYS_API_NONE
 #elif defined(_WIN32)
-       #define DEFAULT_SOUNDSYS        SYS_API_DS
+       #define G_DEFAULT_SOUNDSYS      G_SYS_API_DS
 #elif defined(__APPLE__)
-       #define DEFAULT_SOUNDSYS        SYS_API_CORE
+       #define G_DEFAULT_SOUNDSYS      G_SYS_API_CORE
 #endif
 
-#define DEFAULT_SOUNDDEV_OUT  0       /// FIXME - please override with rtAudio::getDefaultDevice (or similar)
-#define DEFAULT_SOUNDDEV_IN   -1                       // no recording by default: input disabled
-#define DEFAULT_MIDI_SYSTEM   0
-#define DEFAULT_MIDI_PORT_IN  -1
-#define DEFAULT_MIDI_PORT_OUT -1
-#define DEFAULT_SAMPLERATE   44100
-#define DEFAULT_BUFSIZE                   1024
-#define DEFAULT_DELAYCOMP               0
-#define DEFAULT_VOL                               1.0f
-#define G_DEFAULT_PITCH                         1.0f
-#define DEFAULT_BOOST                     0.0f
-#define DEFAULT_OUT_VOL           1.0f
-#define DEFAULT_IN_VOL            1.0f
-#define DEFAULT_CHANMODE          SINGLE_BASIC
-#define DEFAULT_BPM                               120.0f
-#define DEFAULT_BEATS                     4
-#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 G_DEFAULT_PATCH_NAME "(default patch)"
+#define G_DEFAULT_SOUNDDEV_OUT    0      // FIXME - please override with rtAudio::getDefaultDevice (or similar)
+#define G_DEFAULT_SOUNDDEV_IN    -1                    // no recording by default: input disabled
+#define G_DEFAULT_MIDI_SYSTEM     0
+#define G_DEFAULT_MIDI_PORT_IN   -1
+#define G_DEFAULT_MIDI_PORT_OUT  -1
+#define G_DEFAULT_SAMPLERATE      44100
+#define G_DEFAULT_BUFSIZE                    1024
+#define G_DEFAULT_DELAYCOMP                0
+#define G_DEFAULT_VOL                                1.0f
+#define G_DEFAULT_PITCH                              1.0f
+#define G_DEFAULT_BOOST                              1.0f
+#define G_DEFAULT_OUT_VOL            1.0f
+#define G_DEFAULT_IN_VOL             1.0f
+#define G_DEFAULT_CHANMODE           SINGLE_BASIC
+#define G_DEFAULT_BPM                                120.0f
+#define G_DEFAULT_BEATS                              4
+#define G_DEFAULT_BARS                       1
+#define G_DEFAULT_QUANTIZE        0              // quantizer off
+#define G_DEFAULT_FADEOUT_STEP    0.01f  // micro-fadeout speed
+#define G_DEFAULT_COLUMN_WIDTH    380
+#define G_DEFAULT_PATCH_NAME       "(default patch)"
 #define G_DEFAULT_MIDI_INPUT_UI_W 300
 #define G_DEFAULT_MIDI_INPUT_UI_H 350
 
 
 
 /* -- actions --------------------------------------------------------------- */
-#define ACTION_KEYPRESS                0x01 // 0000 0001
-#define ACTION_KEYREL                  0x02 // 0000 0010
-#define ACTION_KILLCHAN                0x04 // 0000 0100
-#define ACTION_MUTEON                  0x08 // 0000 1000
-#define ACTION_MUTEOFF         0x10 // 0001 0000
-#define ACTION_VOLUME     0x20 // 0010 0000
-#define ACTION_MIDI       0x40 // 0100 0000
+#define G_ACTION_KEYPRESS              0x01 // 0000 0001
+#define G_ACTION_KEYREL                        0x02 // 0000 0010
+#define G_ACTION_KILL              0x04 // 0000 0100
+#define G_ACTION_MUTEON                        0x08 // 0000 1000
+#define G_ACTION_MUTEOFF               0x10 // 0001 0000
+#define G_ACTION_VOLUME     0x20 // 0010 0000
+#define G_ACTION_MIDI       0x40 // 0100 0000
 
-#define ACTION_KEYS       0x03 // 0000 0011 any key
-#define ACTION_MUTES      0x24 // 0001 1000 any mute
+#define G_ACTION_KEYS       0x03 // 0000 0011 any key
+#define G_ACTION_MUTES      0x24 // 0001 1000 any mute
 
-#define RANGE_CHAR        0x01 // range for MIDI (0-127)
-#define RANGE_FLOAT       0x02 // range for volumes and VST params (0.0-1.0)
+#define G_RANGE_CHAR        0x01 // range for MIDI (0-127)
+#define G_RANGE_FLOAT       0x02 // range for volumes and VST params (0.0-1.0)
 
 
 
 
 
 /* -- MIDI signals -------------------------------------------------------------
- * all signals are set to channel 0 (where channels are considered).
- * It's up to the caller to bitmask them with the proper channel number. */
-
-/* channel voices messages - controller (0xB0) is a special subset of
- * this family: it drives knobs, volume, faders and such. */
+All signals are set to channel 0 (where channels are considered). It's up to the
+caller to bitmask them with the proper channel number.
+Channel voices messages - controller (0xB0) is a special subset of this family:
+it drives knobs, volume, faders and such. */
 
 #define MIDI_CONTROLLER     0xB0 << 24
 #define MIDI_NOTE_ON        0x90 << 24
@@ -307,10 +305,10 @@ const int MIDI_CHANS[16] = {
 /* midi sync constants */
 
 #define MIDI_SYNC_NONE      0x00
-#define MIDI_SYNC_CLOCK_M   0x01
-#define MIDI_SYNC_CLOCK_S   0x02
-#define MIDI_SYNC_MTC_M     0x04
-#define MIDI_SYNC_MTC_S     0x08
+#define MIDI_SYNC_CLOCK_M   0x01  // master
+#define MIDI_SYNC_CLOCK_S   0x02  // slave
+#define MIDI_SYNC_MTC_M     0x04  // master
+#define MIDI_SYNC_MTC_S     0x08  // slave
 
 /* JSON patch keys */
 
@@ -340,8 +338,7 @@ const int MIDI_CHANS[16] = {
 #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_PAN                  "pan"
 #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"
@@ -362,6 +359,7 @@ const int MIDI_CHANS[16] = {
 #define PATCH_KEY_CHANNEL_BOOST                "boost"
 #define PATCH_KEY_CHANNEL_REC_ACTIVE           "rec_active"
 #define PATCH_KEY_CHANNEL_PITCH                "pitch"
+#define PATCH_KEY_CHANNEL_INPUT_MONITOR        "input_monitor"
 #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"
@@ -415,6 +413,7 @@ const int MIDI_CHANS[16] = {
 #define CONF_KEY_CHANS_STOP_ON_SEQ_HALT   "chans_stop_on_seq_halt"
 #define CONF_KEY_TREAT_RECS_AS_LOOPS      "treat_recs_as_loops"
 #define CONF_KEY_RESIZE_RECORDINGS        "resize_recordings"
+#define CONF_KEY_INPUT_MONITOR_DEFAULT_ON "input_monitor_default_on"
 #define CONF_KEY_PLUGINS_PATH             "plugins_path"
 #define CONF_KEY_PATCHES_PATH             "patches_path"
 #define CONF_KEY_SAMPLES_PATH             "samples_path"
diff --git a/src/core/dataStorageIni.cpp b/src/core/dataStorageIni.cpp
deleted file mode 100644 (file)
index a759782..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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) {
-                       gu_log("[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
deleted file mode 100644 (file)
index 9dbf947..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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
deleted file mode 100644 (file)
index a52f142..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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)) {
-    gu_log("[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) {
-    gu_log("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key);
-    output = 0.0f;
-    return true;
-  }
-  if (!json_is_real(jObject)) {
-    gu_log("[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) {
-    gu_log("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key);
-    output = 0;
-    return true;
-  }
-  if (!json_is_integer(jObject)) {
-    gu_log("[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) {
-    gu_log("[dataStorageJson::setBool] key '%s' not found, using default value\n", key);
-    output = false;
-    return true;
-  }
-  if (!json_is_boolean(jObject)) {
-    gu_log("[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)) {
-    gu_log("[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)) {
-    gu_log("[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
deleted file mode 100644 (file)
index eb43ef3..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 2826793fe903522edf2fb7b552a2fcbd78259e05..f9be05a94053c3d659ba1c09732c2237c6cc1c9d 100644 (file)
@@ -6,7 +6,7 @@
  *
  * ---------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index 7d93be8aa72a3f1d838e32c35e940dccf5d83b83..b1377864029ece6c0ad810ee5c9188cff4068f69 100644 (file)
@@ -6,7 +6,7 @@
  *
  * ---------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -26,8 +26,8 @@
  *
  * ------------------------------------------------------------------ */
 
-#ifndef GRAPHICS_H
-#define GRAPHICS_H
+#ifndef G_GRAPHICS_H
+#define G_GRAPHICS_H
 
 extern const char *giada_logo_xpm[];
 
index 41105a622b1b126cd49272fa60879d827d651a67..681fb82cfa86c53e4d95c83f925fd3eee54fbe70 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * init
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include <ctime>
+#ifdef __APPLE__
+       #include <pwd.h>
+#endif
 #include "../utils/log.h"
 #include "../utils/fs.h"
 #include "../utils/gui.h"
 #include "../gui/dialogs/gd_mainWindow.h"
 #include "../gui/dialogs/gd_warnings.h"
+#include "../glue/main.h"
 #include "init.h"
 #include "mixer.h"
 #include "wave.h"
 #include "const.h"
+#include "clock.h"
 #include "channel.h"
 #include "mixerHandler.h"
-#include "patch_DEPR_.h"
 #include "patch.h"
 #include "conf.h"
 #include "pluginHost.h"
 #include "recorder.h"
 #include "midiMapConf.h"
 #include "kernelMidi.h"
+#include "kernelAudio.h"
 
 
-extern KernelAudio   G_KernelAudio;
-extern Mixer                      G_Mixer;
-extern Recorder           G_Recorder;
-extern KernelMidi    G_KernelMidi;
-extern bool                               G_audio_status;
 extern bool                               G_quit;
-extern Patch_DEPR_   G_Patch_DEPR_;
-extern Patch         G_Patch;
-extern Conf          G_Conf;
-extern MidiMapConf   G_MidiMap;
 extern gdMainWindow *G_MainWin;
 
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+
+using namespace giada::m;
 
 
 void init_prepareParser()
@@ -71,11 +64,11 @@ void init_prepareParser()
   time (&t);
        gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t));
 
-       G_Conf.read();
-       G_Patch_DEPR_.setDefault();
-       G_Patch.init();
+  conf::init();
+       conf::read();
+       patch::init();
 
-       if (!gu_logInit(G_Conf.logMode))
+       if (!gu_logInit(conf::logMode))
                gu_log("[init] log init failed! Using default stdout\n");
 
        gu_log("[init] configuration file ready\n");
@@ -87,25 +80,25 @@ void init_prepareParser()
 
 void init_prepareKernelAudio()
 {
-       G_KernelAudio.openDevice(G_Conf.soundSystem, G_Conf.soundDeviceOut,
-               G_Conf.soundDeviceIn,   G_Conf.channelsOut, G_Conf.channelsIn,
-               G_Conf.samplerate, G_Conf.buffersize);
-       G_Mixer.init();
-       G_Recorder.init();
+  kernelAudio::openDevice();
+  clock::init(conf::samplerate, conf::midiTCfps);
+       mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+       recorder::init();
 
 #ifdef WITH_VST
 
        /* If with Jack don't use buffer size stored in Conf. Use real buffersize
-       from the soundcard (G_KernelAudio.realBufsize). */
+       from the soundcard (kernelAudio::realBufsize). */
 
-       if (G_Conf.soundSystem == SYS_API_JACK)
-               G_PluginHost.init(G_KernelAudio.realBufsize, G_Conf.samplerate);
+       if (conf::soundSystem == G_SYS_API_JACK)
+               pluginHost::init(kernelAudio::getRealBufSize(), conf::samplerate);
        else
-               G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate);
+               pluginHost::init(conf::buffersize, conf::samplerate);
 
-       G_PluginHost.sortPlugins(G_Conf.pluginSortMethod);
+       pluginHost::sortPlugins(conf::pluginSortMethod);
 
 #endif
+
 }
 
 
@@ -114,9 +107,9 @@ void init_prepareKernelAudio()
 
 void init_prepareKernelMIDI()
 {
-       G_KernelMidi.setApi(G_Conf.midiSystem);
-       G_KernelMidi.openOutDevice(G_Conf.midiPortOut);
-       G_KernelMidi.openInDevice(G_Conf.midiPortIn);
+       kernelMidi::setApi(conf::midiSystem);
+       kernelMidi::openOutDevice(conf::midiPortOut);
+       kernelMidi::openInDevice(conf::midiPortIn);
 }
 
 
@@ -125,18 +118,11 @@ void init_prepareKernelMIDI()
 
 void init_prepareMidiMap()
 {
-       G_MidiMap.init();
-       G_MidiMap.setDefault_DEPR_();
-       G_MidiMap.setDefault();
-
-       /* read with deprecated method first. If it fails, try with the new one. */
-       // TODO - do the opposite: if json fails, go with deprecated one
-
-       if (G_MidiMap.read(G_Conf.midiMapPath) != MIDIMAP_READ_OK) {
-               gu_log("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n");
-               if (G_MidiMap.readMap_DEPR_(G_Conf.midiMapPath) == MIDIMAP_INVALID)
-                       gu_log("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n");
-               }
+       midimap::init();
+       midimap::setDefault();
+
+       if (midimap::read(conf::midiMapPath) != MIDIMAP_READ_OK)
+               gu_log("[init_prepareMidiMap] MIDI map read failed!\n");
 }
 
 
@@ -146,15 +132,15 @@ void init_prepareMidiMap()
 void init_startGUI(int argc, char **argv)
 {
        G_MainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, "", argc, argv);
-       G_MainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW,
-    G_Conf.mainWindowH);
+       G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW,
+    conf::mainWindowH);
 
-  gu_updateMainWinLabel(G_Patch.name == "" ? G_DEFAULT_PATCH_NAME : G_Patch.name);
+  gu_updateMainWinLabel(patch::name == "" ? G_DEFAULT_PATCH_NAME : patch::name);
 
-       /* never update the GUI elements if G_audio_status is bad, segfaults
+       /* never update the GUI elements if kernelAudio::getStatus() is bad, segfaults
         * are around the corner */
 
-       if (G_audio_status)
+       if (kernelAudio::getStatus())
                gu_updateControls();
   else
                gdAlert("Your soundcard isn't configured correctly.\n"
@@ -166,8 +152,8 @@ void init_startGUI(int argc, char **argv)
 
 void init_startKernelAudio()
 {
-       if (G_audio_status)
-               G_KernelAudio.startStream();
+       if (kernelAudio::getStatus())
+               kernelAudio::startStream();
 }
 
 
@@ -180,12 +166,12 @@ void init_shutdown()
 
        /* store position and size of the main window for the next startup */
 
-       G_Conf.mainWindowX = G_MainWin->x();
-       G_Conf.mainWindowY = G_MainWin->y();
-       G_Conf.mainWindowW = G_MainWin->w();
-       G_Conf.mainWindowH = G_MainWin->h();
+       conf::mainWindowX = G_MainWin->x();
+       conf::mainWindowY = G_MainWin->y();
+       conf::mainWindowW = G_MainWin->w();
+       conf::mainWindowH = G_MainWin->h();
 
-       /* close any open subwindow, especially before cleaning PluginHost_DEPR_ to
+       /* close any open subwindow, especially before cleaning PluginHost to
         * avoid mess */
 
        gu_closeAllSubwindows();
@@ -193,32 +179,35 @@ void init_shutdown()
 
        /* write configuration file */
 
-       if (!G_Conf.write())
+       if (!conf::write())
                gu_log("[init] error while saving configuration file!\n");
        else
                gu_log("[init] configuration saved\n");
 
-       /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer.
+       /* if kernelAudio::getStatus() we close the kernelAudio FIRST, THEN the mixer.
         * The opposite could cause random segfaults (even now with RtAudio?). */
 
-       if (G_audio_status) {
-               G_KernelAudio.closeDevice();
-               G_Mixer.close();
+       if (kernelAudio::getStatus()) {
+               kernelAudio::closeDevice();
+               mixer::close();
                gu_log("[init] Mixer closed\n");
        }
 
-       G_Recorder.clearAll();
-  for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               G_Mixer.channels.at(i)->hasActions  = false;
-               G_Mixer.channels.at(i)->readActions = false;
-               //if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
-               //      ((SampleChannel*)G_Mixer.channels.at(i))->readActions = false;
+       recorder::clearAll();
+  for (unsigned i=0; i<mixer::channels.size(); i++) {
+               mixer::channels.at(i)->hasActions  = false;
+               mixer::channels.at(i)->readActions = false;
+               //if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
+               //      ((SampleChannel*)mixer::channels.at(i))->readActions = false;
        }
        gu_log("[init] Recorder cleaned up\n");
 
 #ifdef WITH_VST
-       G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
+
+       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
+  pluginHost::close();
        gu_log("[init] PluginHost cleaned up\n");
+
 #endif
 
        gu_log("[init] Giada " G_VERSION_STR " closed\n\n");
index 7d631bc096d127039c02b78a058312ec0ec732dc..19833d4e39b20440f3da644cbbf06e3538a2073f 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * init
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef INIT_H
-#define INIT_H
-
-
-#include <cstdio>
-#include <stdint.h>
-#ifdef __APPLE__
-       #include <pwd.h>
-#endif
+#ifndef G_INIT_H
+#define G_INIT_H
 
 
 void init_prepareParser();
index b487df9fc582c8001573c5d87ab4dc639c291f76..d9fe65ce02765b43d4105353c900de8e371a0204 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -27,6 +27,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include "../deps/rtaudio-mod/RtAudio.h"
 #include "../utils/log.h"
 #include "../glue/main.h"
 #include "conf.h"
 #include "kernelAudio.h"
 
 
-extern KernelAudio G_KernelAudio;
-extern Mixer       G_Mixer;
-extern Conf        G_Conf;
-extern bool           G_audio_status;
-
-
 using std::string;
 using std::vector;
 
 
-KernelAudio::KernelAudio()
+namespace giada {
+namespace m {
+namespace kernelAudio
+{
+namespace
+{
+RtAudio *rtSystem;
+bool status;
+unsigned numDevs;
+bool inputEnabled;
+unsigned realBufsize;          // reale bufsize from the soundcard
+int api;
+
+#ifdef __linux__
+
+JackState jackState;
+
+jack_client_t *jackGetHandle()
+{
+       return static_cast<jack_client_t*>(rtSystem->rtapi_->__HACK__getJackClient());
+}
+
+#endif
+};  // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init()
 {
-       system       = nullptr;
-       numDevs      = 0;
-       inputEnabled = 0;
-       realBufsize  = 0;
-       api          = 0;
+  rtSystem     = nullptr;
+  numDevs      = 0;
+  inputEnabled = 0;
+  realBufsize  = 0;
+  api          = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
+bool getStatus()
+{
+  return status;
+}
 
-int KernelAudio::openDevice(int _api, int outDev,      int inDev, int outChan,
-       int inChan, int samplerate, int buffersize)
+
+/* -------------------------------------------------------------------------- */
+
+
+int openDevice()
 {
-       api = _api;
-       gu_log("[KA] using system 0x%x\n", api);
+       api = conf::soundSystem;
+       gu_log("[KA] using rtSystem 0x%x\n", api);
 
 #if defined(__linux__)
 
-       if (api == SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
-               system = new RtAudio(RtAudio::UNIX_JACK);
+       if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+               rtSystem = new RtAudio(RtAudio::UNIX_JACK);
        else
-       if (api == SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
-               system = new RtAudio(RtAudio::LINUX_ALSA);
+       if (api == G_SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
+               rtSystem = new RtAudio(RtAudio::LINUX_ALSA);
        else
-       if (api == SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
-               system = new RtAudio(RtAudio::LINUX_PULSE);
+       if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+               rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
 
 #elif defined(_WIN32)
 
-       if (api == SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
-               system = new RtAudio(RtAudio::WINDOWS_DS);
+       if (api == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
+               rtSystem = new RtAudio(RtAudio::WINDOWS_DS);
        else
-       if (api == SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
-               system = new RtAudio(RtAudio::WINDOWS_ASIO);
+       if (api == G_SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
+               rtSystem = new RtAudio(RtAudio::WINDOWS_ASIO);
        else
-       if (api == SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
-               system = new RtAudio(RtAudio::WINDOWS_WASAPI);
+       if (api == G_SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
+               rtSystem = new RtAudio(RtAudio::WINDOWS_WASAPI);
 
 #elif defined(__APPLE__)
 
-       if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
-               system = new RtAudio(RtAudio::MACOSX_CORE);
+       if (api == G_SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
+               rtSystem = new RtAudio(RtAudio::MACOSX_CORE);
 
 #endif
 
-       else {
-               G_audio_status = false;
+       else
                return 0;
-       }
 
-       gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate);
+       gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n",
+    conf::soundDeviceOut, conf::soundDeviceIn, conf::samplerate);
 
-       numDevs = system->getDeviceCount();
+       numDevs = rtSystem->getDeviceCount();
 
        if (numDevs < 1) {
                gu_log("[KA] no devices found with this API\n");
                closeDevice();
-               G_audio_status = false;
                return 0;
        }
        else {
@@ -118,62 +149,56 @@ int KernelAudio::openDevice(int _api, int outDev, int inDev, int outChan,
        RtAudio::StreamParameters outParams;
        RtAudio::StreamParameters inParams;
 
-       if (outDev == DEFAULT_SOUNDDEV_OUT)
+       if (conf::soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT)
                outParams.deviceId = getDefaultOut();
        else
-               outParams.deviceId = outDev;
+               outParams.deviceId = conf::soundDeviceOut;
 
        outParams.nChannels = 2;
-       outParams.firstChannel = outChan*2; // chan 0=0, 1=2, 2=4, ...
+       outParams.firstChannel = conf::channelsOut * 2; // chan 0=0, 1=2, 2=4, ...
 
        /* inDevice can be disabled */
 
-       if (inDev != -1) {
-               inParams.deviceId     = inDev;
+       if (conf::soundDeviceIn != -1) {
+               inParams.deviceId     = conf::soundDeviceIn;
                inParams.nChannels    = 2;
-               inParams.firstChannel = inChan*2;   // chan 0=0, 1=2, 2=4, ...
+               inParams.firstChannel = conf::channelsIn * 2;   // chan 0=0, 1=2, 2=4, ...
                inputEnabled = true;
        }
        else
                inputEnabled = false;
 
   RtAudio::StreamOptions options;
-  options.streamName = "Giada";
+  options.streamName = G_APP_NAME;
   options.numberOfBuffers = 4;
 
-       realBufsize = buffersize;
+       realBufsize = conf::buffersize;
 
 #if defined(__linux__) || defined(__APPLE__)
-       if (api == SYS_API_JACK) {
-               samplerate = getFreq(outDev, 0);
-               gu_log("[KA] JACK in use, freq = %d\n", samplerate);
-               G_Conf.samplerate = samplerate;
+
+       if (api == G_SYS_API_JACK) {
+               conf::samplerate = getFreq(conf::soundDeviceOut, 0);
+               gu_log("[KA] JACK in use, freq = %d\n", conf::samplerate);
        }
+
 #endif
 
        try {
-               system->openStream(
+               rtSystem->openStream(
                        &outParams,                                                   // output params
-                       inDev != -1 ? &inParams : nullptr,  // input params if inDevice is selected
+                       conf::soundDeviceIn != -1 ? &inParams : nullptr,  // input params if inDevice is selected
                        RTAUDIO_FLOAT32,                                      // audio format
-                       samplerate,                                                   // sample rate
+                       conf::samplerate,                                               // sample rate
                        &realBufsize,                                         // buffer size in byte
-                       &G_Mixer.masterPlay,                // audio callback
+                       &mixer::masterPlay,                 // audio callback
                        nullptr,                                                                                  // user data (unused)
                        &options);
-               G_audio_status = true;
-
-#if defined(__linux__)
-               if (api == SYS_API_JACK)
-                       jackSetSyncCb();
-#endif
-
+    status = true;
                return 1;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] system init error: %s\n", e.getMessage().c_str());
+               gu_log("[KA] rtSystem init error: %s\n", e.getMessage().c_str());
                closeDevice();
-               G_audio_status = false;
                return 0;
        }
 }
@@ -182,11 +207,11 @@ int KernelAudio::openDevice(int _api, int outDev, int inDev, int outChan,
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::startStream()
+int startStream()
 {
        try {
-               system->startStream();
-               gu_log("[KA] latency = %lu\n", system->getStreamLatency());
+               rtSystem->startStream();
+               gu_log("[KA] latency = %lu\n", rtSystem->getStreamLatency());
                return 1;
        }
        catch (RtAudioError &e) {
@@ -199,10 +224,10 @@ int KernelAudio::startStream()
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::stopStream()
+int stopStream()
 {
        try {
-               system->stopStream();
+               rtSystem->stopStream();
                return 1;
        }
        catch (RtAudioError &e) {
@@ -215,10 +240,10 @@ int KernelAudio::stopStream()
 /* -------------------------------------------------------------------------- */
 
 
-string KernelAudio::getDeviceName(unsigned dev)
+string getDeviceName(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).name;
        }
        catch (RtAudioError &e) {
                gu_log("[KA] invalid device ID = %d\n", dev);
@@ -230,17 +255,17 @@ string KernelAudio::getDeviceName(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::closeDevice()
+int closeDevice()
 {
-       if (system->isStreamOpen()) {
+       if (rtSystem->isStreamOpen()) {
 #if defined(__linux__) || defined(__APPLE__)
-               system->abortStream(); // stopStream seems to lock the thread
+               rtSystem->abortStream(); // stopStream seems to lock the thread
 #elif defined(_WIN32)
-               system->stopStream();    // on Windows it's the opposite
+               rtSystem->stopStream();  // on Windows it's the opposite
 #endif
-               system->closeStream();
-               delete system;
-               system = nullptr;
+               rtSystem->closeStream();
+               delete rtSystem;
+               rtSystem = nullptr;
        }
        return 1;
 }
@@ -249,12 +274,12 @@ int KernelAudio::closeDevice()
 /* -------------------------------------------------------------------------- */
 
 
-unsigned KernelAudio::getMaxInChans(int dev)
+unsigned getMaxInChans(int dev)
 {
        if (dev == -1) return 0;
 
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).inputChannels;
        }
        catch (RtAudioError &e) {
                gu_log("[KA] Unable to get input channels\n");
@@ -266,10 +291,10 @@ unsigned KernelAudio::getMaxInChans(int dev)
 /* -------------------------------------------------------------------------- */
 
 
-unsigned KernelAudio::getMaxOutChans(unsigned dev)
+unsigned getMaxOutChans(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).outputChannels;
        }
        catch (RtAudioError &e) {
                gu_log("[KA] Unable to get output channels\n");
@@ -281,10 +306,10 @@ unsigned KernelAudio::getMaxOutChans(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-bool KernelAudio::isProbed(unsigned dev)
+bool isProbed(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).probed;
        }
        catch (RtAudioError &e) {
                return 0;
@@ -295,10 +320,10 @@ bool KernelAudio::isProbed(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-unsigned KernelAudio::getDuplexChans(unsigned dev)
+unsigned getDuplexChans(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).duplexChannels;
        }
        catch (RtAudioError &e) {
                return 0;
@@ -309,10 +334,10 @@ unsigned KernelAudio::getDuplexChans(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-bool KernelAudio::isDefaultIn(unsigned dev)
+bool isDefaultIn(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultInput;
        }
        catch (RtAudioError &e) {
                return 0;
@@ -323,10 +348,10 @@ bool KernelAudio::isDefaultIn(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-bool KernelAudio::isDefaultOut(unsigned dev)
+bool isDefaultOut(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput;
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultOutput;
        }
        catch (RtAudioError &e) {
                return 0;
@@ -337,10 +362,10 @@ bool KernelAudio::isDefaultOut(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::getTotalFreqs(unsigned dev)
+int getTotalFreqs(unsigned dev)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size();
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.size();
        }
        catch (RtAudioError &e) {
                return 0;
@@ -351,10 +376,10 @@ int KernelAudio::getTotalFreqs(unsigned dev)
 /* -------------------------------------------------------------------------- */
 
 
-int    KernelAudio::getFreq(unsigned dev, int i)
+int    getFreq(unsigned dev, int i)
 {
        try {
-               return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i);
+               return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.at(i);
        }
        catch (RtAudioError &e) {
                return 0;
@@ -365,137 +390,140 @@ int     KernelAudio::getFreq(unsigned dev, int i)
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::getDefaultIn()
+unsigned getRealBufSize()
 {
-       return system->getDefaultInputDevice();
+  return realBufsize;
 }
 
-int KernelAudio::getDefaultOut()
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isInputEnabled()
 {
-       return system->getDefaultOutputDevice();
+  return inputEnabled;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int    KernelAudio::getDeviceByName(const char *name)
+unsigned countDevices()
 {
-       for (unsigned i=0; i<numDevs; i++)
-               if (name == getDeviceName(i))
-                       return i;
-       return -1;
+  return numDevs;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool KernelAudio::hasAPI(int API)
+int getDefaultIn()
 {
-       vector<RtAudio::Api> APIs;
-       RtAudio::getCompiledApi(APIs);
-       for (unsigned i=0; i<APIs.size(); i++)
-               if (APIs.at(i) == API)
-                       return true;
-       return false;
+       return rtSystem->getDefaultInputDevice();
+}
+
+int getDefaultOut()
+{
+       return rtSystem->getDefaultOutputDevice();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string KernelAudio::getRtAudioVersion()
+int    getDeviceByName(const char *name)
 {
-       return RtAudio::getVersion();
+       for (unsigned i=0; i<numDevs; i++)
+               if (name == getDeviceName(i))
+                       return i;
+       return -1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef __linux__
-
-int KernelAudio::jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
-       void *arg)
+bool hasAPI(int API)
 {
-       return G_KernelAudio.__jackSyncCb(state, pos, arg);
+       vector<RtAudio::Api> APIs;
+       RtAudio::getCompiledApi(APIs);
+       for (unsigned i=0; i<APIs.size(); i++)
+               if (APIs.at(i) == API)
+                       return true;
+       return false;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-jack_client_t *KernelAudio::jackGetHandle()
+#ifdef __linux__
+
+
+const JackState &jackTransportQuery()
 {
-       return (jack_client_t*) system->rtapi_->__HACK__getJackClient();
+       if (api != G_SYS_API_JACK)
+    return jackState;
+  jack_position_t position;
+       jack_transport_state_t ts = jack_transport_query(jackGetHandle(), &position);
+  jackState.running = ts != JackTransportStopped;
+  jackState.bpm     = position.beats_per_minute;
+  jackState.frame   = position.frame;
+  return jackState;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelAudio::jackStart()
+void jackStart()
 {
-       if (api == SYS_API_JACK) {
-               jack_client_t *client = jackGetHandle();
-               jack_transport_start(client);
-       }
+       if (api == G_SYS_API_JACK)
+               jack_transport_start(jackGetHandle());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelAudio::jackStop()
+void jackSetPosition(uint32_t frame)
 {
-       if (api == SYS_API_JACK) {
-               jack_client_t *client = jackGetHandle();
-               jack_transport_stop(client);
-       }
+       if (api != G_SYS_API_JACK)
+    return;
+  jack_position_t position;
+  jack_transport_query(jackGetHandle(), &position);
+  position.frame = frame;
+  jack_transport_reposition(jackGetHandle(), &position);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelAudio::jackSetSyncCb()
+void jackSetBpm(double bpm)
 {
-       jack_client_t *client = jackGetHandle();
-       jack_set_sync_callback(client, jackSyncCb, nullptr);
-       //jack_set_sync_timeout(client, 8);
+  if (api != G_SYS_API_JACK)
+    return;
+  jack_position_t position;
+  jack_transport_query(jackGetHandle(), &position);
+  position.valid = jack_position_bits_t::JackPositionBBT;
+  position.bar  = 0;  // no such info from Giada
+  position.beat = 0;  // no such info from Giada
+  position.tick = 0;  // no such info from Giada
+  position.beats_per_minute = bpm;
+  jack_transport_reposition(jackGetHandle(), &position);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int KernelAudio::__jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
-               void *arg)
+void jackStop()
 {
-       switch (state) {
-               case JackTransportStopped:
-                       gu_log("[KA] Jack transport stopped, frame=%d\n", pos->frame);
-                       glue_stopSeq(false);  // false = not from GUI
-                       if (pos->frame == 0)
-                               glue_rewindSeq();
-                       break;
-
-               case JackTransportRolling:
-                       gu_log("[KA] Jack transport rolling\n");
-                       break;
-
-               case JackTransportStarting:
-                       gu_log("[KA] Jack transport starting, frame=%d\n", pos->frame);
-                       glue_startSeq(false);  // false = not from GUI
-                       if (pos->frame == 0)
-                               glue_rewindSeq();
-                       break;
-
-               default:
-                       gu_log("[KA] Jack transport [unknown]\n");
-       }
-       return 1;
+       if (api == G_SYS_API_JACK)
+               jack_transport_stop(jackGetHandle());
 }
 
-#endif
+#endif  // #ifdef __linux__
+
+}}}; // giada::m::kernelAudio
index ba3667ef3b7066951a3b495c65947f41df319d5a..21358c4a403454bc7ea6addefa2e3918a77ede8e 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * KernelAudio
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef KERNELAUDIO_H
-#define KERNELAUDIO_H
+#ifndef G_KERNELAUDIO_H
+#define G_KERNELAUDIO_H
 
 
-#include "../deps/rtaudio-mod/RtAudio.h"
+#include <string>
 #ifdef __linux__
        #include <jack/jack.h>
        #include <jack/intclient.h>
 #endif
 
 
-class KernelAudio
-{
-public:
+class RtAudio;
+class Mixer;
 
-       KernelAudio();
 
-       int openDevice(int api, int outDev,     int inDev, int outChan, int inChan,
-               int samplerate, int buffersize);
+namespace giada {
+namespace m {
+namespace kernelAudio
+{
+#ifdef __linux__
 
-       int closeDevice();
+struct JackState
+{
+  bool running;
+  double bpm;
+  uint32_t frame;
+};
 
-       int startStream();
-       int stopStream();
+#endif
 
-       bool                      isProbed         (unsigned dev);
-       bool                isDefaultIn      (unsigned dev);
-       bool                      isDefaultOut     (unsigned dev);
-       std::string getDeviceName    (unsigned dev);
-       unsigned    getMaxInChans    (int dev);
-       unsigned    getMaxOutChans   (unsigned dev);
-       unsigned    getDuplexChans   (unsigned dev);
-       int         getTotalFreqs    (unsigned dev);
-       int                                     getFreq          (unsigned dev, int i);
-       int                                     getDeviceByName  (const char *name);
-       int         getDefaultOut    ();
-       int         getDefaultIn     ();
-       bool        hasAPI           (int API);
-       std::string getRtAudioVersion();
+void init();
+
+int openDevice();
+int closeDevice();
+int startStream();
+int stopStream();
+
+bool getStatus();
+bool isProbed(unsigned dev);
+bool isDefaultIn(unsigned dev);
+bool isDefaultOut(unsigned dev);
+bool isInputEnabled();
+std::string getDeviceName(unsigned dev);
+unsigned getMaxInChans(int dev);
+unsigned getMaxOutChans(unsigned dev);
+unsigned getDuplexChans(unsigned dev);
+unsigned getRealBufSize();
+unsigned countDevices();
+int getTotalFreqs(unsigned dev);
+int getFreq(unsigned dev, int i);
+int getDeviceByName(const char *name);
+int getDefaultOut();
+int getDefaultIn();
+bool hasAPI(int API);
 
 #ifdef __linux__
 
-       jack_client_t *jackGetHandle();
-       void jackStart();
-       void jackStop();
-       void jackSetSyncCb();
-       static int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
-       int __jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
-       
-#endif
-
-       unsigned numDevs;
-       bool             inputEnabled;
-       unsigned realBufsize;           // reale bufsize from the soundcard
-       int      api;
+void jackStart();
+void jackStop();
+void jackSetPosition(uint32_t frame);
+void jackSetBpm(double bpm);
+const JackState &jackTransportQuery();
 
-private:
-
-       RtAudio *system;
-};
+#endif
+}}}; // giada::m::kernelAudio::
 
 #endif
index f8c519be33314c844848b977c7a1dd38b5871fae..41d57ad0779316d0ac095a201f07687bc7729e95 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * KernelMidi
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <RtMidi.h>
+#include <rtmidi/RtMidi.h>
 #include "../utils/log.h"
 #include "../glue/channel.h"
 #include "../glue/main.h"
+#include "../glue/transport.h"
 #include "../glue/io.h"
 #include "mixer.h"
 #include "const.h"
 #include "kernelMidi.h"
 
 
-extern bool        G_midiStatus;
-extern Conf        G_Conf;
-extern Mixer       G_Mixer;
-extern KernelMidi  G_KernelMidi;
-extern MidiMapConf G_MidiMap;
+using std::string;
+using std::vector;
+
+
+namespace giada {
+namespace m {
+namespace kernelMidi
+{
+namespace
+{
+bool status = false;
+int api = 0;
+RtMidiOut *midiOut = nullptr;
+RtMidiIn  *midiIn  = nullptr;
+unsigned numOutPorts = 0;
+unsigned numInPorts  = 0;
+
+/* cb_learn
+ * callback prepared by the gdMidiGrabber window and called by
+ * kernelMidi. It contains things to do once the midi message has been
+ * stored. */
+
+cb_midiLearn *cb_learn = nullptr;
+void         *cb_data  = nullptr;
+
+
+/* -------------------------------------------------------------------------- */
+
+
 #ifdef WITH_VST
-extern PluginHost  G_PluginHost;
+
+void processPlugins(Channel *ch, uint32_t pure, uint32_t value)
+{
+  /* Plugins' parameters layout reflect the structure of the matrix
+  Channel::midiInPlugins. It is safe to assume then that i and k indexes match
+  both the structure of Channel::midiInPlugins and vector <Plugin *> *plugins. */
+
+  vector <Plugin *> *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
+
+  for (unsigned i=0; i<plugins->size(); i++)
+  {
+    Plugin *plugin = plugins->at(i);
+    for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
+      uint32_t midiInParam = plugin->midiInParams.at(k);
+      if (pure != midiInParam)
+        continue;
+      float vf = (value >> 8)/127.0f;
+      plugin->setParameter(k, vf);
+      gu_log("  >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
+        i, k, ch->index, pure, value >> 8, vf);
+    }
+  }
+}
+
 #endif
 
-using std::string;
-using std::vector;
 
+/* -------------------------------------------------------------------------- */
+
+
+void processChannels(uint32_t pure, uint32_t value)
+{
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+
+               Channel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+
+               if (!ch->midiIn)
+                       continue;
+
+               if      (pure == ch->midiInKeyPress) {
+                       gu_log("  >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
+                       glue_keyPress(ch, false, false);
+               }
+               else if (pure == ch->midiInKeyRel) {
+                       gu_log("  >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
+                       glue_keyRelease(ch, false, false);
+               }
+               else if (pure == ch->midiInMute) {
+                       gu_log("  >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
+                       glue_setMute(ch, false);
+               }
+               else if (pure == ch->midiInSolo) {
+                       gu_log("  >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
+                       ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
+               }
+               else if (pure == ch->midiInVolume) {
+                       float vf = (value >> 8)/127.0f;
+                       gu_log("  >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+                               ch->index, pure, value >> 8, vf);
+                       glue_setVolume(ch, vf, false);
+               }
+               else if (pure == ((SampleChannel*)ch)->midiInPitch) {
+                       float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
+                       gu_log("  >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
+                               ch->index, pure, value >> 8, vf);
+                       glue_setPitch(static_cast<SampleChannel*>(ch), vf);
+               }
+               else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
+                       gu_log("  >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure);
+                       glue_startStopReadingRecs(static_cast<SampleChannel*>(ch), false);
+               }
+
+#ifdef WITH_VST
+    processPlugins(ch, pure, value); // Process plugins' parameters
+#endif
 
-KernelMidi::KernelMidi()
-       :       numOutPorts(0),
-               numInPorts (0),
-               api        (0),      // one api for both in & out
-               midiOut    (nullptr),
-               midiIn     (nullptr),
-               cb_learn   (nullptr),
-               cb_data    (nullptr)
+               /* Redirect full midi message (pure + value) to plugins */
+               ch->receiveMidi(pure | value);
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processMaster(uint32_t pure, uint32_t value)
 {
+  if      (pure == conf::midiInRewind) {
+               gu_log("  >>> rewind (master) (pure=0x%X)\n", pure);
+               glue_rewindSeq(false);
+       }
+       else if (pure == conf::midiInStartStop) {
+               gu_log("  >>> startStop (master) (pure=0x%X)\n", pure);
+               glue_startStopSeq(false);
+       }
+       else if (pure == conf::midiInActionRec) {
+               gu_log("  >>> actionRec (master) (pure=0x%X)\n", pure);
+               glue_startStopActionRec(false);
+       }
+       else if (pure == conf::midiInInputRec) {
+               gu_log("  >>> inputRec (master) (pure=0x%X)\n", pure);
+               glue_startStopInputRec(false);
+       }
+       else if (pure == conf::midiInMetronome) {
+               gu_log("  >>> metronome (master) (pure=0x%X)\n", pure);
+               glue_startStopMetronome(false);
+       }
+       else if (pure == conf::midiInVolumeIn) {
+               float vf = (value >> 8)/127.0f;
+               gu_log("  >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
+                       pure, value >> 8, vf);
+               glue_setInVol(vf, false);
+       }
+       else if (pure == conf::midiInVolumeOut) {
+               float vf = (value >> 8)/127.0f;
+               gu_log("  >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
+                       pure, value >> 8, vf);
+               glue_setOutVol(vf, false);
+       }
+       else if (pure == conf::midiInBeatDouble) {
+               gu_log("  >>> sequencer x2 (master) (pure=0x%X)\n", pure);
+               glue_beatsMultiply();
+       }
+       else if (pure == conf::midiInBeatHalf) {
+               gu_log("  >>> sequencer /2 (master) (pure=0x%X)\n", pure);
+               glue_beatsDivide();
+       }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::callback(double t, vector<unsigned char> *msg, void *data)
+static void callback(double t, std::vector<unsigned char> *msg, void *data)
 {
-       G_KernelMidi.__callback(t, msg, data);
+  /* 0.8.0 - for now we handle other midi signals (common and real-time
+        * messages) as unknown, for debugging purposes */
+
+       if (msg->size() < 3) {
+               gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
+               for (unsigned i=0; i<msg->size(); i++)
+                       gu_log("%X", (int) msg->at(i));
+               gu_log("\n");
+               return;
+       }
+
+       /* in this place we want to catch two things: a) note on/note off
+        * from a keyboard and b) knob/wheel/slider movements from a
+        * controller */
+
+       uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2));
+       uint32_t chan  = input & 0x0F000000;
+       uint32_t value = input & 0x0000FF00;
+       uint32_t pure  = 0x00;
+       if (!conf::noNoteOff)
+               pure = input & 0xFFFF0000;   // input without 'value' byte
+       else
+               pure = input & 0xFFFFFF00;   // input with 'value' byte
+
+       gu_log("[KM] MIDI received - 0x%X (chan %d)\n", input, chan >> 24);
+
+       /* start dispatcher. If midi learn is on don't parse channels, just
+        * learn incoming midi signal. Otherwise process master events first,
+        * then each channel in the stack. This way incoming signals don't
+        * get processed by glue_* when midi learning is on. */
+
+       if (cb_learn)
+               cb_learn(pure, cb_data);
+       else {
+               processMaster(pure, value);
+               processChannels(pure, value);
+       }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::sendMidiLightningInitMsgs()
+void sendMidiLightningInitMsgs()
 {
-       for(unsigned i=0; i<G_MidiMap.initCommands.size(); i++) {
-               MidiMapConf::message_t msg = G_MidiMap.initCommands.at(i);
+  for(unsigned i=0; i<midimap::initCommands.size(); i++) {
+               midimap::message_t msg = midimap::initCommands.at(i);
                if (msg.value != 0x0 && msg.channel != -1) {
                        gu_log("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value);
                        send(msg.value | MIDI_CHANS[msg.channel]);
@@ -94,11 +264,15 @@ void KernelMidi::sendMidiLightningInitMsgs()
        }
 }
 
+}; // {anonymous}
+
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::startMidiLearn(cb_midiLearn *cb, void *data)
+void startMidiLearn(cb_midiLearn *cb, void *data)
 {
        cb_learn = cb;
        cb_data  = data;
@@ -108,7 +282,7 @@ void KernelMidi::startMidiLearn(cb_midiLearn *cb, void *data)
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::stopMidiLearn()
+void stopMidiLearn()
 {
        cb_learn = nullptr;
        cb_data  = nullptr;
@@ -118,7 +292,7 @@ void KernelMidi::stopMidiLearn()
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::setApi(int _api)
+void setApi(int _api)
 {
        api = _api;
        gu_log("[KM] using system 0x%x\n", api);
@@ -128,15 +302,15 @@ void KernelMidi::setApi(int _api)
 /* -------------------------------------------------------------------------- */
 
 
-int KernelMidi::openOutDevice(int port)
+int openOutDevice(int port)
 {
        try {
                midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output");
-               G_midiStatus = true;
+               status = true;
   }
   catch (RtMidiError &error) {
     gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
-    G_midiStatus = false;
+    status = false;
     return 0;
   }
 
@@ -155,14 +329,14 @@ int KernelMidi::openOutDevice(int port)
                        gu_log("[KM] MIDI out port %d open\n", port);
 
                        /* TODO - it shold send midiLightning message only if there is a map loaded
-                       and available in G_MidiMap. */
+                       and available in midimap:: */
 
                        sendMidiLightningInitMsgs();
                        return 1;
                }
                catch (RtMidiError &error) {
                        gu_log("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
-                       G_midiStatus = false;
+                       status = false;
                        return 0;
                }
        }
@@ -174,15 +348,15 @@ int KernelMidi::openOutDevice(int port)
 /* -------------------------------------------------------------------------- */
 
 
-int KernelMidi::openInDevice(int port)
+int openInDevice(int port)
 {
        try {
                midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input");
-               G_midiStatus = true;
+               status = true;
   }
   catch (RtMidiError &error) {
     gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
-    G_midiStatus = false;
+    status = false;
     return 0;
   }
 
@@ -205,7 +379,7 @@ int KernelMidi::openInDevice(int port)
                }
                catch (RtMidiError &error) {
                        gu_log("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
-                       G_midiStatus = false;
+                       status = false;
                        return 0;
                }
        }
@@ -217,7 +391,7 @@ int KernelMidi::openInDevice(int port)
 /* -------------------------------------------------------------------------- */
 
 
-bool KernelMidi::hasAPI(int API)
+bool hasAPI(int API)
 {
        vector<RtMidi::Api> APIs;
        RtMidi::getCompiledApi(APIs);
@@ -231,13 +405,13 @@ bool KernelMidi::hasAPI(int API)
 /* -------------------------------------------------------------------------- */
 
 
-string KernelMidi::getOutPortName(unsigned p)
+string getOutPortName(unsigned p)
 {
        try { return midiOut->getPortName(p); }
        catch (RtMidiError &error) { return ""; }
 }
 
-string KernelMidi::getInPortName(unsigned p)
+string getInPortName(unsigned p)
 {
        try { return midiIn->getPortName(p); }
        catch (RtMidiError &error) { return ""; }
@@ -247,9 +421,9 @@ string KernelMidi::getInPortName(unsigned p)
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::send(uint32_t data)
+void send(uint32_t data)
 {
-       if (!G_midiStatus)
+       if (!status)
                return;
 
   vector<unsigned char> msg(1, getB1(data));
@@ -264,9 +438,9 @@ void KernelMidi::send(uint32_t data)
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::send(int b1, int b2, int b3)
+void send(int b1, int b2, int b3)
 {
-       if (!G_midiStatus)
+       if (!status)
                return;
 
        vector<unsigned char> msg(1, b1);
@@ -284,191 +458,48 @@ void KernelMidi::send(int b1, int b2, int b3)
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::__callback(double t, vector<unsigned char> *msg, void *data)
+unsigned countInPorts()
 {
-       /* 0.8.0 - for now we handle other midi signals (common and real-time
-        * messages) as unknown, for debugging purposes */
-
-       if (msg->size() < 3) {
-               gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
-               for (unsigned i=0; i<msg->size(); i++)
-                       gu_log("%X", (int) msg->at(i));
-               gu_log("\n");
-               return;
-       }
-
-       /* in this place we want to catch two things: a) note on/note off
-        * from a keyboard and b) knob/wheel/slider movements from a
-        * controller */
-
-       uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2));
-       uint32_t chan  = input & 0x0F000000;
-       uint32_t value = input & 0x0000FF00;
-       uint32_t pure  = 0x00;
-       if (!G_Conf.noNoteOff)
-               pure = input & 0xFFFF0000;   // input without 'value' byte
-       else
-               pure = input & 0xFFFFFF00;   // input with 'value' byte
-
-       gu_log("[KM] MIDI received - 0x%X (chan %d)\n", input, chan >> 24);
-
-       /* start dispatcher. If midi learn is on don't parse channels, just
-        * learn incoming midi signal. Otherwise process master events first,
-        * then each channel in the stack. This way incoming signals don't
-        * get processed by glue_* when midi learning is on. */
-
-       if (cb_learn)
-               cb_learn(pure, cb_data);
-       else {
-               processMaster(pure, value);
-               processChannels(pure, value);
-       }
+  return numInPorts;
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void KernelMidi::processMaster(uint32_t pure, uint32_t value)
+unsigned countOutPorts()
 {
-       if      (pure == G_Conf.midiInRewind) {
-               gu_log("  >>> rewind (master) (pure=0x%X)\n", pure);
-               glue_rewindSeq();
-       }
-       else if (pure == G_Conf.midiInStartStop) {
-               gu_log("  >>> startStop (master) (pure=0x%X)\n", pure);
-               glue_startStopSeq(false);
-       }
-       else if (pure == G_Conf.midiInActionRec) {
-               gu_log("  >>> actionRec (master) (pure=0x%X)\n", pure);
-               glue_startStopActionRec(false);
-       }
-       else if (pure == G_Conf.midiInInputRec) {
-               gu_log("  >>> inputRec (master) (pure=0x%X)\n", pure);
-               glue_startStopInputRec(false);
-       }
-       else if (pure == G_Conf.midiInMetronome) {
-               gu_log("  >>> metronome (master) (pure=0x%X)\n", pure);
-               glue_startStopMetronome(false);
-       }
-       else if (pure == G_Conf.midiInVolumeIn) {
-               float vf = (value >> 8)/127.0f;
-               gu_log("  >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
-                       pure, value >> 8, vf);
-               glue_setInVol(vf, false);
-       }
-       else if (pure == G_Conf.midiInVolumeOut) {
-               float vf = (value >> 8)/127.0f;
-               gu_log("  >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
-                       pure, value >> 8, vf);
-               glue_setOutVol(vf, false);
-       }
-       else if (pure == G_Conf.midiInBeatDouble) {
-               gu_log("  >>> sequencer x2 (master) (pure=0x%X)\n", pure);
-               glue_beatsMultiply();
-       }
-       else if (pure == G_Conf.midiInBeatHalf) {
-               gu_log("  >>> sequencer /2 (master) (pure=0x%X)\n", pure);
-               glue_beatsDivide();
-       }
+  return numOutPorts;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void KernelMidi::processChannels(uint32_t pure, uint32_t value)
-{
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
+int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
+int getB3(uint32_t iValue) { return (iValue >> 8)  & 0xFF; }
 
-               Channel *ch = (Channel*) G_Mixer.channels.at(i);
 
-               if (!ch->midiIn)
-                       continue;
-
-               if      (pure == ch->midiInKeyPress) {
-                       gu_log("  >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
-                       glue_keyPress(ch, false, false);
-               }
-               else if (pure == ch->midiInKeyRel) {
-                       gu_log("  >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
-                       glue_keyRelease(ch, false, false);
-               }
-               else if (pure == ch->midiInMute) {
-                       gu_log("  >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
-                       glue_setMute(ch, false);
-               }
-               else if (pure == ch->midiInSolo) {
-                       gu_log("  >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
-                       ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
-               }
-               else if (pure == ch->midiInVolume) {
-                       float vf = (value >> 8)/127.0f;
-                       gu_log("  >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
-                               ch->index, pure, value >> 8, vf);
-                       glue_setChanVol(ch, vf, false);
-               }
-               else if (pure == ((SampleChannel*)ch)->midiInPitch) {
-                       float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
-                       gu_log("  >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
-                               ch->index, pure, value >> 8, vf);
-                       glue_setPitch(nullptr, (SampleChannel*)ch, vf, false);
-               }
-               else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
-                       gu_log("  >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure);
-                       glue_startStopReadingRecs((SampleChannel*)ch, false);
-               }
-
-#ifdef WITH_VST
-
-    /* Process plugins' parameters */
-
-    processPlugins(ch, pure, value);
-
-#endif
-
-               /* Redirect full midi message (pure + value) to plugins */
-
-               ch->receiveMidi(pure | value);
-       }
+uint32_t getIValue(int b1, int b2, int b3)
+{
+  return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef WITH_VST
-
-void KernelMidi::processPlugins(Channel *ch, uint32_t pure, uint32_t value)
+uint32_t setChannel(uint32_t iValue, int channel)
 {
-  /* Plugins' parameters layout reflect the structure of the matrix
-  Channel::midiInPlugins. It is safe to assume then that i and k indexes match
-  both the structure of Channel::midiInPlugins and vector <Plugin *> *plugins. */
-
-  vector <Plugin *> *plugins = G_PluginHost.getStack(PluginHost::CHANNEL, ch);
-
-  for (unsigned i=0; i<plugins->size(); i++)
-  {
-    Plugin *plugin = plugins->at(i);
-    for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
-      uint32_t midiInParam = plugin->midiInParams.at(k);
-      if (pure != midiInParam)
-        continue;
-      float vf = (value >> 8)/127.0f;
-      plugin->setParameter(k, vf);
-      gu_log("  >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
-        i, k, ch->index, pure, value >> 8, vf);
-    }
-  }
+  uint32_t chanMask = 0xF << 24;
+  return (iValue & (~chanMask)) | (channel << 24);
 }
 
-#endif
-
 
 /* -------------------------------------------------------------------------- */
 
 
-string KernelMidi::getRtMidiVersion()
+bool getStatus()
 {
-       return midiOut->getVersion();
+       return status;
 }
+
+}}}; // giada::m::kernelMidi::
index e8cb0f4d259f2086c47e17dfcc4b4d48a7c9a045..700a78aac692e01e955d135a1806b6ca379cecea 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * KernelMidi
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef KERNELMIDI_H
-#define KERNELMIDI_H
+#ifndef G_KERNELMIDI_H
+#define G_KERNELMIDI_H
 
 
-#ifdef __APPLE__  // our compiler still doesn't know about cstdint (c++11 stuff)
+#ifdef __APPLE__  // our Clang still doesn't know about cstdint (c++11 stuff)
        #include <stdint.h>
 #else
        #include <cstdint>
 #include <vector>
 
 
-class KernelMidi
+namespace giada {
+namespace m {
+namespace kernelMidi
 {
-public:
-
-       unsigned numOutPorts;
-       unsigned numInPorts;
-
-       KernelMidi();
-
-       typedef void (cb_midiLearn) (uint32_t, void *);
+typedef void (cb_midiLearn) (uint32_t, void *);
 
-       void startMidiLearn(cb_midiLearn *cb, void *data);
-       void stopMidiLearn();
+void startMidiLearn(cb_midiLearn *cb, void *data);
+void stopMidiLearn();
 
-       int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
-       int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
-       int getB3(uint32_t iValue) { return (iValue >> 8)  & 0xFF; }
+int getB1(uint32_t iValue);
+int getB2(uint32_t iValue);
+int getB3(uint32_t iValue);
 
-       uint32_t getIValue(int b1, int b2, int b3) {
-               return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
-       }
+uint32_t getIValue(int b1, int b2, int b3);
 
-       /* send
-        * send a MIDI message 's' (uint32_t). */
+/* setChannel
+Changes MIDI channel number inside iValue. Returns new message with updated
+channel. */
 
-       void send(uint32_t s);
+uint32_t setChannel(uint32_t iValue, int channel);
 
-       /* send (2)
       * send separate bytes of MIDI message. */
+/* send
* send a MIDI message 's' (uint32_t). */
 
-       void send(int b1, int b2=-1, int b3=-1);
+void send(uint32_t s);
 
-       /* setApi
       * set the Api in use for both in & out messages. */
+/* send (2)
* send separate bytes of MIDI message. */
 
-       void setApi(int api);
+void send(int b1, int b2=-1, int b3=-1);
 
-       /* open/close/in/outDevice */
+/* setApi
+ * set the Api in use for both in & out messages. */
 
-       int openOutDevice(int port);
-       int openInDevice(int port);
-       int closeInDevice();
-       int closeOutDevice();
+void setApi(int api);
 
-       /* getIn/OutPortName
-        * return the name of the port 'p'. */
+/* getStatus
+Returns current engine status. */
 
-       std::string getInPortName(unsigned p);
-       std::string getOutPortName(unsigned p);
+bool getStatus();
 
-       bool hasAPI(int API);
+/* open/close/in/outDevice */
 
-       std::string getRtMidiVersion();
+int openOutDevice(int port);
+int openInDevice(int port);
+int closeInDevice();
+int closeOutDevice();
 
-private:
+/* getIn/OutPortName
+ * return the name of the port 'p'. */
 
-       int api;
-       class RtMidiOut *midiOut;
-       class RtMidiIn  *midiIn;
+std::string getInPortName(unsigned p);
+std::string getOutPortName(unsigned p);
 
-       /* cb_learn
-        * callback prepared by the gdMidiGrabber window and called by
-        * kernelMidi. It contains things to do once the midi message has been
-        * stored. */
+unsigned countInPorts();
+unsigned countOutPorts();
 
-       cb_midiLearn *cb_learn;
-       void         *cb_data;
+bool hasAPI(int API);
 
-       /* callback
-        * master callback for input events. */
+}}}; // giada::m::kernelMidi::
 
-       static void callback(double t, std::vector<unsigned char> *msg, void *data);
-       void __callback(double t, std::vector<unsigned char> *msg, void *data);
-
-       void sendMidiLightningInitMsgs();
-
-
-       void processMaster(uint32_t pure, uint32_t value);
-       void processChannels(uint32_t pure, uint32_t value);
-#ifdef WITH_VST
-  void processPlugins(class Channel *ch, uint32_t pure, uint32_t value);
-#endif
-};
 
 #endif
index ad531d6700d8265a0e7ea9fc3d60a145cf5b672d..0349ac68bb4813b09dee820657daddac97025fba 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -30,8 +28,9 @@
 #include "../utils/log.h"
 #include "midiChannel.h"
 #include "channel.h"
-#include "patch_DEPR_.h"
 #include "patch.h"
+#include "const.h"
+#include "clock.h"
 #include "conf.h"
 #include "mixer.h"
 #ifdef WITH_VST
 #include "kernelMidi.h"
 
 
-extern Recorder   G_Recorder;
-extern KernelMidi G_KernelMidi;
-extern Mixer      G_Mixer;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+using std::string;
+using namespace giada::m;
 
-MidiChannel::MidiChannel(int bufferSize, MidiMapConf *midiMapConf)
-       : Channel    (CHANNEL_MIDI, STATUS_OFF, bufferSize, midiMapConf),
+
+MidiChannel::MidiChannel(int bufferSize)
+       : Channel    (CHANNEL_MIDI, STATUS_OFF, bufferSize),
          midiOut    (false),
          midiOutChan(MIDI_CHANS[0])
 {
@@ -81,9 +77,9 @@ void MidiChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex)
 void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame)
 {
        juce::MidiMessage message = juce::MidiMessage(
-               G_KernelMidi.getB1(msg),
-               G_KernelMidi.getB2(msg),
-               G_KernelMidi.getB3(msg));
+               kernelMidi::getB1(msg),
+               kernelMidi::getB2(msg),
+               kernelMidi::getB3(msg));
        midiBuffer.addEvent(message, localFrame);
 }
 
@@ -111,16 +107,16 @@ void MidiChannel::empty() {}
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::quantize(int index, int localFrame, Mixer *m) {}
+void MidiChannel::quantize(int index, int localFrame) {}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::parseAction(Recorder::action *a, int localFrame,
+void MidiChannel::parseAction(recorder::action *a, int localFrame,
                int globalFrame, int quantize, bool mixerIsRunning)
 {
-       if (a->type == ACTION_MIDI)
+       if (a->type == G_ACTION_MIDI)
                sendMidi(a, localFrame/2);
 }
 
@@ -149,7 +145,7 @@ void MidiChannel::setMute(bool internal)
 {
        mute = true;    // internal mute does not exist for midi (for now)
        if (midiOut)
-               G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+               kernelMidi::send(MIDI_ALL_NOTES_OFF);
 #ifdef WITH_VST
                addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
 #endif
@@ -173,7 +169,7 @@ void MidiChannel::unsetMute(bool internal)
 void MidiChannel::process(float *outBuffer, float *inBuffer)
 {
 #ifdef WITH_VST
-       pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
+       pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
 #endif
 
        /* TODO - isn't this useful only if WITH_VST ? */
@@ -224,7 +220,7 @@ void MidiChannel::kill(int frame)
 {
        if (status & (STATUS_PLAY | STATUS_ENDING)) {
                if (midiOut)
-                       G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+                       kernelMidi::send(MIDI_ALL_NOTES_OFF);
 #ifdef WITH_VST
                addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
 #endif
@@ -237,36 +233,12 @@ void MidiChannel::kill(int frame)
 /* -------------------------------------------------------------------------- */
 
 
-int MidiChannel::readPatch_DEPR_(const char *f, int i, Patch_DEPR_ *patch,
-               int samplerate, int rsmpQuality)
-{
-       volume      = patch->getVol(i);
-       index       = patch->getIndex(i);
-       mute        = patch->getMute(i);
-       mute_s      = patch->getMute_s(i);
-       solo        = patch->getSolo(i);
-       panLeft     = patch->getPanLeft(i);
-       panRight    = patch->getPanRight(i);
-
-       midiOut     = patch->getMidiValue(i, "Out");
-       midiOutChan = patch->getMidiValue(i, "OutChan");
-
-       readPatchMidiIn_DEPR_(i, *patch);
-       readPatchMidiOut_DEPR_(i, *patch);
-
-       return SAMPLE_LOADED_OK;  /// TODO - change name, it's meaningless here
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int MidiChannel::readPatch(const string &basePath, int i, Patch *patch,
+int MidiChannel::readPatch(const string &basePath, int i,
                pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
 {
-       Channel::readPatch("", i, patch, pluginMutex, samplerate, rsmpQuality);
+       Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
 
-       Patch::channel_t *pch = &patch->channels.at(i);
+       patch::channel_t *pch = &patch::channels.at(i);
 
        midiOut     = pch->midiOut;
        midiOutChan = pch->midiOutChan;
@@ -278,11 +250,11 @@ int MidiChannel::readPatch(const string &basePath, int i, Patch *patch,
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::sendMidi(Recorder::action *a, int localFrame)
+void MidiChannel::sendMidi(recorder::action *a, int localFrame)
 {
        if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
                if (midiOut)
-                       G_KernelMidi.send(a->iValue | MIDI_CHANS[midiOutChan]);
+                       kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]);
 
 #ifdef WITH_VST
                addVstMidiEvent(a->iValue, localFrame);
@@ -295,7 +267,7 @@ void MidiChannel::sendMidi(uint32_t data)
 {
        if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
                if (midiOut)
-                       G_KernelMidi.send(data | MIDI_CHANS[midiOutChan]);
+                       kernelMidi::send(data | MIDI_CHANS[midiOutChan]);
 #ifdef WITH_VST
                addVstMidiEvent(data, 0);
 #endif
@@ -309,7 +281,7 @@ void MidiChannel::sendMidi(uint32_t data)
 void MidiChannel::rewind()
 {
        if (midiOut)
-               G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+               kernelMidi::send(MIDI_ALL_NOTES_OFF);
 #ifdef WITH_VST
                addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
 #endif
@@ -319,10 +291,10 @@ void MidiChannel::rewind()
 /* -------------------------------------------------------------------------- */
 
 
-int MidiChannel::writePatch(int i, bool isProject, Patch *patch)
+int MidiChannel::writePatch(int i, bool isProject)
 {
-       int pchIndex = Channel::writePatch(i, isProject, patch);
-       Patch::channel_t *pch = &patch->channels.at(pchIndex);
+       int pchIndex = Channel::writePatch(i, isProject);
+       patch::channel_t *pch = &patch::channels.at(pchIndex);
 
        pch->midiOut     = midiOut;
        pch->midiOutChan = midiOutChan;
@@ -339,21 +311,37 @@ void MidiChannel::receiveMidi(uint32_t msg)
   if (!armed)
     return;
 
+  /* Filter time, based on MIDI channel. If midiFilter == -1, all messages
+  are taken into account. */
+  /* TODO - actual MIDI filtering not yet implemented. */
+
+  if (midiFilter != -1)
+  {
+    gu_log("[Channel::processMidi] MIDI channel filtering not yet implemented\n");
+    return;
+  }
+
+  /* Now all messages are turned into Channel-0 messages. Giada doesn't care about
+  holding MIDI channel information. Moreover, having all internal messages on
+  channel 0 is way easier. */
+
+  msg = kernelMidi::setChannel(msg, 0);
+
 #ifdef WITH_VST
 
   while (true) {
-    if (pthread_mutex_trylock(&G_PluginHost.mutex_midi) != 0)
+    if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0)
       continue;
     gu_log("[Channel::processMidi] msg=%X\n", msg);
     addVstMidiEvent(msg, 0);
-    pthread_mutex_unlock(&G_PluginHost.mutex_midi);
+    pthread_mutex_unlock(&pluginHost::mutex_midi);
     break;
   }
 
 #endif
 
-       if (G_Recorder.canRec(this, &G_Mixer)) {
-               G_Recorder.rec(index, ACTION_MIDI, G_Mixer.currentFrame, msg);
+       if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
+               recorder::rec(index, G_ACTION_MIDI, clock::getCurrentFrame(), msg);
     hasActions = true;
   }
 }
index 94336b8d3c9aed966423b7d61aa400bc4a5571ad..3d938e268214a56b3a3618f23b4caf7efc79cccb 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef MIDI_CHANNEL_H
-#define MIDI_CHANNEL_H
+#ifndef G_MIDI_CHANNEL_H
+#define G_MIDI_CHANNEL_H
 
 
 #ifdef WITH_VST
        #include "../deps/juce-config.h"
 #endif
-
 #include "channel.h"
 
 
+class MidiMapConf;
+class Patch;
+
+
 class MidiChannel : public Channel
 {
 public:
 
-       MidiChannel(int bufferSize, class MidiMapConf *midiMapConf);
+       MidiChannel(int bufferSize);
        ~MidiChannel();
 
   bool    midiOut;           // enable midi output
@@ -60,15 +61,13 @@ public:
        void rewind() override;
        void setMute(bool internal) override;
        void unsetMute(bool internal) override;
-       int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
-                       int samplerate, int rsmpQuality) override;
-       int readPatch(const string &basePath, int i, class Patch *patch,
-                       pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
-       int writePatch(int i, bool isProject, class Patch *patch) override;
-       void quantize(int index, int localFrame, class Mixer *m) override;
+       int readPatch(const std::string &basePath, int i, pthread_mutex_t *pluginMutex,
+    int samplerate, int rsmpQuality) override;
+       int writePatch(int i, bool isProject) override;
+       void quantize(int index, int localFrame) override;
        void onZero(int frame, bool recsStopOnChanHalt) override;
        void onBar(int frame) override;
-       void parseAction(Recorder::action *a, int localFrame, int globalFrame,
+       void parseAction(giada::m::recorder::action *a, int localFrame, int globalFrame,
                        int quantize, bool mixerIsRunning) override;
        void receiveMidi(uint32_t msg) override;
        bool canInputRec() override;
@@ -78,7 +77,7 @@ public:
        /* sendMidi
         * send Midi event to the outside world. */
 
-       void sendMidi(Recorder::action *a, int localFrame);
+       void sendMidi(giada::m::recorder::action *a, int localFrame);
        void sendMidi(uint32_t data);
 
 #ifdef WITH_VST
index c2b40eb301cf5fd0d4cd2b6629e35d5254f62f9b..8b70f1488a4c2858c8ebbc53eaaa796a1de2099e 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiMapConf
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <stdlib.h>
 #include <vector>
-#include <iostream>
 #include <string>
-#include <sstream>
+#include <cstring>
 #include <dirent.h>
-#include "midiMapConf.h"
-#include "const.h"
 #include "../utils/string.h"
 #include "../utils/log.h"
+#include "../utils/fs.h"
+#include "const.h"
+#include "storager.h"
+#include "midiMapConf.h"
 
 
 using std::string;
 using std::vector;
 
 
+namespace giada {
+namespace m {
+namespace midimap
+{
+namespace
+{
+bool readInitCommands(json_t *jContainer)
+{
+       json_t *jInitCommands = json_object_get(jContainer, MIDIMAP_KEY_INIT_COMMANDS);
+  if (!storager::checkArray(jInitCommands, MIDIMAP_KEY_INIT_COMMANDS))
+    return 0;
+
+       size_t commandIndex;
+       json_t *jInitCommand;
+       json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
+
+               string indexStr = "init command " + gu_itoa(commandIndex);
+               if (!storager::checkObject(jInitCommand, indexStr.c_str()))
+                       return 0;
+
+               message_t message;
+    if (!storager::setInt(jInitCommand, MIDIMAP_KEY_CHANNEL, message.channel)) return 0;
+    if (!storager::setString(jInitCommand, MIDIMAP_KEY_MESSAGE, message.valueStr)) return 0;
+               message.value = strtoul(message.valueStr.c_str(), nullptr, 16);
+
+    initCommands.push_back(message);
+       }
+
+       return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
 
-void MidiMapConf::init()
+
+bool readCommand(json_t *jContainer, message_t *msg, const string &key)
+{
+       json_t *jCommand = json_object_get(jContainer, key.c_str());
+  if (!storager::checkObject(jCommand, key.c_str()))
+    return 0;
+
+  if (!storager::setInt(jCommand, MIDIMAP_KEY_CHANNEL, msg->channel)) return 0;
+  if (!storager::setString(jCommand, MIDIMAP_KEY_MESSAGE, msg->valueStr)) return 0;
+
+       return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parse(message_t *message)
+{
+       /* Remove '0x' part from the original string. */
+
+       string input = message->valueStr.replace(0, 2, "");
+
+       /* Then transform string value into the actual uint32_t value, by parsing
+        * each char (i.e. nibble) in the original string. Substitute 'n' with
+        * zeros. */
+
+       string output;
+       for (unsigned i=0, p=24; i<input.length(); i++, p-=4) {
+               if (input[i] == 'n') {
+                       output += '0';
+                       if (message->offset == -1) // do it once
+                               message->offset = p;
+               }
+               else
+                       output += input[i];
+       }
+
+       /* from string to uint32_t */
+
+       message->value = strtoul(output.c_str(), nullptr, 16);
+
+       gu_log("[parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
+                       message->channel, message->valueStr.c_str(), message->value, message->offset);
+}
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+
+string brand;
+string device;
+vector<message_t> initCommands;
+message_t muteOn;
+message_t muteOff;
+message_t soloOn;
+message_t soloOff;
+message_t waiting;
+message_t playing;
+message_t stopping;
+message_t stopped;
+
+string midimapsPath;
+vector<string> maps;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void init()
 {
        midimapsPath = gu_getHomePath() + G_SLASH + "midimaps" + G_SLASH;
 
        /* scan dir of midi maps and load the filenames into <>maps. */
 
-       gu_log("[MidiMapConf::init] scanning midimaps directory...\n");
+       gu_log("[init] scanning midimaps directory...\n");
 
   DIR    *dp;
   dirent *ep;
   dp = opendir(midimapsPath.c_str());
 
        if (!dp) {
-               gu_log("[MidiMapConf::init] unable to scan midimaps directory!\n");
+               gu_log("[init] unable to scan midimaps directory!\n");
                return;
        }
 
@@ -67,12 +172,12 @@ void MidiMapConf::init()
 
                // TODO - check if is a valid midimap file (verify headers)
 
-               gu_log("[MidiMapConf::init] found midimap '%s'\n", ep->d_name);
+               gu_log("[init] found midimap '%s'\n", ep->d_name);
 
                maps.push_back(ep->d_name);
        }
 
-       gu_log("[MidiMapConf::init] total midimaps found: %d\n", maps.size());
+       gu_log("[init] total midimaps found: %d\n", maps.size());
        closedir(dp);
 }
 
@@ -80,7 +185,7 @@ void MidiMapConf::init()
 /* -------------------------------------------------------------------------- */
 
 
-void MidiMapConf::setDefault()
+void setDefault()
 {
        brand  = "";
        device = "";
@@ -122,24 +227,25 @@ void MidiMapConf::setDefault()
 /* -------------------------------------------------------------------------- */
 
 
-int MidiMapConf::read(const string &file)
+int read(const string &file)
 {
        if (file.empty()) {
-               gu_log("[MidiMapConf::read] midimap not specified, nothing to do\n");
+               gu_log("[read] midimap not specified, nothing to do\n");
                return MIDIMAP_NOT_SPECIFIED;
        }
 
-       gu_log("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str());
+       gu_log("[read] reading midimap file '%s'\n", file.c_str());
 
+  json_error_t jError;
        string path = midimapsPath + file;
-       jRoot = json_load_file(path.c_str(), 0, &jError);
+  json_t *jRoot = json_load_file(path.c_str(), 0, &jError);
        if (!jRoot) {
-    gu_log("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
+    gu_log("[read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
     return MIDIMAP_UNREADABLE;
   }
 
-       if (!setString(jRoot, MIDIMAP_KEY_BRAND, brand))   return MIDIMAP_UNREADABLE;
-  if (!setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE;
+       if (!storager::setString(jRoot, MIDIMAP_KEY_BRAND, brand))   return MIDIMAP_UNREADABLE;
+  if (!storager::setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE;
        if (!readInitCommands(jRoot)) return MIDIMAP_UNREADABLE;
        if (!readCommand(jRoot, &muteOn,   MIDIMAP_KEY_MUTE_ON))  return MIDIMAP_UNREADABLE;
        if (!readCommand(jRoot, &muteOff,  MIDIMAP_KEY_MUTE_OFF)) return MIDIMAP_UNREADABLE;
@@ -164,269 +270,4 @@ int MidiMapConf::read(const string &file)
        return MIDIMAP_READ_OK;
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-bool MidiMapConf::readInitCommands(json_t *jContainer)
-{
-       json_t *jInitCommands = json_object_get(jContainer, MIDIMAP_KEY_INIT_COMMANDS);
-  if (!checkArray(jInitCommands, MIDIMAP_KEY_INIT_COMMANDS))
-    return 0;
-
-       size_t commandIndex;
-       json_t *jInitCommand;
-       json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
-
-               string indexStr = "init command " + gu_itoa(commandIndex);
-               if (!checkObject(jInitCommand, indexStr.c_str()))
-                       return 0;
-
-               message_t message;
-    if (!setInt(jInitCommand, MIDIMAP_KEY_CHANNEL, message.channel)) return 0;
-    if (!setString(jInitCommand, MIDIMAP_KEY_MESSAGE, message.valueStr)) return 0;
-               message.value = strtoul(message.valueStr.c_str(), NULL, 16);
-
-    initCommands.push_back(message);
-       }
-
-       return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool MidiMapConf::readCommand(json_t *jContainer, message_t *msg, const string &key)
-{
-       json_t *jCommand = json_object_get(jContainer, key.c_str());
-  if (!checkObject(jCommand, key.c_str()))
-    return 0;
-
-  if (!setInt(jCommand, MIDIMAP_KEY_CHANNEL, msg->channel)) return 0;
-  if (!setString(jCommand, MIDIMAP_KEY_MESSAGE, msg->valueStr)) return 0;
-
-       return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::parse(message_t *message)
-{
-       /* Remove '0x' part from the original string. */
-
-       string input = message->valueStr.replace(0, 2, "");
-
-       /* Then transform string value into the actual uint32_t value, by parsing
-        * each char (i.e. nibble) in the original string. Substitute 'n' with
-        * zeros. */
-
-       string output;
-       for (unsigned i=0, p=24; i<input.length(); i++, p-=4) {
-               if (input[i] == 'n') {
-                       output += '0';
-                       if (message->offset == -1) // do it once
-                               message->offset = p;
-               }
-               else
-                       output += input[i];
-       }
-
-       /* from string to uint32_t */
-
-       message->value = strtoul(output.c_str(), NULL, 16);
-
-       gu_log("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
-                       message->channel, message->valueStr.c_str(), message->value, message->offset);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::setDefault_DEPR_()
-{
-       brand  = "";
-       device = "";
-
-       for (int i=0; i<MAX_INIT_COMMANDS; i++) {
-               init_channels[i] = -1;
-               init_messages[i] = 0x00;
-       }
-
-       muteOnChan     = 0;
-       muteOnOffset   = 0;
-       muteOnMsg      = 0;
-
-       muteOffChan    = 0;
-       muteOffOffset  = 0;
-       muteOffMsg     = 0;
-
-       soloOnChan     = 0;
-       soloOnOffset   = 0;
-       soloOnMsg      = 0;
-
-       soloOffChan    = 0;
-       soloOffOffset  = 0;
-       soloOffMsg     = 0;
-
-       waitingChan    = 0;
-       waitingOffset  = 0;
-       waitingMsg     = 0;
-
-       playingChan    = 0;
-       playingOffset  = 0;
-       playingMsg     = 0;
-
-       stoppingChan   = 0;
-       stoppingOffset = 0;
-       stoppingMsg    = 0;
-
-       stoppedChan    = 0;
-       stoppedOffset  = 0;
-       stoppedMsg     = 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int MidiMapConf::readMap_DEPR_(string file)
-{
-       if (file.empty()) {
-               gu_log("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n");
-               return MIDIMAP_NOT_SPECIFIED;
-       }
-
-       gu_log("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str());
-
-       string path = midimapsPath + file;
-       fp = fopen(path.c_str(), "r");
-       if (!fp) {
-               gu_log("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n");
-               return MIDIMAP_UNREADABLE;
-       }
-
-       brand  = getValue("brand");
-       device = getValue("device");
-
-       if (brand.empty() || device.empty()) {
-               gu_log("[MidiMapConf::readMap_DEPR_] invalid midimap file\n");
-               return MIDIMAP_INVALID;
-       }
-
-       gu_log("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n",
-                       brand.c_str(), device.c_str());
-
-       /* parse init commands */
-
-       vector<string> ic;
-       gu_split(getValue("init_commands"), ";", &ic);
-       for (unsigned i=0; i<(unsigned)MAX_INIT_COMMANDS && i<ic.size(); i++) {
-               sscanf(ic.at(i).c_str(), "%d:%x", &init_channels[i], &init_messages[i]);
-               gu_log("[MidiMapConf::readMap_DEPR_] init command %d - channel %d - message 0x%X\n",
-                               i, init_channels[i], init_messages[i]);
-
-               /* forward compatibility */
-               message_t message;
-               message.channel = init_channels[i];
-               message.value   = init_messages[i];
-               initCommands.push_back(message);
-       }
-
-       /* parse messages */
-
-       parse_DEPR_("mute_on",  &muteOnChan,   &muteOnMsg,   &muteOnOffset);
-       parse_DEPR_("mute_off", &muteOffChan,  &muteOffMsg,  &muteOffOffset);
-       parse_DEPR_("solo_on",  &soloOnChan,   &soloOnMsg,   &soloOnOffset);
-       parse_DEPR_("solo_off", &soloOffChan,  &soloOffMsg,  &soloOffOffset);
-       parse_DEPR_("waiting",  &waitingChan,  &waitingMsg,  &waitingOffset);
-       parse_DEPR_("playing",  &playingChan,  &playingMsg,  &playingOffset);
-       parse_DEPR_("stopping", &stoppingChan, &stoppingMsg, &stoppingOffset);
-       parse_DEPR_("stopped",  &stoppedChan,  &stoppedMsg,  &stoppedOffset);
-
-       /* forward compatibility with new JSON-based midimaps. This stuff will be
-       wiped out soon. */
-
-       muteOn.channel   = muteOnChan;
-       muteOn.offset    = muteOnOffset;
-       muteOn.value     = muteOnMsg;
-       muteOff.channel  = muteOffChan;
-       muteOff.offset   = muteOffOffset;
-       muteOff.value    = muteOffMsg;
-       soloOn.channel   = soloOnChan;
-       soloOn.offset    = soloOnOffset;
-       soloOn.value     = soloOnMsg;
-       soloOff.channel  = soloOffChan;
-       soloOff.offset   = soloOffOffset;
-       soloOff.value    = soloOffMsg;
-       waiting.channel  = waitingChan;
-       waiting.offset   = waitingOffset;
-       waiting.value    = waitingMsg;
-       playing.channel  = playingChan;
-       playing.offset   = playingOffset;
-       playing.value    = playingMsg;
-       stopping.channel = stoppingChan;
-       stopping.offset  = stoppingOffset;
-       stopping.value   = stoppingMsg;
-       stopped.channel  = stoppedChan;
-       stopped.offset   = stoppedOffset;
-       stopped.value    = stoppedMsg;
-
-       close_DEPR_();
-       return MIDIMAP_READ_OK;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::close_DEPR_()
-{
-       if (fp != NULL)
-               fclose(fp);
-       fp = NULL;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::parse_DEPR_(string key, int *chan, uint32_t *msg, int *offset)
-{
-       gu_log("[MidiMapConf::parse_DEPR_] command %s - ", key.c_str());
-       string value = getValue(key.c_str());
-
-       /* grab channel part, i.e. [channel]:*/
-
-       *chan = atoi(value.substr(0, value.find(':')).c_str());
-
-       /* grab MIDI part :[midi-message] and search for 'nn' note placeholder within.
-        * Note: when using 'string::npos' as the value for a len (or sublen)
-        * parameter in string's member functions, means "until the end of the
-        * string". */
-
-       string midiParts = value.substr(value.find(':')+3, string::npos);
-
-       char strmsg[MAX_MIDI_NIBBLES];
-       *offset = 0;
-
-       /* build the message as a string, for each char (i.e. nibble) in the
-        * original string. Substitute 'n' with zeros. */
-
-       for (unsigned i=0, p=24; i<(unsigned)MAX_MIDI_NIBBLES; i++, p-=4) {
-               if (midiParts[i] == 'n') {
-                       strmsg[i] = '0';
-                       if (*offset == 0)
-                               *offset = p;
-               }
-               else
-                       strmsg[i] = midiParts[i];
-       }
-
-       *msg = strtoul(strmsg, NULL, 16);  // from string to uint32_t
-
-       gu_log("chan=%d value=%s msg=%#x, offset=%d\n", *chan, midiParts.c_str(), *msg, *offset);
-}
+}}}; // giada::m::midimap::
index f7e76df2e2e05a4e14fef39c86b2c0266dd53729..7080705e7c9f32a47a664ebfc9647b979795ad92 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiMapConf
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __MIDIMAPCONF_H__
-#define __MIDIMAPCONF_H__
+#ifndef G_MIDIMAPCONF_H
+#define G_MIDIMAPCONF_H
 
 
-#include <limits.h>
-#include <stdint.h>
 #include <vector>
-#include "dataStorageIni.h"
-#include "dataStorageJson.h"
-#include "../utils/fs.h"
-#if defined(__APPLE__)
-#include <pwd.h>
-#endif
-
-
-using std::string;
-using std::vector;
+#include <string>
 
 
-class MidiMapConf : public DataStorageIni, public DataStorageJson
+namespace giada {
+namespace m {
+namespace midimap
 {
-public:
-
-       struct message_t
-  {
-    int      channel;
-    string   valueStr;
-               int      offset;
-               uint32_t value;
-  };
-
-       string brand;
-  string device;
-       vector<message_t> initCommands;
-       message_t muteOn;
-       message_t muteOff;
-       message_t soloOn;
-       message_t soloOff;
-       message_t waiting;
-       message_t playing;
-       message_t stopping;
-       message_t stopped;
-
-       /* midimapsPath
-        * path of midimap files, different between OSes. */
-
-       string midimapsPath;
-
-       /* maps
-        * Maps are the available .giadamap files. Each element of the vector
-        * represents a .giadamap filename. */
-
-       vector<string> maps;
-
-       /* init
-       Parse the midi maps folders and find the available maps. */
-
-       void init();
-
-       /* setDefault
-       Set default values in case no maps are available/choosen. */
-
-       void setDefault();
-
-       /* read
-       Read a midi map from file 'file'. */
-
-       int read(const string &file);
-
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
-
-       static const int MAX_INIT_COMMANDS = 32;
-       static const int MAX_MIDI_BYTES = 4;
-       static const int MAX_MIDI_NIBBLES = 8;
-
-       /* init_*
-        * init_commands. These messages are sent to the physical device as a wake up
-        * signal. */
-
-       int      init_channels[MAX_INIT_COMMANDS];
-       uint32_t init_messages[MAX_INIT_COMMANDS];
-
-       /* events
-        * [event]Channel: the MIDI output channel to send the event to
-        * [event]notePos: the byte where the note is stored ('nn' placeholder)
-        * [event]offset:  the note offset (i.e. of 'nn' placeholder) */
-
-       int      muteOnChan;
-       int      muteOnOffset;
-       uint32_t muteOnMsg;
-
-       int      muteOffChan;
-       int      muteOffOffset;
-       uint32_t muteOffMsg;
-
-       int      soloOnChan;
-       int      soloOnOffset;
-       uint32_t soloOnMsg;
-
-       int      soloOffChan;
-       int      soloOffOffset;
-       uint32_t soloOffMsg;
-
-       int      waitingChan;
-       int      waitingOffset;
-       uint32_t waitingMsg;
-
-       int      playingChan;
-       int      playingOffset;
-       uint32_t playingMsg;
+struct message_t
+{
+  int         channel;
+  std::string valueStr;
+       int         offset;
+       uint32_t    value;
+};
 
-       int      stoppingChan;
-       int      stoppingOffset;
-       uint32_t stoppingMsg;
+extern std::string brand;
+extern std::string device;
+extern std::vector<message_t> initCommands;
+extern message_t muteOn;
+extern message_t muteOff;
+extern message_t soloOn;
+extern message_t soloOff;
+extern message_t waiting;
+extern message_t playing;
+extern message_t stopping;
+extern message_t stopped;
 
-       int      stoppedChan;
-       int      stoppedOffset;
-       uint32_t stoppedMsg;
+/* midimapsPath
+ * path of midimap files, different between OSes. */
 
-       /* setDefault
-       Set default values in case no maps are available/choosen. */
+extern std::string midimapsPath;
 
-       void setDefault_DEPR_();
+/* maps
+ * Maps are the available .giadamap files. Each element of the std::vector
+ * represents a .giadamap filename. */
 
-       /* readMap
-       Read a midi map from file 'file'. */
+extern std::vector<std::string> maps;
 
-       int readMap_DEPR_(string file);
+/* init
+Parse the midi maps folders and find the available maps. */
 
-private:
+void init();
 
-       bool readInitCommands(json_t *jContainer);
+/* setDefault
+Set default values in case no maps are available/choosen. */
 
-       bool readCommand(json_t *jContainer, message_t *msg, const string &key);
+void setDefault();
 
-       void parse(message_t *message);
+/* read
+Read a midi map from file 'file'. */
 
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
-       /* --- DEPRECATED STUFF --------------------------------------------------- */
+int read(const std::string &file);
 
-       void close_DEPR_();
-       void parse_DEPR_(string key, int *chan, uint32_t *msg, int *offset);
-};
+}}}; // giada::m::midimap::
 
 #endif
index efe2532c85f0a40b48f3cc0d59f75e01786f8495..1f5f6612880c283958b866bd35ed0b2a5f106451 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mixer
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
+#include <cstring>
+#include "../deps/rtaudio-mod/RtAudio.h"
 #include "../utils/log.h"
 #include "wave.h"
+#include "kernelAudio.h"
 #include "recorder.h"
 #include "pluginHost.h"
-#include "patch_DEPR_.h"
 #include "conf.h"
 #include "mixerHandler.h"
+#include "clock.h"
+#include "const.h"
 #include "channel.h"
 #include "sampleChannel.h"
 #include "midiChannel.h"
-#include "kernelMidi.h"
 #include "mixer.h"
 
 
-extern KernelAudio G_KernelAudio;
-extern Mixer                    G_Mixer;
-extern Recorder    G_Recorder;
-extern KernelMidi  G_KernelMidi;
-extern MidiMapConf G_MidiMap;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf                             G_Conf;
-#ifdef WITH_VST
-extern PluginHost  G_PluginHost;
-#endif
-
-
-Mixer::Mixer()
-       : vChanInput(NULL),
-               vChanInToOut(NULL)
-{}
-
-
-/* -------------------------------------------------------------------------- */
-
-
+namespace giada {
+namespace m {
+namespace mixer
+{
+namespace
+{
 #define TICKSIZE 38
 
 
-float Mixer::tock[TICKSIZE] = {
-        0.059033,  0.117240,  0.173807,  0.227943,  0.278890,  0.325936,
-        0.368423,  0.405755,  0.437413,  0.462951,  0.482013,  0.494333,
-        0.499738,  0.498153,  0.489598,  0.474195,  0.452159,  0.423798,
-        0.389509,  0.349771,  0.289883,  0.230617,  0.173194,  0.118739,
-        0.068260,  0.022631, -0.017423, -0.051339,     -0.078721, -0.099345,
      -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
      -0.070862, -0.048844
+float tock[TICKSIZE] = {
+  0.059033,  0.117240,  0.173807,  0.227943,  0.278890,  0.325936,
+  0.368423,  0.405755,  0.437413,  0.462951,  0.482013,  0.494333,
+  0.499738,  0.498153,  0.489598,  0.474195,  0.452159,  0.423798,
+  0.389509,  0.349771,  0.289883,  0.230617,  0.173194,  0.118739,
+  0.068260,  0.022631, -0.017423, -0.051339,   -0.078721, -0.099345,
+ -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
+ -0.070862, -0.048844
 };
 
 
-float Mixer::tick[TICKSIZE] = {
-         0.175860,  0.341914,  0.488904,  0.608633,  0.694426,  0.741500,
-         0.747229,  0.711293,  0.635697,  0.524656,  0.384362,  0.222636,
-         0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
       -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
       -0.201360, -0.067752,  0.052194,  0.151746,  0.226280,  0.273493,
-         0.293425,  0.288307,  0.262252,  0.220811,  0.170435,  0.117887,
-         0.069639,  0.031320
+float tick[TICKSIZE] = {
+  0.175860,  0.341914,  0.488904,  0.608633,  0.694426,  0.741500,
+  0.747229,  0.711293, 0.635697,  0.524656,  0.384362,  0.222636,
+  0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
+ -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
+ -0.201360, -0.067752,  0.052194,  0.151746,  0.226280,  0.273493,
+  0.293425,  0.288307,  0.262252,  0.220811,  0.170435,  0.117887,
+  0.069639,  0.031320
 };
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::init()
-{
-       quanto      = 1;
-       docross     = false;
-       rewindWait  = false;
-       running     = false;
-       recording   = false;
-       ready       = true;
-       waitRec     = 0;
-       currentFrame = 0;
-       bpm                 = DEFAULT_BPM;
-       bars                = DEFAULT_BARS;
-       beats               = DEFAULT_BEATS;
-       quantize    = DEFAULT_QUANTIZE;
-       metronome   = false;
-
-       tickTracker = 0;
-       tockTracker = 0;
-       tickPlay    = false;
-       tockPlay    = false;
-
-       outVol       = DEFAULT_OUT_VOL;
-       inVol        = DEFAULT_IN_VOL;
-       peakOut      = 0.0f;
-       peakIn       = 0.0f;
-       inputTracker = 0;
-
-       actualBeat    = 0;
-
-       midiTCstep    = 0;
-       midiTCrate    = (G_Conf.samplerate / G_Conf.midiTCfps) * 2;  // dealing with stereo vals
-       midiTCframes  = 0;
-       midiTCseconds = 0;
-       midiTCminutes = 0;
-       midiTChours   = 0;
-
-       /* alloc virtual input channels. vChanInput malloc is done in
-        * updateFrameBars, because of its variable size */
-       /** TODO - set kernelAudio::realBufsize * 2 as private member */
-
-       vChanInput   = NULL;
-       vChanInToOut = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
-
-       pthread_mutex_init(&mutex_recs, NULL);
-       pthread_mutex_init(&mutex_chans, NULL);
-       pthread_mutex_init(&mutex_plugins, NULL);
-
-       updateFrameBars();
-       rewind();
-}
-
+/* lineInRec
+Records from line in. */
 
-/* -------------------------------------------------------------------------- */
-
-
-Channel *Mixer::addChannel(int type)
+void lineInRec(float *inBuf, unsigned frame)
 {
-       Channel *ch;
-       int bufferSize = G_KernelAudio.realBufsize * 2;
-
-       if (type == CHANNEL_SAMPLE)
-               ch = new SampleChannel(bufferSize, &G_MidiMap);
-       else
-               ch = new MidiChannel(bufferSize, &G_MidiMap);
+       if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording)
+               return;
 
-#ifdef WITH_VST
-       ch->setPluginHost(&G_PluginHost);
-#endif
+       /* Delay comp: wait until waitRec reaches delayComp. WaitRec
+        * returns to 0 in mixerHandler, as soon as the recording ends */
 
-       while (true) {
-               int lockStatus = pthread_mutex_trylock(&mutex_chans);
-               if (lockStatus == 0) {
-                       channels.push_back(ch);
-                       pthread_mutex_unlock(&mutex_chans);
-                       break;
-               }
+       if (waitRec < conf::delayComp) {
+               waitRec += 2;
+               return;
        }
 
-       ch->index = getNewIndex();
-       gu_log("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size());
-       return ch;
+       vChanInput[inputTracker]   += inBuf[frame]   * inVol;
+       vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
+       inputTracker += 2;
+       if (inputTracker >= clock::getTotalFrames())
+               inputTracker = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* ProcessLineIn
+Computes line in peaks, plus handles "hear what you're playin'" thing. */
 
-int Mixer::getNewIndex()
+void processLineIn(float *inBuf, unsigned frame)
 {
-       /* always skip last channel: it's the last one just added */
-
-       if (channels.size() == 1)
-               return 0;
-
-       int index = 0;
-       for (unsigned i=0; i<channels.size()-1; i++) {
-               if (channels.at(i)->index > index)
-                       index = channels.at(i)->index;
-               }
-       index += 1;
-       return index;
-}
-
-
-/* -------------------------------------------------------------------------- */
+       if (!kernelAudio::isInputEnabled())
+               return;
 
+       /* input peak calculation (left chan only so far). */
 
-int Mixer::deleteChannel(Channel *ch)
-{
-       int index = -1;
-       for (unsigned i=0; i<channels.size(); i++) {
-               if (channels.at(i) == ch) {
-                       index = i;
-                       break;
-               }
-       }
+       if (inBuf[frame] * inVol > peakIn)
+               peakIn = inBuf[frame] * inVol;
 
-       if (index == -1) {
-               gu_log("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index);
-               return 0;
-       }
+       /* "hear what you're playing" - process, copy and paste the input buffer
+        * onto the output buffer */
 
-       int lockStatus;
-       while (true) {
-               lockStatus = pthread_mutex_trylock(&mutex_chans);
-               if (lockStatus == 0) {
-                       channels.erase(channels.begin() + index);
-                       delete ch;
-                       pthread_mutex_unlock(&mutex_chans);
-                       return 1;
-               }
-               //else
-               //      gu_log("[mixer::deleteChannel] waiting for mutex...\n");
+       if (inToOut) {
+               vChanInToOut[frame]   = inBuf[frame]   * inVol;
+               vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
        }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* clearAllBuffers
+Cleans up every buffer, both in Mixer and in channels. */
 
-Channel *Mixer::getChannelByIndex(int index)
+void clearAllBuffers(float *outBuf, unsigned bufferSize)
 {
+       memset(outBuf, 0, sizeof(float) * bufferSize);         // out
+       memset(vChanInToOut, 0, sizeof(float) * bufferSize);   // inToOut vChan
+
+       pthread_mutex_lock(&mutex_chans);
        for (unsigned i=0; i<channels.size(); i++)
-               if (channels.at(i)->index == index)
-                       return channels.at(i);
-       gu_log("[mixer::getChannelByIndex] channel at index %d not found!\n", index);
-       return NULL;
+               channels.at(i)->clear();
+       pthread_mutex_unlock(&mutex_chans);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* readActions
+Reads all recorded actions. */
 
-void Mixer::sendMIDIsync()
+void readActions(unsigned frame)
 {
-       if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
-               if (currentFrame % (framesPerBeat/24) == 0)
-                       G_KernelMidi.send(MIDI_CLOCK, -1, -1);
-       }
-       else
-       if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
-
-               /* check if a new timecode frame has passed. If so, send MIDI TC
-                * quarter frames. 8 quarter frames, divided in two branches:
-                * 1-4 and 5-8. We check timecode frame's parity: if even, send
-                * range 1-4, if odd send 5-8. */
-
-               if (currentFrame % midiTCrate == 0) {
-
-                       /* frame low nibble
-                        * frame high nibble
-                        * seconds low nibble
-                        * seconds high nibble */
-
-                       if (midiTCframes % 2 == 0) {
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F)  | 0x00, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes >> 4)    | 0x10, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds >> 4)   | 0x30, -1);
-                       }
-
-                       /* minutes low nibble
-                        * minutes high nibble
-                        * hours low nibble
-                        * hours high nibble SMPTE frame rate */
-
-                       else {
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes >> 4)   | 0x50, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours & 0x0F)   | 0x60, -1);
-                               G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours >> 4)     | 0x70, -1);
-                       }
-
-                       midiTCframes++;
-
-                       /* check if total timecode frames are greater than timecode fps:
-                        * if so, a second has passed */
-
-                       if (midiTCframes > G_Conf.midiTCfps) {
-                               midiTCframes = 0;
-                               midiTCseconds++;
-                               if (midiTCseconds >= 60) {
-                                       midiTCminutes++;
-                                       midiTCseconds = 0;
-                                       if (midiTCminutes >= 60) {
-                                               midiTChours++;
-                                               midiTCminutes = 0;
-                                       }
-                               }
-                               //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+       pthread_mutex_lock(&mutex_recs);
+       for (unsigned i=0; i<recorder::frames.size(); i++) {
+               if (recorder::frames.at(i) == clock::getCurrentFrame()) {
+                       for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+                               int index   = recorder::global.at(i).at(j)->chan;
+                               Channel *ch = mh::getChannelByIndex(index);
+                               ch->parseAction(recorder::global.at(i).at(j), frame,
+          clock::getCurrentFrame(), clock::getQuantize(), clock::isRunning());
                        }
+                       break;
                }
        }
+       pthread_mutex_unlock(&mutex_recs);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* doQuantize
+Computes quantization on 'rewind' button and all channels. */
 
-void Mixer::sendMIDIrewind()
+void doQuantize(unsigned frame)
 {
-       midiTCframes  = 0;
-       midiTCseconds = 0;
-       midiTCminutes = 0;
-       midiTChours   = 0;
-
-       /* For cueing the slave to a particular start point, Quarter Frame
-        * messages are not used. Instead, an MTC Full Frame message should
-        * be sent. The Full Frame is a SysEx message that encodes the entire
-        * SMPTE time in one message */
-
-       if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
-               G_KernelMidi.send(MIDI_SYSEX, 0x7F, 0x00);  // send msg on channel 0
-               G_KernelMidi.send(0x01, 0x01, 0x00);        // hours 0
-               G_KernelMidi.send(0x00, 0x00, 0x00);        // mins, secs, frames 0
-               G_KernelMidi.send(MIDI_EOX, -1, -1);        // end of sysex
+  /* Nothing to do if quantizer disabled or a quanto has not passed yet. */
+
+  if (clock::getQuantize() == 0 || !clock::quantoHasPassed())
+    return;
+       if (rewindWait) {
+               rewindWait = false;
+               rewind();
        }
+       pthread_mutex_lock(&mutex_chans);
+       for (unsigned i=0; i<channels.size(); i++)
+               channels.at(i)->quantize(i, frame);
+       pthread_mutex_unlock(&mutex_chans);
 }
 
+
 /* -------------------------------------------------------------------------- */
 
+/* sumChannels
+Sums channels, i.e. lets them add sample frames to their virtual channels.
+This is required for CHANNEL_SAMPLE only */
 
-int Mixer::masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
-       double streamTime, RtAudioStreamStatus status, void *userData)
+void sumChannels(unsigned frame)
 {
-       return G_Mixer.__masterPlay(outBuf, inBuf, bufferSize);
+       pthread_mutex_lock(&mutex_chans);
+       for (unsigned k=0; k<channels.size(); k++) {
+               if (channels.at(k)->type == CHANNEL_SAMPLE)
+                       static_cast<SampleChannel*>(channels.at(k))->sum(frame, clock::isRunning());
+       }
+       pthread_mutex_unlock(&mutex_chans);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* renderMetronome
+Generates metronome when needed and pastes it to the output buffer. */
 
-int Mixer::__masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize)
+void renderMetronome(float *outBuf, unsigned frame)
 {
-       if (!ready)
-               return 0;
-
-       float *outBuf = (float *) _outBuf;
-       float *inBuf  = G_KernelAudio.inputEnabled ? (float *) _inBuf : nullptr;
-       bufferSize   *= 2;     // stereo
-       peakOut       = 0.0f;  // reset peak calculator
-       peakIn        = 0.0f;  // reset peak calculator
-
-       clearAllBuffers(outBuf, bufferSize);
-
-       for (unsigned j=0; j<bufferSize; j+=2) {
-               processLineIn(inBuf, j);
-               if (running) {
-                       lineInRec(inBuf, j);
-                       doQuantize(j);
-                       testBar(j);
-                       testFirstBeat(j);
-                       readActions(j);
-                       currentFrame += 2;
-                       testLastBeat();  // this test must be the last one
-                       sendMIDIsync();
+       if (tockPlay) {
+               outBuf[frame]   += tock[tockTracker];
+               outBuf[frame+1] += tock[tockTracker];
+               tockTracker++;
+               if (tockTracker >= TICKSIZE-1) {
+                       tockPlay    = false;
+                       tockTracker = 0;
                }
-               sumChannels(j);
        }
-
-       renderIO(outBuf, inBuf);
-
-       /* post processing */
-
-       for (unsigned j=0; j<bufferSize; j+=2) {
-               finalizeOutput(outBuf, j);
-               if (G_Conf.limitOutput)
-                       limitOutput(outBuf, j);
-               computePeak(outBuf, j);
-               renderMetronome(outBuf, j);
+       if (tickPlay) {
+               outBuf[frame]   += tick[tickTracker];
+               outBuf[frame+1] += tick[tickTracker];
+               tickTracker++;
+               if (tickTracker >= TICKSIZE-1) {
+                       tickPlay    = false;
+                       tickTracker = 0;
+               }
        }
-
-       return 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* renderIO
+Final processing stage. Take each channel and process it (i.e. copy its
+content to the output buffer). Process plugins too, if any. */
 
-void Mixer::updateFrameBars()
+void renderIO(float *outBuf, float *inBuf)
 {
-       /* 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
-        *                                                               it's stereo
-        * framesPerBar .. n. of frames within a bar
-        * framesPerBeat . n. of frames within a beat */
-
-       float seconds     = (60.0f / bpm) * beats;
-       totalFrames       = G_Conf.samplerate * seconds * 2;
-       framesPerBar      = totalFrames / bars;
-       framesPerBeat     = totalFrames / beats;
-       framesInSequencer = framesPerBeat * MAX_BEATS;
-
-       /* big troubles if frames are odd. */
-
-       if (totalFrames % 2 != 0)
-               totalFrames--;
-       if (framesPerBar % 2 != 0)
-               framesPerBar--;
-       if (framesPerBeat % 2 != 0)
-               framesPerBeat--;
-
-       updateQuanto();
-
-       /* realloc input virtual channel, if not NULL. TotalFrames is changed! */
+       pthread_mutex_lock(&mutex_chans);
+       for (unsigned k=0; k<channels.size(); k++)
+               channels.at(k)->process(outBuf, inBuf);
+       pthread_mutex_unlock(&mutex_chans);
 
-       if (vChanInput != NULL)
-               free(vChanInput);
-       vChanInput = (float*) malloc(totalFrames * sizeof(float));
-       if (!vChanInput)
-               gu_log("[Mixer] vChanInput realloc error!\n");
+#ifdef WITH_VST
+       pthread_mutex_lock(&mutex_plugins);
+       pluginHost::processStack(outBuf, pluginHost::MASTER_OUT);
+       pluginHost::processStack(vChanInToOut, pluginHost::MASTER_IN);
+       pthread_mutex_unlock(&mutex_plugins);
+#endif
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* limitOutput
+Applies a very dumb hard limiter. */
 
-int Mixer::close()
+void limitOutput(float *outBuf, unsigned frame)
 {
-       running = false;
-       while (channels.size() > 0)
-               deleteChannel(channels.at(0));
+       if (outBuf[frame] > 1.0f)
+               outBuf[frame] = 1.0f;
+       else
+       if (outBuf[frame] < -1.0f)
+               outBuf[frame] = -1.0f;
 
-       if (vChanInput) {
-               free(vChanInput);
-               vChanInput = NULL;
-       }
-       if (vChanInToOut) {
-               free(vChanInToOut);
-               vChanInToOut = NULL;
-       }
-       return 1;
+       if (outBuf[frame+1] > 1.0f)
+               outBuf[frame+1] = 1.0f;
+       else
+       if (outBuf[frame+1] < -1.0f)
+               outBuf[frame+1] = -1.0f;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* computePeak */
 
-bool Mixer::isSilent()
+void computePeak(float *outBuf, unsigned frame)
 {
-       for (unsigned i=0; i<channels.size(); i++)
-               if (channels.at(i)->status == STATUS_PLAY)
-                       return false;
-       return true;
+       /* TODO it takes into account only left channel so far! */
+       if (outBuf[frame] > peakOut)
+               peakOut = outBuf[frame];
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* finalizeOutput
+Last touches after the output has been rendered: apply inToOut if any, apply
+output volume. */
 
-void Mixer::rewind()
+void finalizeOutput(float *outBuf, unsigned frame)
 {
-       currentFrame = 0;
-       actualBeat  = 0;
-
-       if (running)
-               for (unsigned i=0; i<channels.size(); i++)
-                       channels.at(i)->rewind();
+       /* merge vChanInToOut, if enabled */
 
-       sendMIDIrewind();
+       if (inToOut) {
+               outBuf[frame]   += vChanInToOut[frame];
+               outBuf[frame+1] += vChanInToOut[frame+1];
+       }
+       outBuf[frame]   *= outVol;
+       outBuf[frame+1] *= outVol;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* test*
+Checks if the sequencer has reached a specific point (bar, first beat or
+last frame). */
 
-void Mixer::updateQuanto()
+void testBar(unsigned frame)
 {
-       /* big troubles if frames are odd. */
-
-       if (quantize != 0)
-               quanto = framesPerBeat / quantize;
-       if (quanto % 2 != 0)
-               quanto++;
-}
-
-
-/* -------------------------------------------------------------------------- */
+       if (!clock::isOnBar())
+               return;
 
+       if (metronome)
+               tickPlay = true;
 
-bool Mixer::hasLogicalSamples()
-{
-       for (unsigned i=0; i<channels.size(); i++)
-               if (channels.at(i)->type == CHANNEL_SAMPLE)
-                       if (((SampleChannel*)channels.at(i))->wave)
-                               if (((SampleChannel*)channels.at(i))->wave->isLogical)
-                                       return true;
-       return false;
+       pthread_mutex_lock(&mutex_chans);
+       for (unsigned k=0; k<channels.size(); k++)
+               channels.at(k)->onBar(frame);
+       pthread_mutex_unlock(&mutex_chans);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool Mixer::hasEditedSamples()
+void testFirstBeat(unsigned frame)
 {
-       for (unsigned i=0; i<channels.size(); i++)
-               if (channels.at(i)->type == CHANNEL_SAMPLE)
-                       if (((SampleChannel*)channels.at(i))->wave)
-                               if (((SampleChannel*)channels.at(i))->wave->isEdited)
-                                       return true;
-       return false;
+       if (!clock::isOnFirstBeat())
+               return;
+       pthread_mutex_lock(&mutex_chans);
+       for (unsigned k=0; k<channels.size(); k++)
+               channels.at(k)->onZero(frame, conf::recsStopOnChanHalt);
+       pthread_mutex_unlock(&mutex_chans);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::mergeVirtualInput()
+void testLastBeat()
 {
-       for (unsigned i=0; i<channels.size(); i++) {
-               if (channels.at(i)->type == CHANNEL_MIDI)
-                       continue;
-               SampleChannel *ch = (SampleChannel*) channels.at(i);
-               if (ch->armed)
-                       memcpy(ch->wave->data, vChanInput, totalFrames * sizeof(float));
-       }
-       memset(vChanInput, 0, totalFrames * sizeof(float)); // clear vchan
+  if (clock::isOnBeat())
+    if (metronome && !tickPlay)
+      tockPlay = true;
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::lineInRec(float *inBuf, unsigned frame)
-{
-       if (!mh_hasArmedSampleChannels() || !G_KernelAudio.inputEnabled || !recording)
-               return;
-
-       /* Delay comp: wait until waitRec reaches delayComp. WaitRec
-        * returns to 0 in mixerHandler, as soon as the recording ends */
-
-       if (waitRec < G_Conf.delayComp) {
-               waitRec += 2;
-               return;
-       }
-
-       vChanInput[inputTracker]   += inBuf[frame]   * inVol;
-       vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
-       inputTracker += 2;
-       if (inputTracker >= totalFrames)
-               inputTracker = 0;
-}
+}; // {anonymous}
 
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::processLineIn(float *inBuf, unsigned frame)
-{
-       if (!G_KernelAudio.inputEnabled)
-               return;
+std::vector<Channel*> channels;
 
-       /* input peak calculation (left chan only so far). */
+bool   recording    = false;   // is recording something?
+bool   ready        = true;
+float *vChanInput   = nullptr; // virtual channel for recording
+float *vChanInToOut = nullptr; // virtual channel in->out bridge (hear what you're playin)
+float  outVol       = G_DEFAULT_OUT_VOL;
+float  inVol        = G_DEFAULT_IN_VOL;
+float  peakOut      = 0.0f;
+float  peakIn       = 0.0f;
+bool    metronome    = false;
+int    waitRec      = 0;       // delayComp guard
+bool   docross      = false;    // crossfade guard
+bool   rewindWait   = false;    // rewind guard, if quantized
 
-       if (inBuf[frame] * inVol > peakIn)
-               peakIn = inBuf[frame] * inVol;
+int  tickTracker, tockTracker = 0;
+bool tickPlay, tockPlay = false; // 1 = play, 0 = stop
 
-       /* "hear what you're playing" - process, copy and paste the input buffer
-        * onto the output buffer */
-
-       if (inToOut) {
-               vChanInToOut[frame]   = inBuf[frame]   * inVol;
-               vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
-       }
-}
+/* inputTracker
+ * position of the sample in the input side (recording) */
 
+int inputTracker = 0;
 
-/* -------------------------------------------------------------------------- */
+/* inToOut
+ * copy, process and paste the input into the output, in order to
+ * obtain a "hear what you're playing" feature. */
 
+bool inToOut = false;
 
-void Mixer::readActions(unsigned frame)
-{
-       pthread_mutex_lock(&mutex_recs);
-       for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
-               if (G_Recorder.frames.at(i) == currentFrame) {
-                       for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-                               int index   = G_Recorder.global.at(i).at(j)->chan;
-                               Channel *ch = getChannelByIndex(index);
-                               ch->parseAction(G_Recorder.global.at(i).at(j), frame, currentFrame, quantize, running);
-                       }
-                       break;
-               }
-       }
-       pthread_mutex_unlock(&mutex_recs);
-}
+pthread_mutex_t mutex_recs;
+pthread_mutex_t mutex_chans;
+pthread_mutex_t mutex_plugins;
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::doQuantize(unsigned frame)
+void init(int framesInSeq, int audioBufferSize)
 {
-       if (quantize < 0 || quanto <= 0) // if quantizer disabled
-               return;
-       if (currentFrame % (quanto) != 0) // if a quanto has not passed yet
-               return;
-
-       if (rewindWait) {
-               rewindWait = false;
-               rewind();
-       }
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned i=0; i<channels.size(); i++)
-               channels.at(i)->quantize(i, frame, this);  // j == localFrame
-       pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
+       /* Allocate virtual input channels. vChanInput relies on clock::totalFrames:
+  it has variable size. */
 
+  if (vChanInput != nullptr)
+       free(vChanInput);
+  vChanInput = (float*) malloc(framesInSeq * sizeof(float));
+  if (vChanInToOut != nullptr)
+    free(vChanInToOut);
+       vChanInToOut = (float*) malloc(audioBufferSize * 2 * sizeof(float));
 
-void Mixer::testBar(unsigned frame)
-{
-       if (currentFrame % framesPerBar != 0 || currentFrame == 0)
-               return;
+       pthread_mutex_init(&mutex_recs, nullptr);
+       pthread_mutex_init(&mutex_chans, nullptr);
+       pthread_mutex_init(&mutex_plugins, nullptr);
 
-       if (metronome)
-               tickPlay = true;
-
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned k=0; k<channels.size(); k++)
-               channels.at(k)->onBar(frame);
-       pthread_mutex_unlock(&mutex_chans);
+       rewind();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::testFirstBeat(unsigned frame)
+int masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize,
+       double streamTime, RtAudioStreamStatus status, void *userData)
 {
-       if (currentFrame != 0)
-               return;
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned k=0; k<channels.size(); k++)
-               channels.at(k)->onZero(frame, G_Conf.recsStopOnChanHalt);
-       pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
+       if (!ready)
+               return 0;
 
-void Mixer::testLastBeat()
-{
-       /* if currentFrame > totalFrames the sequencer returns to frame 0,
-        * beat 0. This must be the last operation. */
+#ifdef __linux__
+  clock::recvJackSync();
+#endif
 
-       if (currentFrame > totalFrames) {
-               currentFrame = 0;
-               actualBeat  = 0;
-       }
-       else
-       if (currentFrame % framesPerBeat == 0 && currentFrame > 0) {
-               actualBeat++;
+       float *outBuf = (float*) _outBuf;
+       float *inBuf  = kernelAudio::isInputEnabled() ? (float*) _inBuf : nullptr;
+       bufferSize   *= 2;     // stereo
+       peakOut       = 0.0f;  // reset peak calculator
+       peakIn        = 0.0f;  // reset peak calculator
 
-               /* avoid tick and tock to overlap when a new bar has passed (which
-                * is also a beat) */
+       clearAllBuffers(outBuf, bufferSize);
 
-               if (metronome && !tickPlay)
-                       tockPlay = true;
+       for (unsigned j=0; j<bufferSize; j+=2) {
+               processLineIn(inBuf, j);
+               if (clock::isRunning()) {
+                       lineInRec(inBuf, j);
+                       doQuantize(j);
+                       testBar(j);
+                       testFirstBeat(j);
+                       readActions(j);
+                       clock::incrCurrentFrame();
+                       testLastBeat();  // this test must be the last one
+                       clock::sendMIDIsync();
+               }
+               sumChannels(j);
        }
-}
-
 
-/* -------------------------------------------------------------------------- */
+       renderIO(outBuf, inBuf);
 
+       /* post processing */
 
-void Mixer::sumChannels(unsigned frame)
-{
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned k=0; k<channels.size(); k++) {
-               if (channels.at(k)->type == CHANNEL_SAMPLE)
-                       ((SampleChannel*)channels.at(k))->sum(frame, running);
+       for (unsigned j=0; j<bufferSize; j+=2) {
+               finalizeOutput(outBuf, j);
+               if (conf::limitOutput)
+                       limitOutput(outBuf, j);
+               computePeak(outBuf, j);
+               renderMetronome(outBuf, j);
        }
-       pthread_mutex_unlock(&mutex_chans);
-}
-
 
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::renderMetronome(float *outBuf, unsigned frame)
-{
-       if (tockPlay) {
-               outBuf[frame]   += tock[tockTracker];
-               outBuf[frame+1] += tock[tockTracker];
-               tockTracker++;
-               if (tockTracker >= TICKSIZE-1) {
-                       tockPlay    = false;
-                       tockTracker = 0;
-               }
-       }
-       if (tickPlay) {
-               outBuf[frame]   += tick[tickTracker];
-               outBuf[frame+1] += tick[tickTracker];
-               tickTracker++;
-               if (tickTracker >= TICKSIZE-1) {
-                       tickPlay    = false;
-                       tickTracker = 0;
-               }
-       }
+       return 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::renderIO(float *outBuf, float *inBuf)
+int close()
 {
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned k=0; k<channels.size(); k++)
-               channels.at(k)->process(outBuf, inBuf);
-       pthread_mutex_unlock(&mutex_chans);
+       clock::stop();
+       while (channels.size() > 0)
+               mh::deleteChannel(channels.at(0));
 
-#ifdef WITH_VST
-       pthread_mutex_lock(&mutex_plugins);
-       G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT);
-       G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN);
-       pthread_mutex_unlock(&mutex_plugins);
-#endif
+       if (vChanInput) {
+               free(vChanInput);
+               vChanInput = nullptr;
+       }
+       if (vChanInToOut) {
+               free(vChanInToOut);
+               vChanInToOut = nullptr;
+       }
+       return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::computePeak(float *outBuf, unsigned frame)
+bool isSilent()
 {
-       /* TODO it takes into account only left channel so far! */
-       if (outBuf[frame] > peakOut)
-               peakOut = outBuf[frame];
+       for (unsigned i=0; i<channels.size(); i++)
+               if (channels.at(i)->status == STATUS_PLAY)
+                       return false;
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::limitOutput(float *outBuf, unsigned frame)
+void rewind()
 {
-       if (outBuf[frame] > 1.0f)
-               outBuf[frame] = 1.0f;
-       else
-       if (outBuf[frame] < -1.0f)
-               outBuf[frame] = -1.0f;
-
-       if (outBuf[frame+1] > 1.0f)
-               outBuf[frame+1] = 1.0f;
-       else
-       if (outBuf[frame+1] < -1.0f)
-               outBuf[frame+1] = -1.0f;
+  clock::rewind();
+       if (clock::isRunning())
+               for (unsigned i=0; i<channels.size(); i++)
+                       channels.at(i)->rewind();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Mixer::finalizeOutput(float *outBuf, unsigned frame)
+void mergeVirtualInput()
 {
-       /* merge vChanInToOut, if enabled */
-
-       if (inToOut) {
-               outBuf[frame]   += vChanInToOut[frame];
-               outBuf[frame+1] += vChanInToOut[frame+1];
+       for (unsigned i=0; i<channels.size(); i++) {
+               if (channels.at(i)->type == CHANNEL_MIDI)
+                       continue;
+               SampleChannel *ch = static_cast<SampleChannel*>(channels.at(i));
+               if (ch->armed)
+                       memcpy(ch->wave->data, vChanInput, clock::getTotalFrames() * sizeof(float));
        }
-       outBuf[frame]   *= outVol;
-       outBuf[frame+1] *= outVol;
+       memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::clearAllBuffers(float *outBuf, unsigned bufferSize)
-{
-       memset(outBuf, 0, sizeof(float) * bufferSize);         // out
-       memset(vChanInToOut, 0, sizeof(float) * bufferSize);   // inToOut vChan
-
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned i=0; i<channels.size(); i++)
-               channels.at(i)->clear();
-       pthread_mutex_unlock(&mutex_chans);
-}
+}}}; // giada::m::mixer::
index 66d08235603b906393df667aedca3b573330e51b..6433ecce72b684b326717b3e763e805bed2acb7d 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mixer
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef MIXER_H
-#define MIXER_H
+#ifndef G_MIXER_H
+#define G_MIXER_H
 
 
 #include <pthread.h>
 #include <vector>
-#include "kernelAudio.h"
-
-
-class Mixer
-{
-public:
-
-       Mixer();
-
-       void init();
-       int  close();
-
-       /* addChannel
-        * add a new channel without any wave inside of it. */
-
-       class Channel *addChannel(int type);
-
-       /* deleteChannel
-        * completely remove a channel from the stack. */
-
-       int deleteChannel(class Channel *ch);
-
-       /* masterPlay
-        * core method (callback) */
-
-       static int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
-               double streamTime, RtAudioStreamStatus status, void *userData);
-       int __masterPlay(void *outBuf, void *inBuf, unsigned bufferSize);
-
-       /* updateFrameBars
-        * updates bpm, frames, beats and so on. */
-
-       void updateFrameBars();
-
-       /* isSilent
-        * is mixer silent? */
-
-       bool isSilent();
-
-       /* rewind
-        * rewind sequencer to sample 0. */
-
-       void rewind();
-
-       /* updateQuanto
-        * recomputes the quanto between two quantizations */
-
-       void updateQuanto();
-
-       /* hasLogicalSamples
-        * true if 1 or more samples are logical (memory only, such as takes) */
-
-       bool hasLogicalSamples();
-
-       /* hasEditedSamples
-        * true if 1 or more samples was edited via gEditor */
-
-       bool hasEditedSamples();
-
-       /* mergeVirtualInput
-        * memcpy the virtual channel input in the channel designed for input
-        * recording. Called by mixerHandler on stopInputRec() */
-
-       void mergeVirtualInput();
-
-       /* getChannelByIndex
-        * return channel with given index 'i'. */
-
-       Channel *getChannelByIndex(int i);
-
-       /* getLastChannel
-        * Return last channel in the stack. */
+#include "../deps/rtaudio-mod/RtAudio.h"
 
-       inline Channel* getLastChannel() { return channels.back(); }
 
+class Channel;
 
-       /* ---------------------------------------------------------------- */
 
+namespace giada {
+namespace m {
+namespace mixer
+{
+void init(int framesInSeq, int audioBufferSize);
+int  close();
 
-       enum {    // const - what to do when a fadeout ends
-               DO_STOP   = 0x01,
-               DO_MUTE   = 0x02,
-               DO_MUTE_I = 0x04
-       };
-
-       enum {    // const - fade types
-               FADEOUT = 0x01,
-               XFADE   = 0x02
-       };
-
-       std::vector<class Channel*> channels;
-
-       bool   running;
-       bool   recording;         // is recording something?
-       bool   ready;
-       float *vChanInput;        // virtual channel for recording
-       float *vChanInToOut;      // virtual channel in->out bridge (hear what you're playin)
-       int    frameSize;
-       float  outVol;
-       float  inVol;
-       float  peakOut;
-       float  peakIn;
-       int    quanto;
-       char   quantize;
-       bool     metronome;
-       float  bpm;
-       int    bars;
-       int    beats;
-       int    waitRec;      // delayComp guard
-
-       bool docross;                      // crossfade guard
-       bool rewindWait;           // rewind guard, if quantized
-
-       int framesPerBar;      // frames in one bar
-       int framesPerBeat;     // frames in one beat
-       int framesInSequencer; // frames in the whole sequencer
-       int totalFrames;       // frames in the selected range (e.g. 4/4)
-       int currentFrame;
-       int actualBeat;
-
-#define TICKSIZE 38
-       static float tock[TICKSIZE];
-       static float tick[TICKSIZE];
-       int  tickTracker, tockTracker;
-       bool tickPlay, tockPlay; // 1 = play, 0 = stop
-
-       /* inputTracker
-        * position of the sample in the input side (recording) */
-
-       int inputTracker;
-
-       /* inToOut
-        * copy, process and paste the input into the output, in order to
-        * obtain a "hear what you're playing" feature. */
-
-       bool inToOut;
-
-       pthread_mutex_t mutex_recs;
-       pthread_mutex_t mutex_chans;
-       pthread_mutex_t mutex_plugins;
-
-private:
-
-       int midiTCstep;      // part of MTC to send (0 to 7)
-       int midiTCrate;      // send MTC data every midiTCrate frames
-       int midiTCframes;
-       int midiTCseconds;
-       int midiTCminutes;
-       int midiTChours;
-
-       /* getNewIndex
-        * compute new index value for new channels */
-
-       int getNewIndex();
-
-       /* sendMIDIsync
-        * generate MIDI sync output data */
-
-       void sendMIDIsync();
-
-       /* sendMIDIrewind
-        * rewind timecode to beat 0 and also send a MTC full frame to cue
-        * the slave */
-
-       void sendMIDIrewind();
-
-       /* lineInRec
-       Records from line in. */
-
-       void lineInRec(float *inBuf, unsigned frame);
-
-       /* ProcessLineIn
-       Computes line in peaks, plus handles "hear what you're playin'" thing. */
-
-       void processLineIn(float *inBuf, unsigned frame);
-
-       /* clearAllBuffers
-       Cleans up every buffer, both in Mixer and in channels. */
+/* masterPlay
+ * core method (callback) */
 
-       void clearAllBuffers(float *outBuf, unsigned bufferSize);
+int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, double streamTime,
+  RtAudioStreamStatus status, void *userData);
 
-       /* readActions
      Reads all recorded actions. */
+/* isSilent
* is mixer silent? */
 
-       void readActions(unsigned frame);
+bool isSilent();
 
-       /* doQuantize
      Computes quantization on 'rewind' button and all channels. */
+/* rewind
* rewind sequencer to sample 0. */
 
-       void doQuantize(unsigned frame);
+void rewind();
 
-       /* sumChannels
-       Sums channels, i.e. lets them add sample frames to their virtual channels.
      This is required for CHANNEL_SAMPLE only */
+/* mergeVirtualInput
+ * memcpy the virtual channel input in the channel designed for input
* recording. Called by mixerHandler on stopInputRec() */
 
-       void sumChannels(unsigned frame);
+void mergeVirtualInput();
 
-       /* renderMetronome
-       Generates metronome when needed and pastes it to the output buffer. */
+enum {    // const - what to do when a fadeout ends
+       DO_STOP   = 0x01,
+       DO_MUTE   = 0x02,
+       DO_MUTE_I = 0x04
+};
 
-       void renderMetronome(float *outBuf, unsigned frame);
+enum {    // const - fade types
+       FADEOUT = 0x01,
+       XFADE   = 0x02
+};
 
-       /* renderIO
-       Final processing stage. Take each channel and process it (i.e. copy its
-       content to the output buffer). Process plugins too, if any. */
+extern std::vector<Channel*> channels;
 
-       void renderIO(float *outBuf, float *inBuf);
+extern bool   recording;         // is recording something?
+extern bool   ready;
+extern float *vChanInput;        // virtual channel for recording
+extern float *vChanInToOut;      // virtual channel in->out bridge (hear what you're playin)
+extern int    frameSize;
+extern float  outVol;
+extern float  inVol;
+extern float  peakOut;
+extern float  peakIn;
+extern bool     metronome;
+extern int    waitRec;      // delayComp guard
+extern bool  docross;                   // crossfade guard
+extern bool  rewindWait;          // rewind guard, if quantized
 
-       /* limitOutput
-       Applies a very dumb hard limiter. */
+extern int  tickTracker, tockTracker;
+extern bool tickPlay, tockPlay; // 1 = play, 0 = stop
 
-       void limitOutput(float *outBuf, unsigned frame);
+/* inputTracker
+ * position of the sample in the input side (recording) */
 
-       /* computePeak */
+extern int inputTracker;
 
-       void computePeak(float *outBuf, unsigned frame);
+/* inToOut
+ * copy, process and paste the input into the output, in order to
+ * obtain a "hear what you're playing" feature. */
 
-       /* finalizeOutput
-       Last touches after the output has been rendered: apply inToOut if any, apply
-       output volume. */
+extern bool inToOut;
 
-       void finalizeOutput(float *outBuf, unsigned frame);
+extern pthread_mutex_t mutex_recs;
+extern pthread_mutex_t mutex_chans;
+extern pthread_mutex_t mutex_plugins;
 
-       /* test*
-       Checks if the sequencer has reached a specific point (bar, first beat or
-       last frame). */
+}}} // giada::m::mixer::;
 
-       void testBar(unsigned frame);
-       void testFirstBeat(unsigned frame);
-       void testLastBeat();
-};
 
 #endif
index 5bb2bd4dfd2ccc4c7b7cdccf2ed4408c75fb21b0..b099c1ac3676e454774db9879aae99d9f8f255ff 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mixerHandler
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#if defined(__linux__)
-       #include <jack/jack.h>
-       #include <jack/intclient.h>
-       #include <jack/transport.h>
-#endif
-
 #include <vector>
 #include "../utils/fs.h"
 #include "../utils/string.h"
 #include "plugin.h"
 #include "waveFx.h"
 #include "conf.h"
-#include "patch_DEPR_.h"
 #include "patch.h"
 #include "recorder.h"
+#include "clock.h"
 #include "channel.h"
+#include "kernelAudio.h"
+#include "midiMapConf.h"
 #include "sampleChannel.h"
+#include "midiChannel.h"
 #include "wave.h"
 
 
-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;
-#endif
-
-
 using std::vector;
+using std::string;
 
 
+namespace giada {
+namespace m {
+namespace mh
+{
+namespace
+{
 #ifdef WITH_VST
 
-static int __mh_readPatchPlugins__(vector<Patch::plugin_t> *list, int type)
+int readPatchPlugins(vector<patch::plugin_t> *list, int type)
 {
        int ret = 1;
        for (unsigned i=0; i<list->size(); i++) {
-               Patch::plugin_t *ppl = &list->at(i);
+               patch::plugin_t *ppl = &list->at(i);
     // TODO use glue_addPlugin()
-               Plugin *plugin = G_PluginHost.addPlugin(ppl->path.c_str(), type,
-                               &G_Mixer.mutex_plugins, NULL);
-               if (plugin != NULL) {
+               Plugin *plugin = pluginHost::addPlugin(ppl->path.c_str(), type,
+                               &mixer::mutex_plugins, nullptr);
+               if (plugin != nullptr) {
                        plugin->setBypass(ppl->bypass);
                        for (unsigned j=0; j<ppl->params.size(); j++)
                                plugin->setParameter(j, ppl->params.at(j));
@@ -94,27 +86,44 @@ static int __mh_readPatchPlugins__(vector<Patch::plugin_t> *list, int type)
 #endif
 
 
-/* -------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------ */
 
 
-void mh_stopSequencer()
+int getNewChanIndex()
 {
-       G_Mixer.running = false;
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++)
-               G_Mixer.channels.at(i)->stopBySeq(G_Conf.chansStopOnSeqHalt);
+       /* always skip last channel: it's the last one just added */
+
+       if (mixer::channels.size() == 1)
+               return 0;
+
+       int index = 0;
+       for (unsigned i=0; i<mixer::channels.size()-1; i++) {
+               if (mixer::channels.at(i)->index > index)
+                       index = mixer::channels.at(i)->index;
+               }
+       index += 1;
+       return index;
 }
 
 
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-bool mh_uniqueSolo(Channel *ch)
+bool uniqueSampleName(SampleChannel *ch, const string &name)
 {
-       int solos = 0;
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               Channel *ch = G_Mixer.channels.at(i);
-               if (ch->solo) solos++;
-               if (solos > 1) return false;
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               if (ch == mixer::channels.at(i))  // skip itself
+                       continue;
+               if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+                       continue;
+               SampleChannel *other = (SampleChannel*) mixer::channels.at(i);
+               if (other->wave != nullptr && name == other->wave->name)
+                       return false;
        }
        return true;
 }
@@ -123,125 +132,214 @@ bool mh_uniqueSolo(Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-/** TODO - revision needed: mh should not call glue_addChannel */
+Channel *addChannel(int type)
+{
+  Channel *ch;
+       int bufferSize = kernelAudio::getRealBufSize() * 2;
+
+       if (type == CHANNEL_SAMPLE)
+               ch = new SampleChannel(bufferSize, conf::inputMonitorDefaultOn);
+       else
+               ch = new MidiChannel(bufferSize);
+
+       while (true) {
+               if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+      continue;
+               mixer::channels.push_back(ch);
+               pthread_mutex_unlock(&mixer::mutex_chans);
+               break;
+       }
+
+       ch->index = getNewChanIndex();
+       gu_log("[addChannel] channel index=%d added, type=%d, total=%d\n",
+    ch->index, ch->type, mixer::channels.size());
+       return ch;
+}
 
-void mh_loadPatch_DEPR_(bool isProject, const char *projPath)
+
+/* -------------------------------------------------------------------------- */
+
+
+int deleteChannel(Channel *ch)
 {
-       G_Mixer.init();
-       G_Mixer.ready = false;   // put it in wait mode
-
-       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 projectPath = projPath;  // safe
-               string samplePath  = isProject ? projectPath + G_SLASH + G_Patch_DEPR_.getSamplePath(i) : "";
-               ch->readPatch_DEPR_(samplePath.c_str(), i, &G_Patch_DEPR_, G_Conf.samplerate,
-                               G_Conf.rsmpQuality);
+       int index = -1;
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               if (mixer::channels.at(i) == ch) {
+                       index = i;
+                       break;
+               }
+       }
+       if (index == -1) {
+               gu_log("[deleteChannel] unable to find channel %d for deletion!\n", ch->index);
+               return 0;
        }
 
-       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();
-
-       /* rewind and update frames in Mixer (it's vital) */
-
-       G_Mixer.rewind();
-       G_Mixer.updateFrameBars();
-       G_Mixer.ready = true;
+       while (true) {
+               if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+      continue;
+               mixer::channels.erase(mixer::channels.begin() + index);
+               delete ch;
+               pthread_mutex_unlock(&mixer::mutex_chans);
+               return 1;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel *getChannelByIndex(int index)
+{
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+               if (mixer::channels.at(i)->index == index)
+                       return mixer::channels.at(i);
+       gu_log("[getChannelByIndex] channel at index %d not found!\n", index);
+       return nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasLogicalSamples()
+{
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+    if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+      continue;
+    SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+    if (ch->wave && ch->wave->isLogical)
+      return true;
+  }
+       return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasEditedSamples()
+{
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+  {
+               if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+      continue;
+    SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+    if (ch->wave && ch->wave->isEdited)
+      return true;
+  }
+       return false;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void mh_readPatch()
+void stopSequencer()
 {
-       G_Mixer.ready = false;
+  clock::stop();
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+               mixer::channels.at(i)->stopBySeq(conf::chansStopOnSeqHalt);
+}
 
-       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;
+
+/* -------------------------------------------------------------------------- */
+
+
+bool uniqueSolo(Channel *ch)
+{
+       int solos = 0;
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               Channel *ch = mixer::channels.at(i);
+               if (ch->solo) solos++;
+               if (solos > 1) return false;
+       }
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPatch()
+{
+       mixer::ready = false;
+
+       mixer::outVol     = patch::masterVolOut;
+       mixer::inVol      = patch::masterVolIn;
+       clock::setBpm(patch::bpm);
+       clock::setBars(patch::bars);
+       clock::setBeats(patch::beats);
+       clock::setQuantize(patch::quantize);
+       mixer::metronome  = patch::metronome;
 
 #ifdef WITH_VST
 
-       __mh_readPatchPlugins__(&G_Patch.masterInPlugins, PluginHost::MASTER_IN);
-       __mh_readPatchPlugins__(&G_Patch.masterOutPlugins, PluginHost::MASTER_OUT);
+       readPatchPlugins(&patch::masterInPlugins, pluginHost::MASTER_IN);
+       readPatchPlugins(&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;
+       mixer::rewind();
+       clock::updateFrameBars();
+       mixer::ready = true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void mh_rewindSequencer()
+void rewindSequencer()
 {
-       if (G_Mixer.quantize > 0 && G_Mixer.running)   // quantize rewind
-               G_Mixer.rewindWait = true;
+       if (clock::getQuantize() > 0 && clock::isRunning())   // quantize rewind
+               mixer::rewindWait = true;
        else
-               G_Mixer.rewind();
+               mixer::rewind();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool mh_startInputRec()
+bool startInputRec()
 {
        int channelsReady = 0;
 
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
 
-               if (!G_Mixer.channels.at(i)->canInputRec())
+               if (!mixer::channels.at(i)->canInputRec())
                        continue;
 
-               SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+               SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
 
                /* Allocate empty sample for the current channel. */
 
-               if (!ch->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch.lastTakeId))
+               if (!ch->allocEmpty(clock::getTotalFrames(), conf::samplerate, patch::lastTakeId))
                {
-                       gu_log("[mh_startInputRec] unable to allocate new Wave in chan %d!\n",
+                       gu_log("[startInputRec] unable to allocate new Wave in chan %d!\n",
                                ch->index);
                        continue;
                }
 
                /* Increase lastTakeId until the sample name TAKE-[n] is unique */
 
-               while (!mh_uniqueSampleName(ch, ch->wave->name)) {
-                       G_Patch_DEPR_.lastTakeId++;
-                       G_Patch.lastTakeId++;
-                       ch->wave->name = "TAKE-" + gu_itoa(G_Patch.lastTakeId);
+               while (!uniqueSampleName(ch, ch->wave->name)) {
+                       patch::lastTakeId++;
+                       ch->wave->name = "TAKE-" + gu_itoa(patch::lastTakeId);
                }
 
-               gu_log("[mh_startInputRec] start input recs using chan %d with size %d "
-                       "frame=%d\n", ch->index, G_Mixer.totalFrames, G_Mixer.inputTracker);
+               gu_log("[startInputRec] start input recs using chan %d with size %d "
+                       "frame=%d\n", ch->index, clock::getTotalFrames(), mixer::inputTracker);
 
                channelsReady++;
        }
 
        if (channelsReady > 0) {
-               G_Mixer.recording = true;
+               mixer::recording = true;
                /* start to write from the currentFrame, not the beginning */
                /** FIXME: this should be done before wave allocation */
-               G_Mixer.inputTracker = G_Mixer.currentFrame;
+               mixer::inputTracker = clock::getCurrentFrame();
                return true;
        }
        return false;
@@ -251,11 +349,11 @@ bool mh_startInputRec()
 /* -------------------------------------------------------------------------- */
 
 
-void mh_stopInputRec()
+void stopInputRec()
 {
-       G_Mixer.mergeVirtualInput();
-       G_Mixer.recording = false;
-       G_Mixer.waitRec = 0; // in case delay compensation is in use
+       mixer::mergeVirtualInput();
+       mixer::recording = false;
+       mixer::waitRec = 0; // in case delay compensation is in use
        gu_log("[mh] stop input recs\n");
 }
 
@@ -263,10 +361,10 @@ void mh_stopInputRec()
 /* -------------------------------------------------------------------------- */
 
 
-bool mh_hasArmedSampleChannels()
+bool hasArmedSampleChannels()
 {
-  for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-    Channel *ch = G_Mixer.channels.at(i);
+  for (unsigned i=0; i<mixer::channels.size(); i++) {
+    Channel *ch = mixer::channels.at(i);
     if (ch->type == CHANNEL_SAMPLE && ch->armed)
       return true;
   }
@@ -274,19 +372,4 @@ bool mh_hasArmedSampleChannels()
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-bool mh_uniqueSampleName(SampleChannel *ch, const string &name)
-{
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               if (ch == G_Mixer.channels.at(i))  // skip itself
-                       continue;
-               if (G_Mixer.channels.at(i)->type != CHANNEL_SAMPLE)
-                       continue;
-               SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i);
-               if (other->wave != NULL && name == other->wave->name)
-                       return false;
-       }
-       return true;
-}
+}}}; // giada::m::mh::
index 9c33b1c43f1800becd63aa18df95196014579e92..362c346634a3fc06eec7c0063828b27f18188e5e 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mixerHandler
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
-#ifndef MIXERHANDLER_H
-#define MIXERHANDLER_H
+#ifndef G_MIXER_HANDLER_H
+#define G_MIXER_HANDLER_H
 
 
 #include <string>
 
 
+class Channel;
+class SampleChannel;
+
+
+namespace giada {
+namespace m {
+namespace mh
+{
+/* addChannel
+Adds a new channel of type 'type' into mixer's stack. */
+
+Channel *addChannel(int type);
+
+/* deleteChannel
+Completely removes a channel from the stack. */
+
+int deleteChannel(Channel *ch);
+
+/* getChannelByIndex
+Returns channel with given index 'i'. */
+
+Channel *getChannelByIndex(int i);
+
+/* hasLogicalSamples
+True if 1 or more samples are logical (memory only, such as takes) */
+
+bool hasLogicalSamples();
+
+/* hasEditedSamples
+True if 1 or more samples was edited via gEditor */
+
+bool hasEditedSamples();
+
 /* stopSequencer
  * stop the sequencer, with special case if samplesStopOnSeqHalt is
  * true. */
 
-void mh_stopSequencer();
+void stopSequencer();
 
-void mh_rewindSequencer();
+void rewindSequencer();
 
 /* uniqueSolo
  * true if ch is the only solo'd channel in mixer. */
 
-bool mh_uniqueSolo(class Channel *ch);
+bool uniqueSolo(Channel *ch);
 
 /* loadPatch
  * load a path or a project (if isProject) into Mixer. If isProject, path
  * must contain the address of the project folder. */
 
-void mh_loadPatch_DEPR_(bool isProject, const char *projPath=0);
-void mh_readPatch();
+void readPatch();
 
 /* startInputRec - record from line in
  * creates a new empty wave in the first available channels and returns
  * the chan number chosen, otherwise -1 if there are no more empty
  * channels available. */
 
-bool mh_startInputRec();
+bool startInputRec();
 
-void mh_stopInputRec();
+void stopInputRec();
 
 /* uniqueSamplename
  * return true if samplename 'n' is unique. Requires SampleChannel *ch
  * in order to skip check against itself. */
 
-bool mh_uniqueSampleName(class SampleChannel *ch, const std::string &s);
+bool uniqueSampleName(SampleChannel *ch, const std::string &s);
 
 /* hasArmedSampleChannels
 Tells whether Mixer has one or more sample channels armed for input
 recording. */
 
-bool mh_hasArmedSampleChannels();
+bool hasArmedSampleChannels();
+}}}  // giada::m::mh::
+
 
 #endif
index b622537b059b6e65d856e517e61801fce0d0ac0f..f9152e0a840e741d88d09fa9cc562521efe2dba8 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * patch
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../utils/log.h"
 #include "../utils/string.h"
 #include "const.h"
+#include "storager.h"
 #include "conf.h"
 #include "mixer.h"
 #include "patch.h"
 
 
-extern Mixer G_Mixer;
-extern Conf  G_Conf;
+using std::string;
+using std::vector;
 
 
-void Patch::init()
+namespace giada {
+namespace m {
+namespace patch
 {
-  columns.clear();
-  channels.clear();
-#ifdef WITH_VST
-  masterInPlugins.clear();
-  masterOutPlugins.clear();
-#endif
-  header     = "GIADAPTC";
-  lastTakeId = 0;
-  samplerate = DEFAULT_SAMPLERATE;
+namespace
+{
+/* sanitize
+Internal sanity check. */
+
+void sanitize()
+{
+  bpm          = bpm < G_MIN_BPM || bpm > G_MAX_BPM ? G_DEFAULT_BPM : bpm;
+  bars         = bars <= 0 || bars > G_MAX_BARS ? G_DEFAULT_BARS : bars;
+  beats        = beats <= 0 || beats > G_MAX_BEATS ? G_DEFAULT_BEATS : beats;
+  quantize     = quantize < 0 || quantize > G_MAX_QUANTIZE ? G_DEFAULT_QUANTIZE : quantize;
+  masterVolIn  = masterVolIn < 0.0f || masterVolIn > 1.0f ? G_DEFAULT_VOL : masterVolIn;
+  masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? G_DEFAULT_VOL : masterVolOut;
+  samplerate   = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate;
+
+  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 < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width;
+  }
+
+  for (unsigned i=0; i<channels.size(); i++) {
+    channel_t *ch = &channels.at(i);
+    ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume;
+    ch->pan    = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan;
+    ch->boost  = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost;
+    ch->pitch  = ch->pitch < 0.1f || ch->pitch > 4.0f ? G_DEFAULT_PITCH : ch->pitch;
+  }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+/* setInvalid
+Helper function used to return invalid status while reading. */
 
-int Patch::write(const string &file)
+int setInvalid(json_t *jRoot)
 {
-  jRoot = json_object();
+  json_decref(jRoot);
+  return PATCH_INVALID;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool readCommons(json_t *jContainer)
+{
+  if (!storager::setString(jContainer, PATCH_KEY_HEADER, header))  return 0;
+  if (!storager::setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
+  if (!storager::setString(jContainer, PATCH_KEY_NAME, name)) return 0;
+  if (!storager::setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_BARS, bars)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_BEATS, beats)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
+  if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
+  if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
+  if (!storager::setInt   (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
+  return 1;
+}
+
+
+
+/* -------------------------------------------------------------------------- */
 
-  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
 
-  if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
-    gu_log("[Patch::write] unable to write patch file!\n");
+bool readPlugins(json_t *jContainer, vector<plugin_t> *container, const char *key)
+{
+  json_t *jPlugins = json_object_get(jContainer, key);
+  if (!storager::checkArray(jPlugins, key))
     return 0;
+
+  size_t pluginIndex;
+  json_t *jPlugin;
+  json_array_foreach(jPlugins, pluginIndex, jPlugin) {
+
+    if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string
+      return 0;
+
+    plugin_t plugin;
+    if (!storager::setString(jPlugin, PATCH_KEY_PLUGIN_PATH,   plugin.path)) return 0;
+    if (!storager::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 (!storager::checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
+
+    size_t paramIndex;
+    json_t *jParam;
+    json_array_foreach(jParams, paramIndex, jParam)
+      plugin.params.push_back(json_real_value(jParam));
+
+    /* read midiIn params (midi learning on plugins' parameters) */
+
+    json_t *jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
+    if (!storager::checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
+
+    size_t midiInParamIndex;
+    json_t *jMidiInParam;
+    json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
+      plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
+
+    container->push_back(plugin);
   }
   return 1;
 }
 
+#endif
 
 /* -------------------------------------------------------------------------- */
 
 
-int Patch::read(const string &file)
+bool readActions(json_t *jContainer, channel_t *channel)
 {
-  jRoot = json_load_file(file.c_str(), 0, &jError);
-  if (!jRoot) {
-    gu_log("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
-    return PATCH_UNREADABLE;
+  json_t *jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
+  if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
+    return 0;
+
+  size_t actionIndex;
+  json_t *jAction;
+  json_array_foreach(jActions, actionIndex, jAction) {
+
+    if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string
+      return 0;
+
+    action_t action;
+    if (!storager::setInt   (jAction, PATCH_KEY_ACTION_TYPE,    action.type)) return 0;
+    if (!storager::setInt   (jAction, PATCH_KEY_ACTION_FRAME,   action.frame)) return 0;
+    if (!storager::setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
+    if (!storager::setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
+    channel->actions.push_back(action);
   }
+  return 1;
+}
 
-  if (!checkObject(jRoot, "root element"))
-    return PATCH_INVALID;
 
-  init();
+/* -------------------------------------------------------------------------- */
 
-  /* TODO json_decref also when PATCH_INVALID */
 
-  if (!readCommons(jRoot))  return setInvalid();
-  if (!readColumns(jRoot))  return setInvalid();
-  if (!readChannels(jRoot)) return setInvalid();
+bool readChannels(json_t *jContainer)
+{
+  json_t *jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
+  if (!storager::checkArray(jChannels, PATCH_KEY_CHANNELS))
+    return 0;
+
+  size_t channelIndex;
+  json_t *jChannel;
+  json_array_foreach(jChannels, channelIndex, jChannel) {
+
+    string channelIndexStr = "channel " + gu_itoa(channelIndex);
+    if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
+      return 0;
+
+    channel_t channel;
+
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_TYPE,                 channel.type)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_INDEX,                channel.index)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_COLUMN,               channel.column)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_MUTE,                 channel.mute)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_MUTE_S,               channel.mute_s)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_SOLO,                 channel.solo)) return 0;
+    if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME,               channel.volume)) return 0;
+    if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN,                  channel.pan)) return 0;
+    if (!storager::setBool  (jChannel, PATCH_KEY_CHANNEL_MIDI_IN,              channel.midiIn)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS,     channel.midiInKeyPress)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL,       channel.midiInKeyRel)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL,         channel.midiInKill)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM,          channel.midiInArm)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME,       channel.midiInVolume)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE,         channel.midiInMute)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO,         channel.midiInSolo)) return 0;
+    if (!storager::setBool  (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L,           channel.midiOutL)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING,   channel.midiOutLplaying)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE,      channel.midiOutLmute)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO,      channel.midiOutLsolo)) return 0;
+    if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH,          channel.samplePath)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_KEY,                  channel.key)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_MODE,                 channel.mode)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_BEGIN,                channel.begin)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_END,                  channel.end)) return 0;
+    if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST,                channel.boost)) return 0;
+    if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE,           channel.recActive)) return 0;
+    if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH,                channel.pitch)) return 0;
+    if (!storager::setBool  (jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR,        channel.inputMonitor)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH,        channel.midiInPitch)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT,             channel.midiOut)) return 0;
+    if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN,        channel.midiOutChan)) return 0;
+
+    readActions(jChannel, &channel);
+
 #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();
+    readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
 #endif
+    channels.push_back(channel);
+  }
+  return 1;
+}
 
-  json_decref(jRoot);
 
-  sanitize();
+/* -------------------------------------------------------------------------- */
 
-  return PATCH_READ_OK;
+
+bool readColumns(json_t *jContainer)
+{
+  json_t *jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
+  if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS))
+    return 0;
+
+  size_t columnIndex;
+  json_t *jColumn;
+  json_array_foreach(jColumns, columnIndex, jColumn) {
+
+    string columnIndexStr = "column " + gu_itoa(columnIndex);
+    if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
+      return 0;
+
+    column_t column;
+    if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
+    if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
+
+    columns.push_back(column);
+  }
+  return 1;
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 #ifdef WITH_VST
 
-void Patch::writePlugins(json_t *jContainer, vector<plugin_t> *plugins, const char *key)
+void writePlugins(json_t *jContainer, vector<plugin_t> *plugins, const char *key)
 {
   json_t *jPlugins = json_array();
   for (unsigned j=0; j<plugins->size(); j++) {
@@ -146,7 +313,7 @@ void Patch::writePlugins(json_t *jContainer, vector<plugin_t> *plugins, const ch
 /* -------------------------------------------------------------------------- */
 
 
-void Patch::writeColumns(json_t *jContainer, vector<column_t> *columns)
+void writeColumns(json_t *jContainer, vector<column_t> *columns)
 {
   json_t *jColumns = json_array();
   for (unsigned i=0; i<columns->size(); i++) {
@@ -163,7 +330,7 @@ void Patch::writeColumns(json_t *jContainer, vector<column_t> *columns)
 /* -------------------------------------------------------------------------- */
 
 
-void Patch::writeActions(json_t *jContainer, vector<action_t> *actions)
+void writeActions(json_t *jContainer, vector<action_t> *actions)
 {
   json_t *jActions = json_array();
   for (unsigned k=0; k<actions->size(); k++) {
@@ -182,7 +349,7 @@ void Patch::writeActions(json_t *jContainer, vector<action_t> *actions)
 /* -------------------------------------------------------------------------- */
 
 
-void Patch::writeCommons(json_t *jContainer)
+void writeCommons(json_t *jContainer)
 {
   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()));
@@ -205,7 +372,7 @@ void Patch::writeCommons(json_t *jContainer)
 /* -------------------------------------------------------------------------- */
 
 
-void Patch::writeChannels(json_t *jContainer, vector<channel_t> *channels)
+void writeChannels(json_t *jContainer, vector<channel_t> *channels)
 {
   json_t *jChannels = json_array();
   for (unsigned i=0; i<channels->size(); i++) {
@@ -218,8 +385,7 @@ void Patch::writeChannels(json_t *jContainer, vector<channel_t> *channels)
     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_PAN,                  json_real(channel.pan));
     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));
@@ -240,6 +406,7 @@ void Patch::writeChannels(json_t *jContainer, vector<channel_t> *channels)
     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_INPUT_MONITOR,        json_boolean(channel.inputMonitor));
     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));
@@ -257,144 +424,74 @@ void Patch::writeChannels(json_t *jContainer, vector<channel_t> *channels)
   json_object_set_new(jContainer, PATCH_KEY_CHANNELS, jChannels);
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Patch::readCommons(json_t *jContainer)
-{
-  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;
-}
+}; // {anonymous}
 
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-bool Patch::readColumns(json_t *jContainer)
-{
-  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 " + gu_itoa(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;
+std::string header;
+std::string version;
+int    versionMajor;
+int    versionMinor;
+int    versionPatch;
+std::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
+
+std::vector<column_t>  columns;
+std::vector<channel_t> channels;
 
-    columns.push_back(column);
-  }
-  return 1;
-}
+#ifdef WITH_VST
+std::vector<plugin_t> masterInPlugins;
+std::vector<plugin_t> masterOutPlugins;
+#endif
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool Patch::readChannels(json_t *jContainer)
+void init()
 {
-  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) {
-
-    string channelIndexStr = "channel " + gu_itoa(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_ARM,          channel.midiInArm)) 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);
-
+  columns.clear();
+  channels.clear();
 #ifdef WITH_VST
-    readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+  masterInPlugins.clear();
+  masterOutPlugins.clear();
 #endif
-    channels.push_back(channel);
-  }
-  return 1;
+  header     = "GIADAPTC";
+  lastTakeId = 0;
+  samplerate = G_DEFAULT_SAMPLERATE;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool Patch::readActions(json_t *jContainer, channel_t *channel)
+int write(const string &file)
 {
-  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) {
+  json_t *jRoot = json_object();
 
-    if (!checkObject(jAction, "")) // TODO pass actionIndex as string
-      return 0;
+  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
 
-    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.push_back(action);
+  if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
+    gu_log("[write] unable to write patch file!\n");
+    return 0;
   }
   return 1;
 }
@@ -403,88 +500,36 @@ bool Patch::readActions(json_t *jContainer, channel_t *channel)
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef WITH_VST
-
-bool Patch::readPlugins(json_t *jContainer, vector<plugin_t> *container, const char *key)
+int read(const string &file)
 {
-  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;
-
-    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.push_back(json_real_value(jParam));
-
-    /* read midiIn params (midi learning on plugins' parameters) */
-
-    json_t *jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
-    if (!checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
-
-    size_t midiInParamIndex;
-    json_t *jMidiInParam;
-    json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
-      plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
-      
-    container->push_back(plugin);
+  json_error_t jError;
+  json_t *jRoot = json_load_file(file.c_str(), 0, &jError);
+  if (!jRoot) {
+    gu_log("[read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
+    return PATCH_UNREADABLE;
   }
-  return 1;
-}
 
-#endif
+  if (!storager::checkObject(jRoot, "root element"))
+    return PATCH_INVALID;
 
+  init();
 
-/* -------------------------------------------------------------------------- */
+  /* TODO json_decref also when PATCH_INVALID */
 
+  if (!readCommons(jRoot))  return setInvalid(jRoot);
+  if (!readColumns(jRoot))  return setInvalid(jRoot);
+  if (!readChannels(jRoot)) return setInvalid(jRoot);
+#ifdef WITH_VST
+  if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS))   return setInvalid(jRoot);
+  if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid(jRoot);
+#endif
 
-void Patch::sanitize()
-{
-  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;
+  json_decref(jRoot);
 
-  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;
-  }
+  sanitize();
 
-  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 ? G_DEFAULT_PITCH : ch->pitch;
-  }
+  return PATCH_READ_OK;
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::setInvalid()
-{
-  json_decref(jRoot);
-  return PATCH_INVALID;
-}
+}}}; // giada::m::patch::
index ba17077fec825ca322992f94e09ddf1347f96e41..2dd689da3fefc94ce84b9aa8c41e402ca677ff03 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * patch
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __PATCH_H__
-#define __PATCH_H__
+#ifndef G_PATCH_H
+#define G_PATCH_H
 
 
 #include <string>
 #include <vector>
-#include <stdint.h>
-#include "dataStorageJson.h"
-
-
-using std::string;
-using std::vector;
+#ifdef __APPLE__  // our Clang still doesn't know about cstdint (c++11 stuff)
+       #include <stdint.h>
+#else
+       #include <cstdint>
+#endif
 
 
-class Patch : public DataStorageJson
+namespace giada {
+namespace m {
+namespace patch
 {
-public:
-
-  struct action_t
-  {
-    int      type;
-    int      frame;
-    float    fValue;
-    uint32_t iValue;
-  };
+struct action_t
+{
+  int      type;
+  int      frame;
+  float    fValue;
+  uint32_t iValue;
+};
 
 #ifdef WITH_VST
-  struct plugin_t
-  {
-    string           path;
-    bool             bypass;
-    vector<float>    params;
-    vector<uint32_t> midiInParams;
-  };
+struct plugin_t
+{
+  std::string           path;
+  bool                  bypass;
+  std::vector<float>    params;
+  std::vector<uint32_t> midiInParams;
+};
 #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    midiInArm;
-    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;
-
-    vector<action_t> actions;
-
-#ifdef WITH_VST
-    vector<plugin_t> plugins;
-#endif
-  };
-
-  struct column_t
-  {
-    int index;
-    int width;
-    vector<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
-
-  vector<column_t>  columns;
-  vector<channel_t> channels;
+struct channel_t
+{
+  int         type;
+  int         index;
+  int         column;
+  int         mute;
+  int         mute_s;
+  int         solo;
+  float       volume;
+  float       pan;
+  bool        midiIn;
+  uint32_t    midiInKeyPress;
+  uint32_t    midiInKeyRel;
+  uint32_t    midiInKill;
+  uint32_t    midiInArm;
+  uint32_t    midiInVolume;
+  uint32_t    midiInMute;
+  uint32_t    midiInSolo;
+  bool        midiOutL;
+  uint32_t    midiOutLplaying;
+  uint32_t    midiOutLmute;
+  uint32_t    midiOutLsolo;
+  // sample channel
+  std::string samplePath;
+  int         key;
+  int         mode;
+  int         begin;
+  int         end;
+  float       boost;
+  int         recActive;
+  float       pitch;
+  bool        inputMonitor;
+  uint32_t    midiInReadActions;
+  uint32_t    midiInPitch;
+  // midi channel
+  uint32_t    midiOut;
+  uint32_t    midiOutChan;
+
+  std::vector<action_t> actions;
 
 #ifdef WITH_VST
-  vector<plugin_t> masterInPlugins;
-  vector<plugin_t> masterOutPlugins;
+  std::vector<plugin_t> plugins;
 #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();
+struct column_t
+{
+  int index;
+  int width;
+  std::vector<int> channels;
+};
 
-  /* readers */
+extern std::string header;
+extern std::string version;
+extern int    versionMajor;
+extern int    versionMinor;
+extern int    versionPatch;
+extern std::string name;
+extern float  bpm;
+extern int    bars;
+extern int    beats;
+extern int    quantize;
+extern float  masterVolIn;
+extern float  masterVolOut;
+extern int    metronome;
+extern int    lastTakeId;
+extern int    samplerate;   // original samplerate when the patch was saved
+
+extern std::vector<column_t>  columns;
+extern std::vector<channel_t> channels;
 
-  bool readCommons (json_t *jContainer);
-  bool readChannels(json_t *jContainer);
 #ifdef WITH_VST
-  bool readPlugins (json_t *jContainer, vector<plugin_t> *container, const char* key);
+extern std::vector<plugin_t> masterInPlugins;
+extern std::vector<plugin_t> masterOutPlugins;
 #endif
-  bool readActions (json_t *jContainer, channel_t *channel);
-  bool readColumns (json_t *jContainer);
 
-  /* writers */
+/* init
+ * Init Patch with default values. */
 
-  void writeCommons (json_t *jContainer);
-  void writeChannels(json_t *jContainer, vector<channel_t> *channels);
-#ifdef WITH_VST
-  void writePlugins (json_t *jContainer, vector<plugin_t> *plugins, const char* key);
-#endif
-  void writeActions (json_t *jContainer, vector<action_t> *actions);
-  void writeColumns (json_t *jContainer, vector<column_t> *columns);
-};
+void init();
+
+/* read/write
+ * Read/write patch to/from file. */
+
+int write(const std::string &file);
+int read (const std::string &file);
+}}};  // giada::m::patch::
 
 #endif
diff --git a/src/core/patch_DEPR_.cpp b/src/core/patch_DEPR_.cpp
deleted file mode 100644 (file)
index 5ded87d..0000000
+++ /dev/null
@@ -1,619 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * patch
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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/fs.h"
-#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/elems/mainWindow/keyboard/keyboard.h"
-#include "patch_DEPR_.h"
-#include "init.h"
-#include "recorder.h"
-#include "conf.h"
-#include "pluginHost.h"
-#include "plugin.h"
-#include "wave.h"
-#include "mixer.h"
-#include "channel.h"
-
-
-extern Mixer             G_Mixer;
-extern Conf       G_Conf;
-extern Recorder   G_Recorder;
-#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());
-       gu_log("[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()
-{
-       gu_log("[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);
-
-//gu_log("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);
-
-//gu_log("  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)
-                                               G_Recorder.rec(ch->index, type, frame, iValue_fix, fValue);
-                                       else
-                                               G_Recorder.rec(ch->index, type, frame, iValue, fValue);
-          ch->hasActions = true;
-                               }
-               }
-       }
-       return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-int Patch_DEPR_::readPlugins()
-{
-       gu_log("[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, &G_Mixer.mutex_plugins, 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);
-                               if (pPlugin) {
-                                       pPlugin->setBypass(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->setParameter(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, &G_Mixer.mutex_plugins);
-               if (p != NULL) {
-                       Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
-                       sprintf(tmp, "master%c_p%dbypass", chr, i);
-                       pPlugin->setBypass(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->setParameter(j, pval);
-                       }
-                       res &= 1;
-               }
-               else
-                       res &= 0;
-       }
-
-       return res;
-}
-
-#endif
diff --git a/src/core/patch_DEPR_.h b/src/core/patch_DEPR_.h
deleted file mode 100644 (file)
index 0f01167..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * patch
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 1ef79b2afea5e5725f088d5558c8df4f437ee9e4..3616392b2aaa44139819be19a4c3871d2f7ed149 100644 (file)
@@ -4,7 +4,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -56,12 +56,12 @@ Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate,
 
   /* Try to enable editor (i.e. plugin's UI) */
   
-  if (plugin->getActiveEditor() != NULL) {
+  if (plugin->getActiveEditor() != nullptr) {
     gu_log("[Plugin] plugin has an already active editor!\n");
     return;
   }
   ui = plugin->createEditorIfNeeded();
-  if (ui == NULL) {
+  if (ui == nullptr) {
     gu_log("[Plugin] unable to create editor, the plugin might be GUI-less!\n");
     return;
   }
@@ -87,7 +87,7 @@ Plugin::~Plugin()
 
 void Plugin::showEditor(void *parent)
 {
-  if (ui == NULL) {
+  if (ui == nullptr) {
     gu_log("[Plugin::showEditor] can't show editor!\n");
     return;
   }
@@ -254,7 +254,7 @@ string Plugin::getParameterLabel(int index)
 
 void Plugin::closeEditor()
 {
-  if (ui == NULL)
+  if (ui == nullptr)
     return;
   if (ui->isOnDesktop())
        ui->removeFromDesktop();
index d295e507e1e7e181ea969f3b6f2954f558950a38..9a524738eba269ee7aaca854b6add66436a261ef 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -29,8 +29,8 @@
 
 #ifdef WITH_VST
 
-#ifndef __PLUGIN_H__
-#define __PLUGIN_H__
+#ifndef G_PLUGIN_H
+#define G_PLUGIN_H
 
 
 #include "../deps/juce-config.h"
index a23f2669ca90235f871b39b1a7e9a0eb2855f5f4..ae916f8842d7376f59d25f94c7d6718fe00f6f28 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * pluginHost
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include "../utils/log.h"
+#include "../utils/fs.h"
 #include "const.h"
 #include "channel.h"
 #include "plugin.h"
 #include "pluginHost.h"
 
 
+using std::vector;
 using std::string;
 
 
-PluginHost::~PluginHost()
+namespace giada {
+namespace m {
+namespace pluginHost
+{
+namespace
+{
+juce::MessageManager *messageManager;
+
+/* pluginFormat
+ * Plugin format manager. */
+
+juce::VSTPluginFormat pluginFormat;
+
+/* knownPuginList
+ * List of known (i.e. scanned) plugins. */
+
+juce::KnownPluginList knownPluginList;
+
+/* unknownPluginList
+ * List of unrecognized plugins found in a patch. */
+
+std::vector<std::string> unknownPluginList;
+
+std::vector<Plugin*> masterOut;
+std::vector<Plugin*> masterIn;
+
+/* Audio|MidiBuffer
+ * Dynamic buffers. */
+
+juce::AudioBuffer<float> audioBuffer;
+
+int samplerate;
+int buffersize;
+
+/* missingPlugins
+ * If some plugins from any stack are missing. */
+
+bool missingPlugins;
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+pthread_mutex_t mutex_midi;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void close()
 {
   messageManager->deleteInstance();
 }
@@ -49,9 +102,9 @@ PluginHost::~PluginHost()
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::init(int _buffersize, int _samplerate)
+void init(int _buffersize, int _samplerate)
 {
-  gu_log("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
+  gu_log("[pluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
     _buffersize, _samplerate);
 
   messageManager = juce::MessageManager::getInstance();
@@ -62,18 +115,18 @@ void PluginHost::init(int _buffersize, int _samplerate)
   //unknownPluginList.empty();
   loadList(gu_getHomePath() + G_SLASH + "plugins.xml");
 
-  pthread_mutex_init(&mutex_midi, NULL);
+  pthread_mutex_init(&mutex_midi, nullptr);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress, void *p),
+int scanDir(const string &dirpath, void (*callback)(float progress, void *p),
     void *p)
 {
-  gu_log("[PluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str());
-  gu_log("[PluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes());
+  gu_log("[pluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str());
+  gu_log("[pluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes());
 
   knownPluginList.clear();   // clear up previous plugins
 
@@ -85,13 +138,13 @@ int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress,
   bool cont = true;
   juce::String name;
   while (cont) {
-    gu_log("[PluginHost::scanDir]   scanning '%s'\n", name.toRawUTF8());
+    gu_log("[pluginHost::scanDir]   scanning '%s'\n", name.toRawUTF8());
     cont = scanner.scanNextFile(false, name);
     if (callback)
       callback(scanner.getProgress(), p);
   }
 
-  gu_log("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
+  gu_log("[pluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
   return knownPluginList.getNumTypes();
 }
 
@@ -99,11 +152,11 @@ int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress,
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::saveList(const string &filepath)
+int saveList(const string &filepath)
 {
   int out = knownPluginList.createXml()->writeToFile(juce::File(filepath), "");
   if (!out)
-    gu_log("[PluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str());
+    gu_log("[pluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str());
   return out;
 }
 
@@ -111,7 +164,7 @@ int PluginHost::saveList(const string &filepath)
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::loadList(const string &filepath)
+int loadList(const string &filepath)
 {
   juce::XmlElement *elem = juce::XmlDocument::parse(juce::File(filepath));
   if (elem) {
@@ -126,8 +179,8 @@ int PluginHost::loadList(const string &filepath)
 /* -------------------------------------------------------------------------- */
 
 
-Plugin *PluginHost::addPlugin(const string &fid, int stackType,
-  pthread_mutex_t *mutex, class Channel *ch)
+Plugin *addPlugin(const string &fid, int stackType,
+  pthread_mutex_t *mutex, Channel *ch)
 {
   /* Get the proper stack to add the plugin to */
 
@@ -138,19 +191,19 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType,
 
   juce::PluginDescription *pd = knownPluginList.getTypeForFile(fid);
   if (!pd) {
-    gu_log("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
+    gu_log("[pluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
     missingPlugins = true;
     unknownPluginList.push_back(fid);
-    return NULL;
+    return nullptr;
   }
 
   juce::AudioPluginInstance *pi = pluginFormat.createInstanceFromDescription(*pd, samplerate, buffersize);
   if (!pi) {
-    gu_log("[PluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str());
+    gu_log("[pluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str());
     missingPlugins = true;
-    return NULL;
+    return nullptr;
   }
-  gu_log("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
+  gu_log("[pluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
 
   Plugin *p = new Plugin(pi, samplerate, buffersize);
 
@@ -164,7 +217,7 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType,
     break;
   }
 
-  gu_log("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
+  gu_log("[pluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
     fid.c_str(), p->getName().c_str(), stackType, pStack->size());
 
   return p;
@@ -174,18 +227,18 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType,
 /* -------------------------------------------------------------------------- */
 
 
-Plugin *PluginHost::addPlugin(int index, int stackType, pthread_mutex_t *mutex,
-  class Channel *ch)
+Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex,
+  Channel *ch)
 {
   juce::PluginDescription *pd = knownPluginList.getType(index);
   if (pd) {
-    gu_log("[PluginHost::addPlugin] plugin found, uid=%s, name=%s...\n",
+    gu_log("[pluginHost::addPlugin] plugin found, uid=%s, name=%s...\n",
       pd->fileOrIdentifier.toStdString().c_str(), pd->name.toStdString().c_str());
     return addPlugin(pd->fileOrIdentifier.toStdString(), stackType, mutex, ch);
   }
   else {
-    gu_log("[PluginHost::addPlugin] no plugins found at index=%d!\n", index);
-    return NULL;
+    gu_log("[pluginHost::addPlugin] no plugins found at index=%d!\n", index);
+    return nullptr;
   }
 }
 
@@ -193,7 +246,7 @@ Plugin *PluginHost::addPlugin(int index, int stackType, pthread_mutex_t *mutex,
 /* -------------------------------------------------------------------------- */
 
 
-vector <Plugin *> *PluginHost::getStack(int stackType, Channel *ch)
+vector <Plugin *> *getStack(int stackType, Channel *ch)
 {
        switch(stackType) {
                case MASTER_OUT:
@@ -203,7 +256,7 @@ vector <Plugin *> *PluginHost::getStack(int stackType, Channel *ch)
                case CHANNEL:
                        return &ch->plugins;
                default:
-                       return NULL;
+                       return nullptr;
        }
 }
 
@@ -211,7 +264,7 @@ vector <Plugin *> *PluginHost::getStack(int stackType, Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-unsigned PluginHost::countPlugins(int stackType, Channel *ch)
+unsigned countPlugins(int stackType, Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
        return pStack->size();
@@ -221,7 +274,7 @@ unsigned PluginHost::countPlugins(int stackType, Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::countAvailablePlugins()
+int countAvailablePlugins()
 {
   return knownPluginList.getNumTypes();
 }
@@ -230,7 +283,7 @@ int PluginHost::countAvailablePlugins()
 /* -------------------------------------------------------------------------- */
 
 
-unsigned PluginHost::countUnknownPlugins()
+unsigned countUnknownPlugins()
 {
   return unknownPluginList.size();
 }
@@ -239,7 +292,7 @@ unsigned PluginHost::countUnknownPlugins()
 /* -------------------------------------------------------------------------- */
 
 
-PluginHost::PluginInfo PluginHost::getAvailablePluginInfo(int i)
+pluginHost::PluginInfo getAvailablePluginInfo(int i)
 {
   juce::PluginDescription *pd = knownPluginList.getType(i);
   PluginInfo pi;
@@ -251,8 +304,8 @@ PluginHost::PluginInfo PluginHost::getAvailablePluginInfo(int i)
   pi.isInstrument = pd->isInstrument;
 /*
   if (!p) {
-    gu_log("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n");
-    return NULL;
+    gu_log("[pluginHost::getAvailablePlugin] unable to create plugin instance!\n");
+    return nullptr;
   }
   */
   return pi;
@@ -262,7 +315,16 @@ PluginHost::PluginInfo PluginHost::getAvailablePluginInfo(int i)
 /* -------------------------------------------------------------------------- */
 
 
-string PluginHost::getUnknownPluginInfo(int i)
+bool hasMissingPlugins()
+{
+  return missingPlugins;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getUnknownPluginInfo(int i)
 {
   return unknownPluginList.at(i);
 }
@@ -271,7 +333,7 @@ string PluginHost::getUnknownPluginInfo(int i)
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch)
+void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch)
 {
        vector <Plugin *> *pStack;
        pStack = getStack(stackType, ch);
@@ -288,20 +350,20 @@ void PluginHost::freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch)
                pthread_mutex_unlock(mutex);
                break;
        }
-  gu_log("[PluginHost::freeStack] stack type=%d freed\n", stackType);
+  gu_log("[pluginHost::freeStack] stack type=%d freed\n", stackType);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::processStack(float *buffer, int stackType, Channel *ch)
+void processStack(float *buffer, int stackType, Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
 
        /* empty stack, stack not found or mixer not ready: do nothing */
 
-       if (pStack == NULL || pStack->size() == 0)
+       if (pStack == nullptr || pStack->size() == 0)
                return;
 
        /* converting buffer from Giada to Juce */
@@ -349,13 +411,13 @@ void PluginHost::processStack(float *buffer, int stackType, Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch)
+Plugin *getPluginByIndex(int index, int stackType, Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
        if (pStack->size() == 0)
-               return NULL;
+               return nullptr;
        if ((unsigned) index >= pStack->size())
-               return NULL;
+               return nullptr;
        return pStack->at(index);
 }
 
@@ -363,7 +425,7 @@ Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::getPluginIndex(int id, int stackType, Channel *ch)
+int getPluginIndex(int id, int stackType, Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
        for (unsigned i=0; i<pStack->size(); i++)
@@ -376,7 +438,7 @@ int PluginHost::getPluginIndex(int id, int stackType, Channel *ch)
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType,
+void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
   pthread_mutex_t *mutex, Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
@@ -394,7 +456,7 @@ void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType,
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::freePlugin(int id, int stackType, pthread_mutex_t *mutex,
+int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
   Channel *ch)
 {
        vector <Plugin *> *pStack = getStack(stackType, ch);
@@ -420,22 +482,22 @@ int PluginHost::freePlugin(int id, int stackType, pthread_mutex_t *mutex,
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::runDispatchLoop()
+void runDispatchLoop()
 {
   messageManager->runDispatchLoopUntil(10);
-  //gu_log("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
+  //gu_log("[pluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex)
+void freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex)
 {
-       freeStack(PluginHost::MASTER_OUT, mutex);
-       freeStack(PluginHost::MASTER_IN, mutex);
+       freeStack(pluginHost::MASTER_OUT, mutex);
+       freeStack(pluginHost::MASTER_IN, mutex);
        for (unsigned i=0; i<channels->size(); i++)
-               freeStack(PluginHost::CHANNEL, mutex, channels->at(i));
+               freeStack(pluginHost::CHANNEL, mutex, channels->at(i));
   missingPlugins = false;
   unknownPluginList.clear();
 }
@@ -444,12 +506,12 @@ void PluginHost::freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mut
 /* -------------------------------------------------------------------------- */
 
 
-int PluginHost::clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
+int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
   Channel *ch)
 {
        Plugin *p = addPlugin(src->getUniqueId(), stackType, mutex, ch);
        if (!p) {
-               gu_log("[PluginHost::clonePlugin] unable to add new plugin to stack!\n");
+               gu_log("[pluginHost::clonePlugin] unable to add new plugin to stack!\n");
                return 0;
        }
 
@@ -463,7 +525,7 @@ int PluginHost::clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
 /* -------------------------------------------------------------------------- */
 
 
-bool PluginHost::doesPluginExist(const string &fid)
+bool doesPluginExist(const string &fid)
 {
   return pluginFormat.doesPluginStillExist(*knownPluginList.getTypeForFile(fid));
 }
@@ -472,7 +534,7 @@ bool PluginHost::doesPluginExist(const string &fid)
 /* -------------------------------------------------------------------------- */
 
 
-void PluginHost::sortPlugins(int method)
+void sortPlugins(int method)
 {
   switch (method) {
     case sortMethod::NAME:
@@ -490,5 +552,7 @@ void PluginHost::sortPlugins(int method)
   }
 }
 
+}}}; // giada::m::pluginHost::
+
 
 #endif // #ifdef WITH_VST
index 2ddc7a582b547712642e6be81d611e16a74400f6..13ee170a6ef762bc5c8b4d5d0af33b96afa51be7 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * pluginHost
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  *
  * -------------------------------------------------------------------------- */
 
+
 #ifdef WITH_VST
 
-#ifndef __PLUGIN_HOST_H__
-#define __PLUGIN_HOST_H__
+#ifndef G_PLUGIN_HOST_H
+#define G_PLUGIN_HOST_H
 
 
 #include <pthread.h>
 #include "../deps/juce-config.h"
 
 
-using std::string;
-using std::vector;
+class Plugin;
+class Channel;
 
 
-class PluginHost
+namespace giada {
+namespace m {
+namespace pluginHost
 {
-private:
-
-  juce::MessageManager *messageManager;
-
-  /* pluginFormat
-   * Plugin format manager. */
-
-  juce::VSTPluginFormat pluginFormat;
-
-  /* knownPuginList
-   * List of known (i.e. scanned) plugins. */
-
-  juce::KnownPluginList knownPluginList;
-
-  /* unknownPluginList
-   * List of unrecognized plugins found in a patch. */
-
-  vector<string> unknownPluginList;
-
-  vector<class Plugin*> masterOut;
-  vector<class Plugin*> masterIn;
-
-  /* Audio|MidiBuffer
-   * Dynamic buffers. */
-
-  juce::AudioBuffer<float> audioBuffer;
-
-  int samplerate;
-  int buffersize;
-
-  /* missingPlugins
-   * If some plugins from any stack are missing. */
-
-  bool missingPlugins;
+enum stackType
+{
+       MASTER_OUT,
+       MASTER_IN,
+       CHANNEL
+};
 
-public:
+enum sortMethod
+{
+  NAME,
+  CATEGORY,
+  MANUFACTURER,
+  FORMAT
+};
 
-  enum stackType {
-               MASTER_OUT,
-               MASTER_IN,
-               CHANNEL
-       };
+struct PluginInfo
+{
+  std::string uid;
+  std::string name;
+  std::string category;
+  std::string manufacturerName;
+  std::string format;
+  bool isInstrument;
+};
 
-  enum sortMethod {
-    NAME,
-    CATEGORY,
-    MANUFACTURER,
-    FORMAT
-  };
+void init(int bufSize, int frequency);
+void close();
 
-  struct PluginInfo {
-    string uid;
-    string name;
-    string category;
-    string manufacturerName;
-    string format;
-    bool isInstrument;
-  };
+/* scanDir
+ * Parse plugin directory and store list in knownPluginList. The callback is
+ * called on each plugin found. Used to update the main window from the GUI
+ * thread. */
 
-  ~PluginHost();
+int scanDir(const std::string &path, void (*callback)(float progress, void *p)=nullptr,
+    void *p=nullptr);
 
-  void init(int bufSize, int frequency);
+/* (save|load)List
+ * (Save|Load) knownPluginList (in|from) an XML file. */
 
-  /* scanDir
-   * Parse plugin directory and store list in knownPluginList. The callback is
-   * called on each plugin found. Used to update the main window from the GUI
-   * thread. */
+int saveList(const std::string &path);
+int loadList(const std::string &path);
 
-  int scanDir(const string &path, void (*callback)(float progress, void *p)=NULL,
-      void *p=NULL);
+/* addPlugin
+ * Add a new plugin to 'stackType' by unique id or by index in knownPluginList
+ * std::vector. Requires:
+ * fid - plugin unique file id (i.e. path to dynamic library)
+ * stackType - which stack to add plugin to
+ * mutex - Mixer.mutex_plugin
+ * freq - current audio frequency
+ * bufSize - buffer size
+ * ch - if stackType == CHANNEL. */
 
-  /* (save|load)List
-   * (Save|Load) knownPluginList (in|from) an XML file. */
+Plugin *addPlugin(const std::string &fid, int stackType, pthread_mutex_t *mutex,
+  Channel *ch=nullptr);
+Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex,
+  Channel *ch=nullptr);
 
-  int saveList(const string &path);
-  int loadList(const string &path);
+/* countPlugins
+ * Return size of 'stackType'. */
 
-  /* addPlugin
-   * Add a new plugin to 'stackType' by unique id or by index in knownPluginList
-   * vector. Requires:
-   * fid - plugin unique file id (i.e. path to dynamic library)
-   * stackType - which stack to add plugin to
-   * mutex - Mixer.mutex_plugin
-   * freq - current audio frequency
-   * bufSize - buffer size
-   * ch - if stackType == CHANNEL. */
+unsigned countPlugins(int stackType, Channel *ch=nullptr);
 
-  Plugin *addPlugin(const string &fid, int stackType, pthread_mutex_t *mutex,
-    class Channel *ch=NULL);
-  Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex,
-    class Channel *ch=NULL);
+/* countAvailablePlugins
+ * Return size of knownPluginList. */
 
-  /* countPlugins
-   * Return size of 'stackType'. */
+int countAvailablePlugins();
 
-  unsigned countPlugins(int stackType, class Channel *ch=NULL);
+/* countUnknownPlugins
+ * Return size of unknownPluginList. */
 
-  /* countAvailablePlugins
-   * Return size of knownPluginList. */
+unsigned countUnknownPlugins();
 
-  int countAvailablePlugins();
+/* getAvailablePluginInfo
+ * Return the available plugin information (name, type, ...) from
+ * knownPluginList at index 'index'. */
 
-  /* countUnknownPlugins
-   * Return size of unknownPluginList. */
+PluginInfo getAvailablePluginInfo(int index);
 
-  unsigned countUnknownPlugins();
+std::string getUnknownPluginInfo(int index);
 
-  /* getAvailablePluginInfo
-   * Return the available plugin information (name, type, ...) from
-   * knownPluginList at index 'index'. */
+/* freeStack
+ * free plugin stack of type 'stackType'. */
 
-  PluginInfo getAvailablePluginInfo(int index);
+void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch=nullptr);
 
-  string getUnknownPluginInfo(int index);
+/* processStack
+ * apply the fx list to the buffer. */
 
-  /* freeStack
-   * free plugin stack of type 'stackType'. */
+void processStack(float *buffer, int stackType, Channel *ch=nullptr);
 
-  void freeStack(int stackType, pthread_mutex_t *mutex, class Channel *ch=NULL);
+/* getStack
+* Return a std::vector <Plugin *> given the stackType. If stackType == CHANNEL
+* a pointer to Channel is also required. */
 
-  /* processStack
-   * apply the fx list to the buffer. */
+std::vector <Plugin *> *getStack(int stackType, Channel *ch=nullptr);
 
-  void processStack(float *buffer, int stackType, class Channel *ch=NULL);
+/* getPluginByIndex */
 
-  /* getStack
-  * Return a vector <Plugin *> given the stackType. If stackType == CHANNEL
-  * a pointer to Channel is also required. */
+Plugin *getPluginByIndex(int index, int stackType, Channel *ch=nullptr);
 
-  vector <Plugin *> *getStack(int stackType, class Channel *ch=NULL);
+/* getPluginIndex */
 
-  /* getPluginByIndex */
+int getPluginIndex(int id, int stackType, Channel *ch=nullptr);
 
-  Plugin *getPluginByIndex(int index, int stackType, class Channel *ch=NULL);
+/* swapPlugin */
 
-  /* getPluginIndex */
+void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
+  pthread_mutex_t *mutex, Channel *ch=nullptr);
 
-  int getPluginIndex(int id, int stackType, class Channel *ch=NULL);
+/* freePlugin.
+Returns the internal stack index of the deleted plugin. */
 
-  /* swapPlugin */
+int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
+  Channel *ch=nullptr);
 
-  void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
-    pthread_mutex_t *mutex, class Channel *ch=NULL);
+/* runDispatchLoop
+ * Wakes up plugins' GUI manager for N milliseconds. */
 
-  /* freePlugin.
-  Returns the internal stack index of the deleted plugin. */
+void runDispatchLoop();
 
-  int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
-    class Channel *ch=NULL);
+/* freeAllStacks
+ * Frees everything. */
 
-  /* runDispatchLoop
-   * Wakes up plugins' GUI manager for N milliseconds. */
+void freeAllStacks(std::vector <Channel*> *channels, pthread_mutex_t *mutex);
 
-  void runDispatchLoop();
+/* clonePlugin */
 
-  /* freeAllStacks
-   * Frees everything. */
+int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
+  Channel *ch);
 
-  void freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex);
+/* doesPluginExist */
 
-  /* clonePlugin */
+bool doesPluginExist(const std::string &fid);
 
-  int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
-    class Channel *ch);
+bool hasMissingPlugins();
 
-  /* doesPluginExist */
+void sortPlugins(int sortMethod);
 
-  bool doesPluginExist(const string &fid);
+extern pthread_mutex_t mutex_midi;
 
-  bool hasMissingPlugins() { return missingPlugins; };
+}}}; // giada::m::pluginHost::
 
-  void sortPlugins(int sortMethod);
 
-  pthread_mutex_t mutex_midi;
-};
 #endif
 
 #endif // #ifdef WITH_VST
index 992a98e265d8c81a8b511d4fb932f5c9342ce87b..c760ae143332360524c7a96f365ffd5a223241f1 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * recorder
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <math.h>
+#include <cassert>
+#include <cmath>
 #include "../utils/log.h"
-#include "mixer.h"
-#include "patch_DEPR_.h"
+#include "const.h"
 #include "sampleChannel.h"
 #include "recorder.h"
 
 
-Recorder::Recorder()
-       : active       (false),
-         sortedActions(false)
+using std::vector;
+
+
+namespace giada {
+namespace m {
+namespace recorder
+{
+namespace
+{
+/* composite
+A group of two actions (keypress+keyrel, muteon+muteoff) used during the overdub
+process. */
+
+struct composite
+{
+       action a1;
+       action a2;
+} cmp;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* fixOverdubTruncation
+Fixes underlying action truncation when overdubbing over a longer action. I.e.:
+  Original:    |#############|
+  Overdub:     ---|#######|---
+  fix:         |#||#######|--- */
+
+void fixOverdubTruncation(const composite &comp, pthread_mutex_t *mixerMutex)
 {
+  action *next = nullptr;
+  int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame,
+    &next);
+  if (res != 1 || next->type != comp.a2.type)
+    return;
+  gu_log("[recorder::fixOverdubTruncation] add truncation at frame %d, type=%d\n",
+    next->frame, next->type);
+  deleteAction(next->chan, next->frame, next->type, false, mixerMutex);
 }
 
+}; // {anonymous}
+
+
 /* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+vector<int> frames;
+vector<vector<action*>> global;
+vector<action*> actions;     // used internally
+
+bool active = false;
+bool sortedActions = false;
 
 
-void Recorder::init()
+/* -------------------------------------------------------------------------- */
+
+
+void init()
 {
-       sortedActions = false;
        active = false;
+  sortedActions = false;
        clearAll();
 }
 
@@ -55,7 +104,7 @@ void Recorder::init()
 /* -------------------------------------------------------------------------- */
 
 
-bool Recorder::canRec(Channel *ch, Mixer *mixer)
+bool canRec(Channel *ch, bool clockRunning, bool mixerRecording)
 {
        /* NO recording if:
         * recorder is inactive
@@ -63,10 +112,10 @@ bool Recorder::canRec(Channel *ch, Mixer *mixer)
         * mixer is recording a take somewhere
         * channel is empty */
 
-       if (!active           ||
-                 !mixer->running   ||
-                        mixer->recording ||
-                       (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)
+       if (!active         ||
+                 !clockRunning   ||
+                        mixerRecording ||
+                       (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == nullptr)
                )
                return false;
        return true;
@@ -76,7 +125,7 @@ bool Recorder::canRec(Channel *ch, Mixer *mixer)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue)
+void rec(int index, int type, int frame, uint32_t iValue, float fValue)
 {
        /* make sure frame is even */
 
@@ -133,7 +182,7 @@ void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue
 
        sortedActions = false;
 
-       gu_log("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n",
+       gu_log("[recorder::rec] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n",
                a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue);
        //print();
 }
@@ -142,9 +191,9 @@ void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::clearChan(int index)
+void clearChan(int index)
 {
-       gu_log("[REC] clearing chan %d...\n", index);
+       gu_log("[recorder::clearChan] clearing chan %d...\n", index);
 
        for (unsigned i=0; i<global.size(); i++) {      // for each frame i
                unsigned j=0;
@@ -167,9 +216,9 @@ void Recorder::clearChan(int index)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::clearAction(int index, char act)
+void clearAction(int index, char act)
 {
-       gu_log("[REC] clearing action %d from chan %d...\n", act, index);
+       gu_log("[recorder::clearAction] clearing action %d from chan %d...\n", act, index);
        for (unsigned i=0; i<global.size(); i++) {                                              // for each frame i
                unsigned j=0;
                while (true) {                                   // for each action j of frame i
@@ -192,7 +241,7 @@ void Recorder::clearAction(int index, char act)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::deleteAction(int chan, int frame, char type, bool checkValues,
+void deleteAction(int chan, int frame, char type, bool checkValues,
        pthread_mutex_t *mixerMutex, uint32_t iValue, float fValue)
 {
        /* make sure frame is even */
@@ -231,17 +280,17 @@ void Recorder::deleteAction(int chan, int frame, char type, bool checkValues,
           break;
         }
         else
-          gu_log("[REC] delete action: waiting for mutex...\n");
+          gu_log("[recorder::deleteAction] waiting for mutex...\n");
       }
                }
        }
        if (found) {
                optimize();
-               gu_log("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+               gu_log("[recorder::deleteAction] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
                        type, frame, chan, iValue, iValue, fValue);
        }
        else
-               gu_log("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+               gu_log("[recorder::deleteAction] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
                        type, frame, chan, iValue, iValue, fValue);
 }
 
@@ -249,7 +298,7 @@ void Recorder::deleteAction(int chan, int frame, char type, bool checkValues,
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::deleteActions(int chan, int frame_a, int frame_b, char type,
+void deleteActions(int chan, int frame_a, int frame_b, char type,
   pthread_mutex_t *mixerMutex)
 {
        sortActions();
@@ -267,7 +316,7 @@ void Recorder::deleteActions(int chan, int frame_a, int frame_b, char type,
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::clearAll()
+void clearAll()
 {
        while (global.size() > 0) {
                for (unsigned i=0; i<global.size(); i++) {
@@ -285,7 +334,7 @@ void Recorder::clearAll()
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::optimize()
+void optimize()
 {
        /* do something until the i frame is empty. */
 
@@ -307,7 +356,7 @@ void Recorder::optimize()
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::sortActions()
+void sortActions()
 {
        if (sortedActions)
                return;
@@ -325,7 +374,7 @@ void Recorder::sortActions()
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::updateBpm(float oldval, float newval, int oldquanto)
+void updateBpm(float oldval, float newval, int oldquanto)
 {
        for (unsigned i=0; i<frames.size(); i++) {
 
@@ -336,7 +385,7 @@ void Recorder::updateBpm(float oldval, float newval, int oldquanto)
                 * and the quantizer set to 44100. That would mean two recs completely
                 * useless. So we compute a reject value ('scarto'): if it's lower
                 * than 6 frames the new frame is collapsed with a quantized frame. */
-               /** CHECKME - maybe 6 frames are too low */
+               /** XXX - maybe 6 frames are too low */
 
                if (frames.at(i) != 0) {
                        int scarto = oldquanto % frames.at(i);
@@ -366,7 +415,7 @@ void Recorder::updateBpm(float oldval, float newval, int oldquanto)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::updateSamplerate(int systemRate, int patchRate)
+void updateSamplerate(int systemRate, int patchRate)
 {
        /* diff ratio: systemRate / patchRate
         * e.g.  44100 / 96000 = 0.4... */
@@ -374,12 +423,12 @@ void Recorder::updateSamplerate(int systemRate, int patchRate)
        if (systemRate == patchRate)
                return;
 
-       gu_log("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
+       gu_log("[recorder::updateSamplerate] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
 
        float ratio = systemRate / (float) patchRate;
        for (unsigned i=0; i<frames.size(); i++) {
 
-               gu_log("[REC]    oldFrame = %d", frames.at(i));
+               gu_log("[recorder::updateSamplerate]    oldFrame = %d", frames.at(i));
 
                float newFrame = frames.at(i);
                newFrame = floorf(newFrame * ratio);
@@ -406,7 +455,7 @@ void Recorder::updateSamplerate(int systemRate, int patchRate)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::expand(int old_fpb, int new_fpb)
+void expand(int old_fpb, int new_fpb)
 {
        /* this algorithm requires multiple passages if we expand from e.g. 2
         * to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group,
@@ -429,7 +478,7 @@ void Recorder::expand(int old_fpb, int new_fpb)
                        }
                }
        }
-       gu_log("[REC] expanded recs\n");
+       gu_log("[recorder::expand] expanded recs\n");
        //print();
 }
 
@@ -437,7 +486,7 @@ void Recorder::expand(int old_fpb, int new_fpb)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::shrink(int new_fpb)
+void shrink(int new_fpb)
 {
        /* easier than expand(): here we delete eveything beyond old_framesPerBars. */
 
@@ -456,7 +505,7 @@ void Recorder::shrink(int new_fpb)
                        i++;
        }
        optimize();
-       gu_log("[REC] shrinked recs\n");
+       gu_log("[recorder::shrink] shrinked recs\n");
        //print();
 }
 
@@ -464,7 +513,7 @@ void Recorder::shrink(int new_fpb)
 /* -------------------------------------------------------------------------- */
 
 
-bool Recorder::hasActions(int chanIndex)
+bool hasActions(int chanIndex)
 {
   if (global.size() == 0)
     return false;
@@ -481,7 +530,7 @@ bool Recorder::hasActions(int chanIndex)
 /* -------------------------------------------------------------------------- */
 
 
-int Recorder::getNextAction(int chan, char type, int frame, action **out,
+int getNextAction(int chan, char type, int frame, action **out,
        uint32_t iValue)
 {
        sortActions();  // mandatory
@@ -511,7 +560,7 @@ int Recorder::getNextAction(int chan, char type, int frame, action **out,
 /* -------------------------------------------------------------------------- */
 
 
-int Recorder::getAction(int chan, char action, int frame, struct action **out)
+int getAction(int chan, char action, int frame, struct action **out)
 {
        for (unsigned i=0; i<global.size(); i++)
                for (unsigned j=0; j<global.at(i).size(); j++)
@@ -529,18 +578,17 @@ int Recorder::getAction(int chan, char action, int frame, struct action **out)
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::startOverdub(int index, char actionMask, int frame,
-  unsigned bufferSize)
+void startOverdub(int index, char actionMask, int frame, unsigned bufferSize)
 {
        /* prepare the composite struct */
 
-       if (actionMask == ACTION_KEYS) {
-               cmp.a1.type = ACTION_KEYPRESS;
-               cmp.a2.type = ACTION_KEYREL;
+       if (actionMask == G_ACTION_KEYS) {
+               cmp.a1.type = G_ACTION_KEYPRESS;
+               cmp.a2.type = G_ACTION_KEYREL;
        }
        else {
-               cmp.a1.type = ACTION_MUTEON;
-               cmp.a2.type = ACTION_MUTEOFF;
+               cmp.a1.type = G_ACTION_MUTEON;
+               cmp.a2.type = G_ACTION_MUTEOFF;
        }
        cmp.a1.chan  = index;
        cmp.a2.chan  = index;
@@ -552,14 +600,14 @@ void Recorder::startOverdub(int index, char actionMask, int frame,
 
        rec(index, cmp.a1.type, frame);
 
-       action *act = NULL;
+       action *act = nullptr;
        int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act);
        if (res == 1) {
                if (act->type == cmp.a2.type) {
                        int truncFrame = cmp.a1.frame - bufferSize;
                        if (truncFrame < 0)
                                truncFrame = 0;
-                       gu_log("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
+                       gu_log("[recorder::startOverdub] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
                        rec(index, cmp.a2.type, truncFrame);
                }
        }
@@ -569,61 +617,52 @@ void Recorder::startOverdub(int index, char actionMask, int frame,
 /* -------------------------------------------------------------------------- */
 
 
-void Recorder::stopOverdub(Mixer *mixer)
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex)
 {
-       cmp.a2.frame  = mixer->currentFrame;
+       cmp.a2.frame  = currentFrame;
        bool ringLoop = false;
        bool nullLoop = false;
 
-       /* ring loop verification, i.e. a composite action with key_press at
-        * frame N and key_release at frame M, with M <= N */
-
-       if (cmp.a2.frame < cmp.a1.frame) {
+       /* Check for ring loops or null loops. Ring loop: a composite action with
+  key_press at frame N and key_release at frame M, with M <= N.
+  Null loop: a composite action that begins and ends on the very same frame,
+  i.e. with 0 size. Very unlikely.
+  If ring loop: record the last action at the end of the sequencer (that
+  is 'totalFrames').
+  If null loop: remove previous action and do nothing. Also make sure to avoid
+  underlying action truncation, if the null loop occurs inside a composite
+  action. */
+
+       if (cmp.a2.frame < cmp.a1.frame) {  // ring loop
                ringLoop = true;
-               gu_log("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
-               rec(cmp.a2.chan, cmp.a2.type, mixer->totalFrames);      // record at the end of the sequencer
+               gu_log("[recorder::stopOverdub] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+               rec(cmp.a2.chan, cmp.a2.type, totalFrames);
        }
        else
-       if (cmp.a2.frame == cmp.a1.frame) {
+       if (cmp.a2.frame == cmp.a1.frame) { // null loop
                nullLoop = true;
-               gu_log("[REC]  null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
-               deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, &mixer->mutex_recs); // false == don't check values
-       }
+               gu_log("[recorder::stopOverdub] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+               deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, mixerMutex); // false == don't check values
+    fixOverdubTruncation(cmp, mixerMutex);
+  }
 
-       /* remove any nested action between keypress----keyrel, then record */
+  if (nullLoop)
+    return;
 
-       if (!nullLoop) {
-               deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, &mixer->mutex_recs);
-               deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, &mixer->mutex_recs);
-       }
+       /* Remove any nested action between keypress----keyrel. */
 
-       if (!ringLoop && !nullLoop) {
-               rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
+       deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, mixerMutex);
+       deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, mixerMutex);
 
-               /* avoid underlying action truncation, if keyrel happens inside a
-               * composite action */
+  if (ringLoop)
+    return;
 
-               action *act = NULL;
-               int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act);
-               if (res == 1 && act->type == cmp.a2.type) {
-                       gu_log("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type);
-                       deleteAction(act->chan, act->frame, act->type, false, &mixer->mutex_recs); // false == don't check values
-               }
-       }
-}
+  /* Record second part of the composite action. Also make sure to avoid
+  underlying action truncation, if keyrel happens inside a composite action. */
 
-
-/* -------------------------------------------------------------------------- */
+       rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
+  fixOverdubTruncation(cmp, mixerMutex);
+}
 
 
-void Recorder::print()
-{
-       gu_log("[REC] ** print debug **\n");
-       for (unsigned i=0; i<global.size(); i++) {
-               gu_log("  frame %d\n", frames.at(i));
-               for (unsigned j=0; j<global.at(i).size(); j++) {
-                       gu_log("    action %d | chan %d | frame %d\n", global.at(i).at(j)->type,
-        global.at(i).at(j)->chan, global.at(i).at(j)->frame);
-               }
-       }
-}
+}}}; // giada::m::recorder::
index a4f021067f7a7b89f0b11761a7903568c850d618..eecf52f9189fd18b35d6a4edebd10b65f77e2681 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * recorder
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef RECORDER_H
-#define RECORDER_H
+#ifndef G_RECORDER_H
+#define G_RECORDER_H
 
 
-#ifdef __APPLE__  // our compiler still doesn't know about cstdint (c++11 stuff)
+#ifdef __APPLE__  // our Clang still doesn't know about cstdint (c++11 stuff)
        #include <stdint.h>
 #else
        #include <cstdint>
 #include <pthread.h>
 
 
-using std::vector;
+class Channel;
 
 
-class Recorder
+namespace giada {
+namespace m {
+namespace recorder
 {
-public:
-
-       Recorder();
-
-       /* action
-        * struct containing fields to describe an atomic action. Note from
-        * VST sdk: parameter values, like all VST parameters, are declared as
-        * floats with an inclusive range of 0.0 to 1.0 (fValue). */
-
-       struct action
-       {
-               int      chan;    // channel index, i.e. Channel->index
-               int      type;
-               int      frame;   // redundant info, used by helper functions
-               float    fValue;  // used only for envelopes (volumes, vst params).
-               uint32_t iValue;  // used only for MIDI events
-       };
-
-       /* [global0]-->[vector<_action*>0]-->[a0][a1][a2]                               0[frames1]
-        * [global1]-->[vector<_action*>1]-->[a0][a1][a2]                               1[frames2]
-        * [global2]-->[vector<_action*>2]-->[a0][a1][a2]                               2[frames3]
-        * [global3]-->[vector<_action*>3]-->[a0][a1][a2]                               3[frames4] */
+/* action
+ * struct containing fields to describe an atomic action. Note from
+ * VST sdk: parameter values, like all VST parameters, are declared as
+ * floats with an inclusive range of 0.0 to 1.0 (fValue). */
 
-       vector<int>  frames;                                      // frame counter (sentinel) frames.size == global.size
-       vector<vector<action*>> global; // container of containers of actions
-       vector<action*> actions;                                // container of actions
-
-       bool active;
-       bool sortedActions;             // are actions sorted via sortActions()?
+struct action
+{
+       int      chan;    // channel index, i.e. Channel->index
+       int      type;
+       int      frame;   // redundant info, used by helper functions
+       float    fValue;  // used only for envelopes (volumes, vst params).
+       uint32_t iValue;  // used only for MIDI events
+};
 
-       /* init
-        * everything starts from here. */
+/* frames
+Frame counter sentinel. It tells which frames contain actions. E.g.:
+  frames[0] = 155   // some actions on frame 155
+  frames[1] = 2048  // some actions on frame 2048
+It always matches 'global''s size: frames.size() == global.size() */
 
-       void init();
+extern std::vector<int> frames;
 
-       /* hasActions
-       Checks if the channel has at least one action recorded. Used after an
-  action deletion. */
+/* global
+Contains the actual actions. E.g.:
+  global[0] = <actions>
+  global[1] = <actions> */
 
-       bool hasActions(int chanIndex);
+extern std::vector<std::vector<action*>> global;
 
-       /* canRec
-        * can a channel rec an action? Call this one BEFORE rec(). */
+extern bool active;
+extern bool sortedActions;   // are actions sorted via sortActions()?
 
-       bool canRec(class Channel *ch, class Mixer *m);
+/* init
+ * everything starts from here. */
 
-       /* rec
-        * record an action. */
+void init();
 
-       void rec(int chan, int action, int frame, uint32_t iValue=0,
-               float fValue=0.0f);
+/* hasActions
+Checks if the channel has at least one action recorded. Used after an
+action deletion. */
 
-       /* clearChan
-        * clear all actions from a channel. */
+bool hasActions(int chanIndex);
 
-       void clearChan(int chan);
+/* canRec
+ * can a channel rec an action? Call this one BEFORE rec(). */
 
-       /* clearAction
-        * clear the 'action' action type from a channel. */
+bool canRec(Channel *ch, bool clockRunning, bool mixerRecording);
 
-       void clearAction(int chan, char action);
+/* rec
+ * record an action. */
 
-       /* deleteAction
-        * delete ONE action. Useful in the action editor. 'type' can be a mask. */
+void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f);
 
-       void deleteAction(int chan, int frame, char type, bool checkValues,
-    pthread_mutex_t *mixerMutex, uint32_t iValue=0, float fValue=0.0);
+/* clearChan
+ * clear all actions from a channel. */
 
-       /* deleteActions
-        * delete A RANGE of actions from frame_a to frame_b in channel 'chan'.
-        * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */
+void clearChan(int chan);
 
-       void deleteActions(int chan, int frame_a, int frame_b, char type,
-    pthread_mutex_t *mixerMutex);
+/* clearAction
+ * clear the 'action' action type from a channel. */
 
-       /* clearAll
-        * delete everything. */
+void clearAction(int chan, char action);
 
-       void clearAll();
+/* deleteAction
+ * delete ONE action. Useful in the action editor. 'type' can be a mask. */
 
-       /* optimize
-        * clear frames without actions. */
+void deleteAction(int chan, int frame, char type, bool checkValues,
+  pthread_mutex_t *mixerMutex, uint32_t iValue=0, float fValue=0.0);
 
-       void optimize();
+/* deleteActions
+Deletes A RANGE of actions from frame_a to frame_b in channel 'chan' of type
+'type' (can be a bitmask). Exclusive range (frame_a, frame_b). */
 
-       /* sortActions
-        * sorts actions by frame, asc mode. */
+void deleteActions(int chan, int frame_a, int frame_b, char type,
+  pthread_mutex_t *mixerMutex);
 
-       void sortActions();
+/* clearAll
+ * delete everything. */
 
-       /* updateBpm
-        * reassign frames by calculating the new bpm value. */
+void clearAll();
 
-       void updateBpm(float oldval, float newval, int oldquanto);
+/* optimize
+ * clear frames without actions. */
 
-       /* updateSamplerate
-        * reassign frames taking in account the samplerate. If f_system ==
-        * f_patch nothing changes, otherwise the conversion is mandatory. */
+void optimize();
 
-       void updateSamplerate(int systemRate, int patchRate);
+/* sortActions
+ * sorts actions by frame, asc mode. */
 
-       void expand(int old_fpb, int new_fpb);
-       void shrink(int new_fpb);
+void sortActions();
 
-       /* getNextAction
-        * Return the nearest action in chan 'chan' of type 'action' starting
-        * from 'frame'. Action can be a bitmask. If iValue != 0 search for
-        * next action with iValue == iValue: useful for MIDI key_release. iValue
-        * can be a bitmask. */
+/* updateBpm
+ * reassign frames by calculating the new bpm value. */
 
-       int getNextAction(int chan, char action, int frame, struct action **out,
-               uint32_t iValue=0);
+void updateBpm(float oldval, float newval, int oldquanto);
 
-       /* getAction
-        * return a pointer to action in chan 'chan' of type 'action' at frame
       * 'frame'. */
+/* updateSamplerate
+ * reassign frames taking in account the samplerate. If f_system ==
* f_patch nothing changes, otherwise the conversion is mandatory. */
 
-       int getAction(int chan, char action, int frame, struct action **out);
+void updateSamplerate(int systemRate, int patchRate);
 
-       /* start/endOverdub */
+void expand(int old_fpb, int new_fpb);
+void shrink(int new_fpb);
 
-       void startOverdub(int chan, char action, int frame, unsigned bufferSize);
-       void stopOverdub(class Mixer *m);
+/* getNextAction
+ * Return the nearest action in chan 'chan' of type 'action' starting
+ * from 'frame'. Action can be a bitmask. If iValue != 0 search for
+ * next action with iValue == iValue: useful for MIDI key_release. iValue
+ * can be a bitmask. */
 
-private:
+int getNextAction(int chan, char action, int frame, struct action **out,
+       uint32_t iValue=0);
 
-       /* composite
-        * a group of two actions (keypress+keyrel, muteon+muteoff) used during
       * the overdub process */
+/* getAction
+ * return a pointer to action in chan 'chan' of type 'action' at frame
* 'frame'. */
 
-       struct composite
-       {
-               action a1;
-               action a2;
-       } cmp;
+int getAction(int chan, char action, int frame, struct action **out);
 
-       /* print
-        * debug of the frame stack. */
+/* start/stopOverdub
+These functions are used when you overwrite existing actions. For example:
+pressing Mute button on a channel with some existing mute actions. */
 
-       void print();
+void startOverdub(int chan, char action, int frame, unsigned bufferSize);
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex);
 
-};
+}}}; // giada::m::recorder::
 
 #endif
index 650d36f63e7a8371f83155574bc6f34b434bad0c..fd83e52dcc908ecdb3434b93541d1c35864ecb68 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <math.h>
+#include <cmath>
+#include <cstring>
 #include "../utils/log.h"
+#include "../utils/fs.h"
 #include "../utils/string.h"
 #include "sampleChannel.h"
-#include "patch_DEPR_.h"
 #include "patch.h"
+#include "const.h"
 #include "conf.h"
+#include "clock.h"
 #include "mixer.h"
 #include "wave.h"
 #include "pluginHost.h"
 
 
 using std::string;
+using namespace giada::m;
 
 
-extern Recorder    G_Recorder;
-extern KernelAudio G_KernelAudio;
-
-
-SampleChannel::SampleChannel(int bufferSize, MidiMapConf *midiMapConf)
-       : Channel          (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize, midiMapConf),
+SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
+       : Channel          (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
                frameRewind      (-1),
-               wave             (NULL),
+               boost            (G_DEFAULT_BOOST),
+               pitch            (G_DEFAULT_PITCH),
+               wave             (nullptr),
                tracker          (0),
                begin            (0),
                end              (0),
-               pitch            (G_DEFAULT_PITCH),
-               boost            (1.0f),
-               mode             (DEFAULT_CHANMODE),
+               mode             (G_DEFAULT_CHANMODE),
                qWait              (false),
                fadeinOn         (false),
                fadeinVol        (1.0f),
                fadeoutOn        (false),
                fadeoutVol       (1.0f),
                fadeoutTracker   (0),
-               fadeoutStep      (DEFAULT_FADEOUT_STEP),
+               fadeoutStep      (G_DEFAULT_FADEOUT_STEP),
+    inputMonitor     (inputMonitor),
          midiInReadActions(0x0),
          midiInPitch      (0x0)
 {
-       rsmp_state = src_new(SRC_LINEAR, 2, NULL);
-       pChan      = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
+       rsmp_state = src_new(SRC_LINEAR, 2, nullptr);
+       pChan = (float *) malloc(kernelAudio::getRealBufSize() * 2 * sizeof(float));
 }
 
 
@@ -125,7 +124,7 @@ void SampleChannel::generateUniqueSampleName()
 {
        string oldName = wave->name;
        int k = 1; // Start from k = 1, zero is too nerdy
-       while (!mh_uniqueSampleName(this, wave->name)) {
+       while (!mh::uniqueSampleName(this, wave->name)) {
                wave->updateName((oldName + "-" + gu_itoa(k)).c_str());
                k++;
        }
@@ -140,8 +139,8 @@ void SampleChannel::clear()
        /** TODO - these memsets can be done only if status PLAY (if below),
         * but it would require extra clearPChan calls when samples stop */
 
-               memset(vChan, 0, sizeof(float) * bufferSize);
-               memset(pChan, 0, sizeof(float) * bufferSize);
+               std::memset(vChan, 0, sizeof(float) * bufferSize);
+               std::memset(pChan, 0, sizeof(float) * bufferSize);
 
        if (status & (STATUS_PLAY | STATUS_ENDING)) {
                tracker = fillChan(vChan, tracker, 0);
@@ -160,26 +159,26 @@ void SampleChannel::calcVolumeEnv(int frame)
 {
        /* method: check this frame && next frame, then calculate delta */
 
-       Recorder::action *a0 = NULL;
-       Recorder::action *a1 = NULL;
+       recorder::action *a0 = nullptr;
+       recorder::action *a1 = nullptr;
        int res;
 
        /* get this action on frame 'frame'. It's unlikely that the action
         * is not found. */
 
-       res = G_Recorder.getAction(index, ACTION_VOLUME, frame, &a0);
+       res = recorder::getAction(index, G_ACTION_VOLUME, frame, &a0);
        if (res == 0)
                return;
 
        /* get the action next to this one.
         * res == -1: a1 not found, this is the last one. Rewind the search
         * and use action at frame number 0 (actions[0]).
-        * res == -2 ACTION_VOLUME not found. This should never happen */
+        * res == -2 G_ACTION_VOLUME not found. This should never happen */
 
-       res = G_Recorder.getNextAction(index, ACTION_VOLUME, frame, &a1);
+       res = recorder::getNextAction(index, G_ACTION_VOLUME, frame, &a1);
 
        if (res == -1)
-               res = G_Recorder.getAction(index, ACTION_VOLUME, 0, &a1);
+               res = recorder::getAction(index, G_ACTION_VOLUME, 0, &a1);
 
        volume_i = a0->fValue;
        volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f;
@@ -236,9 +235,18 @@ int SampleChannel::save(const char *path)
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setBegin(unsigned v)
+void SampleChannel::setBegin(int v)
 {
-       begin   = v;
+       if (v < 0)
+               begin = 0;
+       else
+       if (v > wave->size)
+               begin = wave->size - 2;
+       else
+       if (v >= end)
+               begin = end - 2;
+       else
+               begin = v;
        tracker = begin;
 }
 
@@ -246,9 +254,18 @@ void SampleChannel::setBegin(unsigned v)
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setEnd(unsigned v)
+void SampleChannel::setEnd(int v)
 {
-       end = v;
+       if (v < 0)
+               end = begin + 2;
+       else
+       if (v > wave->size)
+               end = wave->size;
+       else
+       if (v <= begin)
+               end = begin + 2;
+       else
+               end = v;
 }
 
 
@@ -257,7 +274,14 @@ void SampleChannel::setEnd(unsigned v)
 
 void SampleChannel::setPitch(float v)
 {
-       pitch = v;
+       if (v > G_MAX_PITCH)
+               pitch = G_MAX_PITCH;
+       else
+       if (v < 0.1f)
+               pitch = 0.1000f;
+       else 
+               pitch = v;
+
        rsmp_data.src_ratio = 1/pitch;
 
        /* if status is off don't slide between frequencies */
@@ -267,6 +291,12 @@ void SampleChannel::setPitch(float v)
 }
 
 
+float SampleChannel::getPitch()
+{
+       return pitch;
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -274,7 +304,7 @@ void SampleChannel::rewind()
 {
        /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */
 
-       if (wave != NULL) {
+       if (wave != nullptr) {
                if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY)))
                        reset(0);  // rewind is user-generated events, always on frame 0
        }
@@ -284,32 +314,32 @@ void SampleChannel::rewind()
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::parseAction(Recorder::action *a, int localFrame,
+void SampleChannel::parseAction(recorder::action *a, int localFrame,
                int globalFrame, int quantize, bool mixerIsRunning)
 {
        if (readActions == false)
                return;
 
        switch (a->type) {
-               case ACTION_KEYPRESS:
+               case G_ACTION_KEYPRESS:
                        if (mode & SINGLE_ANY)
                                start(localFrame, false, quantize, mixerIsRunning, false, false);
                        break;
-               case ACTION_KEYREL:
+               case G_ACTION_KEYREL:
                        if (mode & SINGLE_ANY)
                                stop();
                        break;
-               case ACTION_KILLCHAN:
+               case G_ACTION_KILL:
                        if (mode & SINGLE_ANY)
                                kill(localFrame);
                        break;
-               case ACTION_MUTEON:
+               case G_ACTION_MUTEON:
                        setMute(true);   // internal mute
                        break;
-               case ACTION_MUTEOFF:
+               case G_ACTION_MUTEOFF:
                        unsetMute(true); // internal mute
                        break;
-               case ACTION_VOLUME:
+               case G_ACTION_VOLUME:
                        calcVolumeEnv(globalFrame);
                        break;
        }
@@ -321,7 +351,7 @@ void SampleChannel::parseAction(Recorder::action *a, int localFrame,
 
 void SampleChannel::sum(int frame, bool running)
 {
-       if (wave == NULL || status & ~(STATUS_PLAY | STATUS_ENDING))
+       if (wave == nullptr || status & ~(STATUS_PLAY | STATUS_ENDING))
                return;
 
        if (frame != frameRewind) {
@@ -434,7 +464,7 @@ void SampleChannel::sum(int frame, bool running)
 
 void SampleChannel::onZero(int frame, bool recsStopOnChanHalt)
 {
-       if (wave == NULL)
+       if (wave == nullptr)
                return;
 
        if (mode & LOOP_ANY) {
@@ -477,7 +507,7 @@ void SampleChannel::onZero(int frame, bool recsStopOnChanHalt)
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::quantize(int index, int localFrame, Mixer *mixer)
+void SampleChannel::quantize(int index, int localFrame)
 {
        /* skip if LOOP_ANY or not in quantizer-wait mode */
 
@@ -500,14 +530,14 @@ void SampleChannel::quantize(int index, int localFrame, Mixer *mixer)
        /* this is the moment in which we record the keypress, if the
         * quantizer is on. SINGLE_PRESS needs overdub */
 
-       if (G_Recorder.canRec(this, mixer)) {
+       if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
                if (mode == SINGLE_PRESS) {
-                       G_Recorder.startOverdub(index, ACTION_KEYS, mixer->currentFrame,
-        G_KernelAudio.realBufsize);
+                       recorder::startOverdub(index, G_ACTION_KEYS, clock::getCurrentFrame(),
+        kernelAudio::getRealBufSize());
       readActions = false;   // don't read actions while overdubbing
     }
                else
-                       G_Recorder.rec(index, ACTION_KEYPRESS, mixer->currentFrame);
+                       recorder::rec(index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
     hasActions = true;
        }
 }
@@ -599,12 +629,33 @@ void SampleChannel::unsetMute(bool internal)
 /* -------------------------------------------------------------------------- */
 
 
+void SampleChannel::setBoost(float v)
+{
+       if (v > G_MAX_BOOST_DB)
+               boost = G_MAX_BOOST_DB;
+       else 
+       if (v < 0.0f)
+               boost = 0.0f;
+       else
+               boost = v;
+}
+
+
+float SampleChannel::getBoost()
+{
+       return boost;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void SampleChannel::calcFadeoutStep()
 {
-       if (end - tracker < (1 / DEFAULT_FADEOUT_STEP) * 2)
+       if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP) * 2)
                fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ???
        else
-               fadeoutStep = DEFAULT_FADEOUT_STEP;
+               fadeoutStep = G_DEFAULT_FADEOUT_STEP;
 }
 
 
@@ -689,9 +740,14 @@ void SampleChannel::empty()
        status = STATUS_OFF;
        if (wave) {
                delete wave;
-               wave = NULL;
+               wave = nullptr;
        }
-       status = STATUS_EMPTY;
+  begin   = 0;
+  end     = 0;
+  tracker = 0;
+       status  = STATUS_EMPTY;
+  volume  = G_DEFAULT_VOL;
+  boost   = G_DEFAULT_BOOST;
        sendMidiLplay();
 }
 
@@ -736,22 +792,22 @@ bool SampleChannel::allocEmpty(int frames, int samplerate, int takeId)
 
 void SampleChannel::process(float *outBuffer, float *inBuffer)
 {
-       /* If armed and inbuffer is not null (i.e. input device available), copy input
-       buffer to vChan: this enables the live recording mode. The vChan will be
-       overwritten later by PluginHost::processStack, so that you would record "clean"
-       audio (i.e. not plugin-processed). */
+       /* If armed and inbuffer is not nullptr (i.e. input device available) and
+  input monitor is on, copy input buffer to vChan: this enables the input
+  monitoring. The vChan will be overwritten later by pluginHost::processStack,
+  so that you would record "clean" audio (i.e. not plugin-processed). */
 
-       if (armed && inBuffer)
+       if (armed && inBuffer && inputMonitor)
     for (int i=0; i<bufferSize; i++)
       vChan[i] += inBuffer[i]; // add, don't overwrite (no raw memcpy)
 
 #ifdef WITH_VST
-       pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
+       pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
 #endif
 
-       for (int j=0; j<bufferSize; j+=2) {
-               outBuffer[j]   += vChan[j]   * volume * panLeft  * boost;
-               outBuffer[j+1] += vChan[j+1] * volume * panRight * boost;
+  for (int j=0; j<bufferSize; j+=2) {
+               outBuffer[j]   += vChan[j]   * volume * calcPanning(0) * boost;
+               outBuffer[j+1] += vChan[j+1] * volume * calcPanning(1) * boost;
        }
 }
 
@@ -761,7 +817,7 @@ void SampleChannel::process(float *outBuffer, float *inBuffer)
 
 void SampleChannel::kill(int frame)
 {
-       if (wave != NULL && status != STATUS_OFF) {
+       if (wave != nullptr && status != STATUS_OFF) {
                if (mute || mute_i || (status == STATUS_WAIT && mode & LOOP_ANY))
                        hardStop(frame);
                else
@@ -870,64 +926,15 @@ int SampleChannel::load(const char *file, int samplerate, int rsmpQuality)
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::readPatch_DEPR_(const char *f, int i, Patch_DEPR_ *patch,
-               int samplerate, int rsmpQuality)
-{
-       int res = load(f, samplerate, rsmpQuality);
-
-               volume      = patch->getVol(i);
-               key         = patch->getKey(i);
-               index       = patch->getIndex(i);
-               mode        = patch->getMode(i);
-               mute        = patch->getMute(i);
-               mute_s      = patch->getMute_s(i);
-               solo        = patch->getSolo(i);
-               boost       = patch->getBoost(i);
-               panLeft     = patch->getPanLeft(i);
-               panRight    = patch->getPanRight(i);
-               readActions = patch->getRecActive(i);
-               recStatus   = readActions ? REC_READING : REC_STOPPED;
-
-               readPatchMidiIn_DEPR_(i, *patch);
-               midiInReadActions = patch->getMidiValue(i, "InReadActions");
-               midiInPitch       = patch->getMidiValue(i, "InPitch");
-               readPatchMidiOut_DEPR_(i, *patch);
-
-       if (res == SAMPLE_LOADED_OK) {
-               setBegin(patch->getBegin(i));
-               setEnd  (patch->getEnd(i, wave->size));
-               setPitch(patch->getPitch(i));
-       }
-       else {
-               // volume = DEFAULT_VOL;
-               // mode   = DEFAULT_CHANMODE;
-               // status = STATUS_WRONG;
-               // key    = 0;
-
-               if (res == SAMPLE_LEFT_EMPTY)
-                       status = STATUS_EMPTY;
-               else
-               if (res == SAMPLE_READ_ERROR)
-                       status = STATUS_MISSING;
-               sendMidiLplay();
-       }
-
-       return res;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::readPatch(const string &basePath, int i, Patch *patch,
+int SampleChannel::readPatch(const string &basePath, int i,
                pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
 {
        /* load channel's data first: if the sample is missing or wrong, the channel
         * is not completely blank. */
 
-       Channel::readPatch("", i, patch, pluginMutex, samplerate, rsmpQuality);
+       Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
 
-       Patch::channel_t *pch = &patch->channels.at(i);
+       patch::channel_t *pch = &patch::channels.at(i);
 
        mode              = pch->mode;
        boost             = pch->boost;
@@ -935,6 +942,7 @@ int SampleChannel::readPatch(const string &basePath, int i, Patch *patch,
        recStatus         = readActions ? REC_READING : REC_STOPPED;
        midiInReadActions = pch->midiInReadActions;
        midiInPitch       = pch->midiInPitch;
+  inputMonitor      = pch->inputMonitor;
 
        int res = load((basePath + pch->samplePath).c_str(), samplerate, rsmpQuality);
        if (res == SAMPLE_LOADED_OK) {
@@ -960,7 +968,7 @@ int SampleChannel::readPatch(const string &basePath, int i, Patch *patch,
 
 bool SampleChannel::canInputRec()
 {
-       return wave == NULL && armed;
+       return wave == nullptr && armed;
 }
 
 
@@ -1042,14 +1050,14 @@ void SampleChannel::start(int frame, bool doQuantize, int quantize,
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::writePatch(int i, bool isProject, Patch *patch)
+int SampleChannel::writePatch(int i, bool isProject)
 {
        // TODO - this code belongs to an upper level (glue)
 
-       int pchIndex = Channel::writePatch(i, isProject, patch);
-       Patch::channel_t *pch = &patch->channels.at(pchIndex);
+       int pchIndex = Channel::writePatch(i, isProject);
+       patch::channel_t *pch = &patch::channels.at(pchIndex);
 
-       if (wave != NULL) {
+       if (wave != nullptr) {
                pch->samplePath = wave->pathfile;
                if (isProject)
                        pch->samplePath = gu_basename(wave->pathfile);  // make it portable
@@ -1063,6 +1071,7 @@ int SampleChannel::writePatch(int i, bool isProject, Patch *patch)
        pch->boost             = boost;
        pch->recActive         = readActions;
        pch->pitch             = pitch;
+       pch->inputMonitor      = inputMonitor;
        pch->midiInReadActions = midiInReadActions;
        pch->midiInPitch       = midiInPitch;
 
index 2eeb574603475231124964e84677a7103aef9e9a..f8203ef90a47c353c466e28ed92eeb8d97d3e9df 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * sampleChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef SAMPLE_CHANNEL_H
-#define SAMPLE_CHANNEL_H
+#ifndef G_SAMPLE_CHANNEL_H
+#define G_SAMPLE_CHANNEL_H
 
 
 #include <samplerate.h>
 #include "channel.h"
 
 
+class Patch;
+class Wave;
+
+
 class SampleChannel : public Channel
 {
 private:
@@ -46,15 +48,18 @@ private:
        SRC_DATA   rsmp_data;
 
        /* pChan
-        * extra virtual channel for processing resampled data.  */
+       Extra virtual channel for processing resampled data. */
 
        float *pChan;
 
        /* frameRewind
-        * exact frame in which a rewind occurs */
+       Exact frame in which a rewind occurs. */
 
        int frameRewind;
 
+       float boost;
+       float pitch;
+
        /* fillChan
         * copy from wave to *dest and resample data from wave, if necessary.
         * Start to fill pChan from byte 'offset'. If rewind=false don't
@@ -86,7 +91,7 @@ private:
 
 public:
 
-       SampleChannel(int bufferSize, class MidiMapConf *midiMapConf);
+       SampleChannel(int bufferSize, bool inputMonitor);
        ~SampleChannel();
 
        void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
@@ -101,15 +106,13 @@ public:
        void rewind() override;
        void setMute(bool internal) override;
        void unsetMute(bool internal) override;
-       int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
-                       int samplerate, int rsmpQuality) override;
-  int readPatch(const string &basePath, int i, class Patch *patch,
-                       pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
-       int writePatch(int i, bool isProject, class Patch *patch) override;
-       void quantize(int index, int localFrame, class Mixer *m) override;
+  int readPatch(const std::string &basePath, int i, pthread_mutex_t *pluginMutex,
+    int samplerate, int rsmpQuality) override;
+       int writePatch(int i, bool isProject) override;
+       void quantize(int index, int localFrame) override;
        void onZero(int frame, bool recsStopOnChanHalt) override;
        void onBar(int frame) override;
-       void parseAction(Recorder::action *a, int localFrame, int globalFrame,
+       void parseAction(giada::m::recorder::action *a, int localFrame, int globalFrame,
                        int quantize, bool mixerIsRunning) override;
        bool canInputRec() override;
 
@@ -128,7 +131,7 @@ public:
        /* pushWave
         * add a new wave to an existing channel. */
 
-       void pushWave(class Wave *w);
+       void pushWave(Wave *w);
 
        /* getPosition
         * returns the position of an active sample. If EMPTY o MISSING
@@ -146,12 +149,13 @@ public:
         * updates the pitch value and chanStart+chanEnd accordingly. */
 
        void setPitch(float v);
+       float getPitch();
 
        /* setStart/end
         * change begin/end read points in sample. */
 
-       void setBegin(unsigned v);
-       void setEnd  (unsigned v);
+       void setBegin(int v);
+       void setEnd  (int v);
 
        /* save
         * save sample to file. */
@@ -174,14 +178,15 @@ public:
 
        void setReadActions(bool v, bool recsStopOnChanHalt);
 
+       void setBoost(float v);
+       float getBoost();       
+
        /* ------------------------------------------------------------------------ */
 
-       class  Wave *wave;
+       Wave  *wave;
        int    tracker;         // chan position
        int    begin;
        int    end;
-  float  pitch;
-       float  boost;
        int    mode;            // mode: see const.h
        bool   qWait;           // quantizer wait
        bool   fadeinOn;
@@ -192,6 +197,7 @@ public:
        float  fadeoutStep;     // fadeout decrease
   int    fadeoutType;     // xfade or fadeout
   int           fadeoutEnd;      // what to do when fadeout ends
+  bool   inputMonitor;
 
        /* midi stuff */
 
diff --git a/src/core/storager.cpp b/src/core/storager.cpp
new file mode 100644 (file)
index 0000000..191d485
--- /dev/null
@@ -0,0 +1,152 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "storager.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace m {
+namespace storager
+{
+bool setString(json_t *jRoot, const char *key, string &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!json_is_string(jObject)) {
+    gu_log("[storager::setString] key '%s' is not a string!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_string_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool setFloat(json_t *jRoot, const char *key, float &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gu_log("[storager::setFloat] key '%s' not found, using default value\n", key);
+    output = 0.0f;
+    return true;
+  }
+  if (!json_is_real(jObject)) {
+    gu_log("[storager::setFloat] key '%s' is not a float!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_real_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool setUint32(json_t *jRoot, const char *key, uint32_t &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gu_log("[storager::setUint32] key '%s' not found, using default value\n", key);
+    output = 0;
+    return true;
+  }
+  if (!json_is_integer(jObject)) {
+    gu_log("[storager::setUint32] key '%s' is not an integer!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_integer_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool setBool(json_t *jRoot, const char *key, bool &output)
+{
+  json_t *jObject = json_object_get(jRoot, key);
+  if (!jObject) {
+    gu_log("[storager::setBool] key '%s' not found, using default value\n", key);
+    output = false;
+    return true;
+  }
+  if (!json_is_boolean(jObject)) {
+    gu_log("[storager::setBool] key '%s' is not a boolean!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  output = json_boolean_value(jObject);
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool setInt(json_t *jRoot, const char *key, int &output)
+{
+  return setUint32(jRoot, key, (uint32_t&) output);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool checkObject(json_t *jRoot, const char *key)
+{
+  if (!json_is_object(jRoot)) {
+    gu_log("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool checkArray(json_t *jRoot, const char *key)
+{
+  if (!json_is_array(jRoot)) {
+    gu_log("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key);
+    json_decref(jRoot);
+    return false;
+  }
+  return true;
+}
+
+}}}; // giada::m::storager::
diff --git a/src/core/storager.h b/src/core/storager.h
new file mode 100644 (file)
index 0000000..395c0a5
--- /dev/null
@@ -0,0 +1,58 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_STORAGER_H
+#define G_STORAGER_H
+
+
+#include <jansson.h>
+
+
+namespace giada {
+namespace m {
+namespace storager
+{
+bool setString(json_t *jRoot, const char *key, std::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);
+
+}}}; // giada::m::storager::
+
+
+#endif
index 7f89e32002540fe4895d57fb707c964868eaf351..1f821b6859819d98ecf50dd2658d92517bda7e39 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>  // memcpy
-#include <math.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>  // memcpy
+#include <cmath>
+#include <samplerate.h>
 #include "../utils/fs.h"
 #include "../utils/log.h"
 #include "init.h"
@@ -42,7 +43,7 @@ using std::string;
 
 
 Wave::Wave()
-: data     (NULL),
+: data     (nullptr),
        size     (0),
        isLogical(0),
        isEdited (0) {}
@@ -61,7 +62,7 @@ Wave::~Wave()
 
 
 Wave::Wave(const Wave &other)
-: data     (NULL),
+: data     (nullptr),
        size     (0),
        isLogical(false),
        isEdited (false)
@@ -84,7 +85,7 @@ int Wave::open(const char *f)
        name     = gu_stripExt(gu_basename(f));
        fileIn   = sf_open(f, SFM_READ, &inHeader);
 
-       if (fileIn == NULL) {
+       if (fileIn == nullptr) {
                gu_log("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
                pathfile = "";
                name     = "";
@@ -118,7 +119,7 @@ int Wave::readData()
 {
        size = inHeader.frames * inHeader.channels;
        data = (float *) malloc(size * sizeof(float));
-       if (data == NULL) {
+       if (data == nullptr) {
                gu_log("[wave] unable to allocate memory\n");
                return 0;
        }
@@ -143,7 +144,7 @@ int Wave::writeData(const char *f)
        outHeader.format     = inHeader.format;
 
        fileOut = sf_open(f, SFM_WRITE, &outHeader);
-       if (fileOut == NULL) {
+       if (fileOut == nullptr) {
                gu_log("[wave] unable to open %s for exporting\n", f);
                return 0;
        }
@@ -166,9 +167,9 @@ int Wave::writeData(const char *f)
 
 void Wave::clear()
 {
-       if (data != NULL) {
+       if (data != nullptr) {
                free(data);
-               data     = NULL;
+               data     = nullptr;
                pathfile = "";
                size     = 0;
        }
@@ -185,7 +186,7 @@ int Wave::allocEmpty(unsigned __size, unsigned samplerate)
        /// FIXME - this way if malloc fails size becomes wrong
        size = __size;
        data = (float *) malloc(size * sizeof(float));
-       if (data == NULL) {
+       if (data == nullptr) {
                gu_log("[wave] unable to allocate memory\n");
                return 0;
        }
@@ -267,3 +268,19 @@ void Wave::updateName(const char *n)
        /* a wave with updated name must become logical, since the underlying
         * file does not exist yet. */
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int  Wave::rate    () { return inHeader.samplerate; }
+int  Wave::channels() { return inHeader.channels; }
+int  Wave::frames  () { return inHeader.frames; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Wave::rate    (int v) { inHeader.samplerate = v; }
+void Wave::channels(int v) { inHeader.channels = v; }
+void Wave::frames  (int v) { inHeader.frames = v; }
index f602667de69593285ad7f2f19fcd6a64b17faa67..11c721d51eb9a09489a360ddec9bf5577bdfbaee 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef WAVE_H
-#define WAVE_H
+#ifndef G_WAVE_H
+#define G_WAVE_H
 
 
-#include <samplerate.h>
 #include <sndfile.h>
 #include <string>
 
 
-using std::string;
-
-
 class Wave
 {
 private:
 
-       SNDFILE   *fileIn;
-       SNDFILE   *fileOut;
-       SF_INFO    inHeader;
-       SF_INFO    outHeader;
-
+       SNDFILE *fileIn;
+       SNDFILE *fileOut;
+       SF_INFO  inHeader;
+       SF_INFO  outHeader;
 
 public:
 
@@ -55,23 +50,23 @@ public:
        ~Wave();
        Wave(const Wave &other);
 
-       string pathfile; // full path + sample name
-       string name;                    // sample name (changeable)
+       std::string pathfile; // full path + sample name
+       std::string name;                       // sample name (changeable)
 
-       float     *data;
-       int        size;                          // wave size (size in stereo: size / 2)
-       bool       isLogical;   // memory only (a take)
-       bool       isEdited;    // edited via editor
+       float *data;
+       int    size;                      // wave size (size in stereo: size / 2)
+       bool   isLogical;   // memory only (a take)
+       bool   isEdited;    // edited via editor
 
-       inline int  rate    () { return inHeader.samplerate; }
-       inline int  channels() { return inHeader.channels; }
-       inline int  frames  () { return inHeader.frames; }
-       inline void rate    (int v) { inHeader.samplerate = v; }
-       inline void channels(int v) { inHeader.channels = v; }
-       inline void frames  (int v) { inHeader.frames = v; }
+       int  rate    ();
+       int  channels();
+       int  frames  ();
+       void rate    (int v);
+       void channels(int v);
+       void frames  (int v);
 
-       string basename (bool ext=false) const;
-       string extension() const;
+       std::string basename(bool ext=false) const;
+       std::string extension() const;
 
        void updateName(const char *n);
        int  open      (const char *f);
index 58319684749dcbd05f64c05b8c6a5fd434625d75..6358c25419d03b06e3956e0507ac1af4ac7f771e 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -60,7 +60,7 @@ bool wfx_monoToStereo(Wave *w)
 {
        unsigned newSize = w->size * 2;
        float *dataNew = (float *) malloc(newSize * sizeof(float));
-       if (dataNew == NULL) {
+       if (dataNew == nullptr) {
                gu_log("[wfx] unable to allocate memory for mono>stereo conversion\n");
                return 0;
        }
@@ -119,7 +119,7 @@ int wfx_cut(Wave *w, int a, int b)
 
        unsigned newSize = w->size-(b-a);
        float *temp = (float *) malloc(newSize * sizeof(float));
-       if (temp == NULL) {
+       if (temp == nullptr) {
                gu_log("[wfx] unable to allocate memory for cutting\n");
                return 0;
        }
@@ -159,7 +159,7 @@ int wfx_trim(Wave *w, int a, int b)
 
        int newSize = b - a;
        float *temp = (float *) malloc(newSize * sizeof(float));
-       if (temp == NULL) {
+       if (temp == nullptr) {
                gu_log("[wfx] unable to allocate memory for trimming\n");
                return 0;
        }
index 7eae643133cffbdd003bd77ecfa4c2a9a10d449b..4b7dee82377c697791457abe2ae6f50df8759e5c 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef WAVEFX_H
-#define WAVEFX_H
+#ifndef G_WAVE_FX_H
+#define G_WAVE_FX_H
+
+
+class Wave;
 
 
 /* normalizeSoft
  * normalize the wave by returning the dB value for the boost volume. It
  * doesn't deal with data in memory. */
 
-float wfx_normalizeSoft(class Wave *w);
+float wfx_normalizeSoft(Wave *w);
 
-bool wfx_monoToStereo(class Wave *w);
+bool wfx_monoToStereo(Wave *w);
 
-void wfx_silence(class Wave *w, int a, int b);
+void wfx_silence(Wave *w, int a, int b);
 
-int wfx_cut(class Wave *w, int a, int b);
+int wfx_cut(Wave *w, int a, int b);
 
-int wfx_trim(class Wave *w, int a, int b);
+int wfx_trim(Wave *w, int a, int b);
 
 /* fade
  * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */
 
-void wfx_fade(class Wave *w, int a, int b, int type);
+void wfx_fade(Wave *w, int a, int b, int type);
 
 /* smooth
  * smooth edges of selection. */
 
-void wfx_smooth(class Wave *w, int a, int b);
+void wfx_smooth(Wave *w, int a, int b);
 
 
 #endif
index 0af1044731100071d2eb979a074eeef7d25a7abb..742b77f872345b6c6ca1d6caffbd94cb58c1964f 100644 (file)
@@ -2,12 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <cmath>
+#include <FL/Fl.H>
 #include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_editor.h"
-#include "../gui/elems/ge_waveTools.h"
-#include "../gui/elems/ge_waveform.h"
-#include "../gui/elems/ge_mixed.h"
+#include "../gui/dialogs/sampleEditor.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/elems/basics/input.h"
+#include "../gui/elems/basics/dial.h"
+#include "../gui/elems/sampleEditor/waveTools.h"
+#include "../gui/elems/sampleEditor/volumeTool.h"
+#include "../gui/elems/sampleEditor/boostTool.h"
+#include "../gui/elems/sampleEditor/panTool.h"
+#include "../gui/elems/sampleEditor/pitchTool.h"
+#include "../gui/elems/sampleEditor/rangeTool.h"
+#include "../gui/elems/sampleEditor/waveform.h"
 #include "../gui/elems/mainWindow/keyboard/keyboard.h"
 #include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "../gui/elems/mainWindow/keyboard/channelButton.h"
 #include "../utils/gui.h"
+#include "../utils/fs.h"
+#include "../core/kernelAudio.h"
 #include "../core/mixerHandler.h"
 #include "../core/mixer.h"
+#include "../core/clock.h"
 #include "../core/pluginHost.h"
 #include "../core/conf.h"
 #include "../core/wave.h"
 
 
 extern gdMainWindow *G_MainWin;
-extern Conf          G_Conf;
-extern KernelAudio   G_KernelAudio;
-extern Recorder                         G_Recorder;
-extern Mixer                    G_Mixer;
-#ifdef WITH_VST
-extern PluginHost    G_PluginHost;
-#endif
 
 
 using std::string;
+using namespace giada::m;
 
 
 static bool __soloSession__ = false;
@@ -75,9 +80,9 @@ int glue_loadChannel(SampleChannel *ch, const string &fname)
        /* save the patch and take the last browser's dir in order to re-use it
         * the next time */
 
-       G_Conf.samplePath = gu_dirname(fname);
+       conf::samplePath = gu_dirname(fname);
 
-       int result = ch->load(fname.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality);
+       int result = ch->load(fname.c_str(), conf::samplerate, conf::rsmpQuality);
 
        if (result == SAMPLE_LOADED_OK)
                G_MainWin->keyboard->updateChannel(ch->guiChannel);
@@ -91,7 +96,7 @@ int glue_loadChannel(SampleChannel *ch, const string &fname)
 
 Channel *glue_addChannel(int column, int type)
 {
-       Channel *ch     = G_Mixer.addChannel(type);
+       Channel *ch     = mh::addChannel(type);
        geChannel *gch  = G_MainWin->keyboard->addChannel(column, ch);
        ch->guiChannel  = gch;
        return ch;
@@ -103,15 +108,17 @@ Channel *glue_addChannel(int column, int type)
 
 void glue_deleteChannel(Channel *ch)
 {
-       G_Recorder.clearChan(ch->index);
+  if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+    return;
+  recorder::clearChan(ch->index);
   ch->hasActions = false;
 #ifdef WITH_VST
-       G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
+       pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex_plugins, ch);
 #endif
        Fl::lock();
        G_MainWin->keyboard->deleteChannel(ch->guiChannel);
        Fl::unlock();
-       G_Mixer.deleteChannel(ch);
+       mh::deleteChannel(ch);
        gu_closeAllSubwindows();
 }
 
@@ -121,16 +128,25 @@ void glue_deleteChannel(Channel *ch)
 
 void glue_freeChannel(Channel *ch)
 {
-#ifdef WITH_VST
+  if (ch->status == STATUS_PLAY) {
+    if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
+      return;
+  }
+  else
+  if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
+    return;
 
-       G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
-  ch->guiChannel->fx->full = false;
-
-#endif
        G_MainWin->keyboard->freeChannel(ch->guiChannel);
-       G_Recorder.clearChan(ch->index);
+       recorder::clearChan(ch->index);
   ch->hasActions = false;
        ch->empty();
+
+  /* delete any related subwindow */
+  /** TODO - use gu_closeAllSubwindows()   */
+  G_MainWin->delSubWindow(WID_FILE_BROWSER);
+  G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+  G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+  G_MainWin->delSubWindow(WID_FX_LIST);
 }
 
 
@@ -148,13 +164,23 @@ void glue_toggleArm(Channel *ch, bool gui)
 /* -------------------------------------------------------------------------- */
 
 
+void glue_toggleInputMonitor(Channel *ch)
+{
+  SampleChannel *sch = static_cast<SampleChannel*>(ch);
+  sch->inputMonitor = !sch->inputMonitor;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 int glue_cloneChannel(Channel *src)
 {
-       Channel *ch    = G_Mixer.addChannel(src->type);
+       Channel *ch    = mh::addChannel(src->type);
        geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch);
 
        ch->guiChannel = gch;
-       ch->copy(src, &G_Mixer.mutex_plugins);
+       ch->copy(src, &mixer::mutex_plugins);
 
        G_MainWin->keyboard->updateChannel(ch->guiChannel);
        return true;
@@ -164,19 +190,20 @@ int glue_cloneChannel(Channel *src)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setChanVol(Channel *ch, float v, bool gui)
+void glue_setVolume(Channel *ch, float v, bool gui, bool editor)
 {
        ch->volume = v;
 
-       /* also update wave editor if it's shown */
+       /* Changing channel volume? Update wave editor (if it's shown). */
 
-       gdEditor *editor = (gdEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR);
-       if (editor) {
-               glue_setVolEditor(editor, (SampleChannel*) ch, v, false);
-               Fl::lock();
-               editor->volume->value(v);
-               Fl::unlock();
-       }
+  if (!editor) {
+       gdSampleEditor *gdEditor = (gdSampleEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR);
+       if (gdEditor) {
+               Fl::lock();
+               gdEditor->volumeTool->refresh();
+               Fl::unlock();
+       }
+  }
 
        if (!gui) {
                Fl::lock();
@@ -189,25 +216,13 @@ void glue_setChanVol(Channel *ch, float v, bool gui)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setPitch(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_setPitch(SampleChannel *ch, float val)
 {
-       if (numeric) {
-               if (val <= 0.0f)
-                       val = 0.1000f;
-               if (val > 4.0f)
-                       val = 4.0000f;
-               if (win)
-                       win->pitch->value(val);
-       }
-
        ch->setPitch(val);
-
-       if (win) {
-               char buf[16];
-               sprintf(buf, "%.4f", val);
+       gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       if (gdEditor) {
                Fl::lock();
-               win->pitchNum->value(buf);
-               win->pitchNum->redraw();
+               gdEditor->pitchTool->refresh();
                Fl::unlock();
        }
 }
@@ -216,30 +231,15 @@ void glue_setPitch(gdEditor *win, SampleChannel *ch, float val, bool numeric)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setPanning(gdEditor *win, SampleChannel *ch, float val)
+void glue_setPanning(SampleChannel *ch, float val)
 {
-       if (val < 1.0f) {
-               ch->panLeft = 1.0f;
-               ch->panRight= 0.0f + val;
-
-               char buf[8];
-               sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
-               win->panNum->value(buf);
-       }
-       else if (val == 1.0f) {
-               ch->panLeft = 1.0f;
-               ch->panRight= 1.0f;
-         win->panNum->value("C");
-       }
-       else {
-               ch->panLeft = 2.0f - val;
-               ch->panRight= 1.0f;
-
-               char buf[8];
-               sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
-               win->panNum->value(buf);
+       ch->setPan(val);
+       gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       if (gdEditor) {
+               Fl::lock();
+               gdEditor->panTool->refresh();
+               Fl::unlock();
        }
-       win->panNum->redraw();
 }
 
 
@@ -248,14 +248,15 @@ void glue_setPanning(gdEditor *win, SampleChannel *ch, float val)
 
 void glue_setMute(Channel *ch, bool gui)
 {
-       if (G_Recorder.active && G_Recorder.canRec(ch, &G_Mixer)) {
+       if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) {
                if (!ch->mute) {
-                       G_Recorder.startOverdub(ch->index, ACTION_MUTES, G_Mixer.currentFrame,
-        G_KernelAudio.realBufsize);
+                       recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(),
+        kernelAudio::getRealBufSize());
       ch->readActions = false;   // don't read actions while overdubbing
     }
                else
-                G_Recorder.stopOverdub(&G_Mixer);
+                recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+      &mixer::mutex_recs);
        }
 
        ch->mute ? ch->unsetMute(false) : ch->setMute(false);
@@ -277,8 +278,8 @@ void glue_setSoloOn(Channel *ch, bool gui)
         * and start the session */
 
        if (!__soloSession__) {
-               for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-                       Channel *och = G_Mixer.channels.at(i);
+               for (unsigned i=0; i<mixer::channels.size(); i++) {
+                       Channel *och = mixer::channels.at(i);
                        och->mute_s  = och->mute;
                }
                __soloSession__ = true;
@@ -289,8 +290,8 @@ void glue_setSoloOn(Channel *ch, bool gui)
 
        /* mute all other channels and unmute this (if muted) */
 
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               Channel *och = G_Mixer.channels.at(i);
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               Channel *och = mixer::channels.at(i);
                if (!och->solo && !och->mute) {
                        och->setMute(false);
                        Fl::lock();
@@ -322,10 +323,10 @@ void glue_setSoloOff(Channel *ch, bool gui)
        /* if this is uniqueSolo, stop solo session and restore mute status,
         * else mute this */
 
-       if (mh_uniqueSolo(ch)) {
+       if (mh::uniqueSolo(ch)) {
                __soloSession__ = false;
-               for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-                       Channel *och = G_Mixer.channels.at(i);
+               for (unsigned i=0; i<mixer::channels.size(); i++) {
+                       Channel *och = mixer::channels.at(i);
                        if (och->mute_s) {
                                och->setMute(false);
                                Fl::lock();
@@ -362,126 +363,93 @@ void glue_setSoloOff(Channel *ch, bool gui)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e,
-       bool recalc, bool check)
+void glue_setBeginEndChannel(SampleChannel *ch, int b, int e)
 {
-       if (check) {
-               if (e > ch->wave->size)
-                       e = ch->wave->size;
-               if (b < 0)
-                       b = 0;
-               if (b > ch->wave->size)
-                       b = ch->wave->size-2;
-               if (b >= ch->end)
-                       b = ch->begin;
-               if (e <= ch->begin)
-                       e = ch->end;
-       }
-
-       /* continue only if new values != old values */
-
-       if (b == ch->begin && e == ch->end)
-               return;
-
-       /* print mono values */
-
-       char tmp[16];
-       sprintf(tmp, "%d", b/2);
-       win->chanStart->value(tmp);
-
-       tmp[0] = '\0';
-       sprintf(tmp, "%d", e/2);
-       win->chanEnd->value(tmp);
-
        ch->setBegin(b);
        ch->setEnd(e);
-
-       /* Recalc is not needed when the user drags the bars directly over the
-       waveform */
-
-       if (recalc) {
-               win->waveTools->waveform->recalcPoints();
-               win->waveTools->waveform->redraw();
-       }
+       gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       if (gdEditor) {
+               Fl::lock();
+               gdEditor->rangeTool->refresh();
+               Fl::unlock();
+       }       
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_setBoost(SampleChannel *ch, float val)
 {
-       if (numeric) {
-               if (val > 20.0f)
-                       val = 20.0f;
-               else if (val < 0.0f)
-                       val = 0.0f;
+       ch->setBoost(val);
+       gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       if (gdEditor) {
+               Fl::lock();
+               gdEditor->boostTool->refresh();
+               Fl::unlock();
+       }
+}
 
-         float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
 
-               ch->boost = linear;
+/* -------------------------------------------------------------------------- */
 
-               char buf[10];
-               sprintf(buf, "%.2f", val);
-               win->boostNum->value(buf);
-               win->boostNum->redraw();
 
-               win->boost->value(linear);
-               win->boost->redraw();       /// inutile
-       }
-       else {
-               ch->boost = val;
-               char buf[10];
-               sprintf(buf, "%.2f", 20*log10(val));
-               win->boostNum->value(buf);
-               win->boostNum->redraw();
-       }
+void glue_startStopReadingRecs(SampleChannel *ch, bool gui)
+{
+       /* When you call glue_startReadingRecs with conf::treatRecsAsLoops, the
+       member value ch->readActions actually is not set to true immediately, because
+       the channel is in wait mode (REC_WAITING). ch->readActions will become true on
+       the next first beat. So a 'stop rec' command should occur also when
+       ch->readActions is false but the channel is in wait mode; this check will
+       handle the case of when you press 'R', the channel goes into REC_WAITING and
+       then you press 'R' again to undo the status. */
+
+       if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING))
+               glue_stopReadingRecs(ch, gui);
+       else
+               glue_startReadingRecs(ch, gui);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setVolEditor(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_startReadingRecs(SampleChannel *ch, bool gui)
 {
-       if (numeric) {
-               if (val > 0.0f)
-                       val = 0.0f;
-               else if (val < -60.0f)
-                       val = -INFINITY;
+       if (conf::treatRecsAsLoops)
+               ch->recStatus = REC_WAITING;
+       else
+               ch->setReadActions(true, conf::recsStopOnChanHalt);
+       if (!gui) {
+               Fl::lock();
+               ((geSampleChannel*)ch->guiChannel)->readActions->value(1);
+               Fl::unlock();
+       }
+}
 
-         float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
 
-               ch->volume = linear;
+/* -------------------------------------------------------------------------- */
 
-               win->volume->value(linear);
-               win->volume->redraw();
 
-               char buf[10];
-               if (val > -INFINITY)
-                       sprintf(buf, "%.2f", val);
-               else
-                       sprintf(buf, "-inf");
-               win->volumeNum->value(buf);
-               win->volumeNum->redraw();
+void glue_stopReadingRecs(SampleChannel *ch, bool gui)
+{
+       /* First of all, if the mixer is not running just stop and disable everything.
+       Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
+       channel in REC_ENDING status. */
 
-               ch->guiChannel->vol->value(linear);
-               ch->guiChannel->vol->redraw();
+       if (!clock::isRunning()) {
+               ch->recStatus = REC_STOPPED;
+               ch->readActions = false;
        }
-       else {
-               ch->volume = val;
-
-               float dbVal = 20 * log10(val);
-               char buf[10];
-               if (dbVal > -INFINITY)
-                       sprintf(buf, "%.2f", dbVal);
-               else
-                       sprintf(buf, "-inf");
-
-               win->volumeNum->value(buf);
-               win->volumeNum->redraw();
+       else
+       if (conf::treatRecsAsLoops)
+               ch->recStatus = REC_ENDING;
+       else
+               ch->setReadActions(false, conf::recsStopOnChanHalt);
 
-               ch->guiChannel->vol->value(val);
-               ch->guiChannel->vol->redraw();
+       if (!gui) {
+               Fl::lock();
+               ((geSampleChannel*)ch->guiChannel)->readActions->value(0);
+               Fl::unlock();
        }
 }
index 3534addfbda54bc52d36dd6b77a7b8ed3d0ed5ee..fe5ca350cfee63647eef22c02c6e52127e648712 100644 (file)
@@ -2,12 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef GLUE_CHANNEL_H
-#define GLUE_CHANNEL_H
+#ifndef G_GLUE_CHANNEL_H
+#define G_GLUE_CHANNEL_H
 
 
 #include <string>
 
 
+class Channel;
+class SampleChannel;
+class gdSampleEditor;
+
+
 /* addChannel
  * add an empty new channel to the stack. Returns the new channel. */
 
-class Channel *glue_addChannel(int column, int type);
+Channel *glue_addChannel(int column, int type);
 
 /* loadChannel
  * fill an existing channel with a wave. */
 
-int glue_loadChannel(class SampleChannel *ch, const std::string &fname);
+int glue_loadChannel(SampleChannel *ch, const std::string &fname);
 
 /* deleteChannel
  * Remove a channel from Mixer. */
 
-void glue_deleteChannel(class Channel *ch);
+void glue_deleteChannel(Channel *ch);
 
 /* freeChannel
  * Unload the sample from a sample channel. */
 
-void glue_freeChannel(class Channel *ch);
+void glue_freeChannel(Channel *ch);
 
 /* cloneChannel
  * Make an exact copy of Channel *ch. */
 
-int glue_cloneChannel(class Channel *ch);
+int glue_cloneChannel(Channel *ch);
 
 /* toggle/set*
  * Toggle or set several channel properties. If gui == true the signal comes
  * from a manual interaction on the GUI, otherwise it's a MIDI/Jack/external
  * signal. */
 
-void glue_toggleArm(class Channel *ch, bool gui=true);
-void glue_setChanVol(class Channel *ch, float v, bool gui=true);
-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);
+void glue_toggleArm(Channel *ch, bool gui=true);
+void glue_toggleInputMonitor(Channel *ch);
+void glue_setMute(Channel *ch, bool gui=true);
+void glue_setSoloOn (Channel *ch, bool gui=true);
+void glue_setSoloOff(Channel *ch, bool gui=true);
+void glue_setVolume(Channel *ch, float v, bool gui=true, bool editor=false);
 
-void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val,
-  bool numeric);
+/* TODO move to glue_sampleEditor */
+void glue_setPitch(SampleChannel *ch, float val);
 
-void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val);
+/* TODO move to glue_sampleEditor */
+void glue_setPanning(SampleChannel *ch, float val);
 
 /* setBeginEndChannel
  * sets start/end points in the sample editor. Recalc=false: don't recalc
  * internal position. check=true: check the points' consistency */
 
-void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch,
-  int b, int e, bool recalc=false, bool check=true);
-
-void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val,
-  bool numeric);
+/* TODO move to glue_sampleEditor */
+void glue_setBeginEndChannel(SampleChannel *ch, int b, int e);
 
-/* setVolEditor
- * handles the volume inside the SAMPLE EDITOR (not the main gui). The
- * numeric flag tells if we want to handle the dial or the numeric input
- * field. */
+/* TODO move to glue_sampleEditor */
+void glue_setBoost(SampleChannel *ch, float val);
 
-void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val,
-  bool numeric);
+/* start/stopReadingRecs
+Handles the 'R' button. If gui == true the signal comes from an user interaction
+on the GUI, otherwise it's a MIDI/Jack/external signal. */
 
+void glue_startStopReadingRecs(SampleChannel *ch, bool gui=true);
+void glue_startReadingRecs    (SampleChannel *ch, bool gui=true);
+void glue_stopReadingRecs     (SampleChannel *ch, bool gui=true);
 
 #endif
index 4dc9acfd83490d551191959a2fcc838481feb890..31d678121175d3618f2b10ed169913194d5580b6 100644 (file)
@@ -2,16 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../utils/gui.h"
 #include "../utils/log.h"
 #include "../core/recorder.h"
+#include "../core/kernelAudio.h"
 #include "../core/mixer.h"
 #include "../core/mixerHandler.h"
 #include "../core/wave.h"
 #include "../core/channel.h"
+#include "../core/clock.h"
 #include "../core/sampleChannel.h"
 #include "../core/midiChannel.h"
 #include "main.h"
 #include "channel.h"
+#include "transport.h"
 #include "io.h"
 
 
-extern Recorder                         G_Recorder;
-extern KernelAudio   G_KernelAudio;
-extern bool                             G_audio_status;
-extern Mixer                    G_Mixer;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
 void glue_keyPress(Channel *ch, bool ctrl, bool shift)
 {
        if (ch->type == CHANNEL_SAMPLE)
@@ -91,7 +86,7 @@ void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift)
        if (shift)
                ch->kill(0);        // on frame 0: user-generated event
        else
-               ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true); // on frame 0: user-generated event
+               ch->start(0, true, clock::getQuantize(), clock::isRunning(), false, true); // on frame 0: user-generated event
 }
 
 
@@ -117,18 +112,20 @@ void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
 
        else
        if (shift) {
-               if (G_Recorder.active) {
-                       if (G_Mixer.running) {
+               if (recorder::active) {
+                       if (clock::isRunning()) {
                                ch->kill(0); // on frame 0: user-generated event
-                               if (G_Recorder.canRec(ch, &G_Mixer) && !(ch->mode & LOOP_ANY)) {   // don't record killChan actions for LOOP channels
-                                       G_Recorder.rec(ch->index, ACTION_KILLCHAN, G_Mixer.currentFrame);
+                               if (recorder::canRec(ch, clock::isRunning(), mixer::recording) &&
+            !(ch->mode & LOOP_ANY))
+        {   // don't record killChan actions for LOOP channels
+                                       recorder::rec(ch->index, G_ACTION_KILL, clock::getCurrentFrame());
           ch->hasActions = true;
         }
                        }
                }
                else {
                        if (ch->hasActions) {
-                               if (G_Mixer.running || ch->status == STATUS_OFF)
+                               if (clock::isRunning() || ch->status == STATUS_OFF)
                                        ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
                                else
                                        ch->kill(0);  // on frame 0: user-generated event
@@ -143,17 +140,17 @@ void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
                 * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are
                 * meaningless for loop modes */
 
-               if (G_Mixer.quantize == 0            &&
-                   G_Recorder.canRec(ch, &G_Mixer)  &&
+               if (clock::getQuantize() == 0 &&
+                   recorder::canRec(ch, clock::isRunning(), mixer::recording) &&
              !(ch->mode & LOOP_ANY))
                {
                        if (ch->mode == SINGLE_PRESS) {
-                               G_Recorder.startOverdub(ch->index, ACTION_KEYS, G_Mixer.currentFrame,
-          G_KernelAudio.realBufsize);
+                               recorder::startOverdub(ch->index, G_ACTION_KEYS, clock::getCurrentFrame(),
+          kernelAudio::getRealBufSize());
         ch->readActions = false;   // don't read actions while overdubbing
       }
                        else {
-                               G_Recorder.rec(ch->index, ACTION_KEYPRESS, G_Mixer.currentFrame);
+                               recorder::rec(ch->index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
         ch->hasActions = true;
 
         /* Why return here? You record an action (as done on line 148) and then
@@ -171,7 +168,7 @@ void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
 
                /* This is a user-generated event, so it's on frame 0 */
 
-               ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true);
+               ch->start(0, true, clock::getQuantize(), clock::isRunning(), false, true);
        }
 
        /* the GUI update is done by gui_refresh() */
@@ -191,8 +188,9 @@ void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift)
        /* record a key release only if channel is single_press. For any
         * other mode the KEY REL is meaningless. */
 
-       if (ch->mode == SINGLE_PRESS && G_Recorder.canRec(ch, &G_Mixer))
-               G_Recorder.stopOverdub(&G_Mixer);
+       if (ch->mode == SINGLE_PRESS && recorder::canRec(ch, clock::isRunning(), mixer::recording))
+               recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+      &mixer::mutex_recs);
 
        /* the GUI update is done by gui_refresh() */
 
@@ -204,7 +202,7 @@ void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift)
 
 void glue_startStopActionRec(bool gui)
 {
-       G_Recorder.active ? glue_stopActionRec(gui) : glue_startActionRec(gui);
+       recorder::active ? glue_stopActionRec(gui) : glue_startActionRec(gui);
 }
 
 
@@ -213,12 +211,12 @@ void glue_startStopActionRec(bool gui)
 
 void glue_startActionRec(bool gui)
 {
-       if (G_audio_status == false)
+       if (kernelAudio::getStatus() == false)
                return;
 
-       G_Recorder.active = true;
+       recorder::active = true;
 
-       if (!G_Mixer.running)
+       if (!clock::isRunning())
                glue_startSeq(false);  // update gui ayway
 
        if (!gui) {
@@ -236,14 +234,14 @@ void glue_stopActionRec(bool gui)
 {
        /* stop the recorder and sort new actions */
 
-       G_Recorder.active = false;
-       G_Recorder.sortActions();
+       recorder::active = false;
+       recorder::sortActions();
 
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++)
+       for (unsigned i=0; i<mixer::channels.size(); i++)
        {
-               if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+               if (mixer::channels.at(i)->type == CHANNEL_MIDI)
                        continue;
-               SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+               SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
                G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel);
                if (!ch->readActions && ch->hasActions)
                        glue_startReadingRecs(ch, false);
@@ -264,7 +262,7 @@ void glue_stopActionRec(bool gui)
 
 void glue_startStopInputRec(bool gui)
 {
-       if (G_Mixer.recording)
+       if (mixer::recording)
                glue_stopInputRec(gui);
        else
        if (!glue_startInputRec(gui))
@@ -277,17 +275,17 @@ void glue_startStopInputRec(bool gui)
 
 int glue_startInputRec(bool gui)
 {
-       if (G_audio_status == false)
+       if (kernelAudio::getStatus() == false)
                return false;
 
-       if (!mh_startInputRec()) {
+       if (!mh::startInputRec()) {
          Fl::lock();
          G_MainWin->mainTransport->updateRecInput(0);  // set it off, anyway
                Fl::unlock();
                return false;
        }
 
-       if (!G_Mixer.running)
+       if (!clock::isRunning())
                glue_startSeq(false); // update gui anyway
 
   Fl::lock();
@@ -299,8 +297,8 @@ int glue_startInputRec(bool gui)
   /* Update sample name inside sample channels' main button. This is useless for
   midi channel, but let's do it anyway. */
 
-  for (unsigned i=0; i<G_Mixer.channels.size(); i++)
-    G_Mixer.channels.at(i)->guiChannel->update();
+  for (unsigned i=0; i<mixer::channels.size(); i++)
+    mixer::channels.at(i)->guiChannel->update();
 
        return true;
 }
@@ -311,19 +309,20 @@ int glue_startInputRec(bool gui)
 
 int glue_stopInputRec(bool gui)
 {
-       mh_stopInputRec();
+       mh::stopInputRec();
 
        /* Start all sample channels in loop mode that were armed, i.e. that were
        recording stuff and not yet in play. They are also started in force mode, i.e.
   they must start playing right away at the current frame, not at the next first
   beat. */
 
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               if (mixer::channels.at(i)->type == CHANNEL_MIDI)
                        continue;
-               SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+               SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
                if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->armed)
-                       ch->start(G_Mixer.currentFrame, true, G_Mixer.quantize, G_Mixer.running, true, true);
+                       ch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
+        clock::isRunning(), true, true);
        }
 
   Fl::lock();
index b56160fe2e7e22904476a400dcf57e0e01e9c3f4..110d5f239dcdf9a0319d2468f86dbcda702fbace 100644 (file)
@@ -11,7 +11,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -32,8 +32,8 @@
  * -------------------------------------------------------------------------- */
 
 
-#ifndef GLUE_IO_H
-#define GLUE_IO_H
+#ifndef G_GLUE_IO_H
+#define G_GLUE_IO_H
 
 
 /* keyPress / keyRelease
index d9033d05f263e43566e22270ca464ca08a899d47..f3bd26b2c372c89a00f65f0abd7470e7530d8db9 100644 (file)
@@ -2,16 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include <cmath>
-#include "../gui/elems/ge_waveform.h"
-#include "../gui/elems/ge_mixed.h"
-#include "../gui/elems/ge_waveTools.h"
-#include "../gui/elems/mainWindow/mainTransport.h"
+#include <FL/Fl.H>
 #include "../gui/elems/mainWindow/mainIO.h"
 #include "../gui/elems/mainWindow/mainTimer.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
 #include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
 #include "../gui/elems/mainWindow/keyboard/keyboard.h"
 #include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_editor.h"
-#include "../gui/dialogs/gd_warnings.h"
 #include "../utils/gui.h"
-#include "../utils/fs.h"
+#include "../utils/string.h"
 #include "../utils/log.h"
 #include "../core/mixerHandler.h"
 #include "../core/mixer.h"
-#include "../core/recorder.h"
-#include "../core/wave.h"
-#include "../core/pluginHost.h"
-#include "../core/channel.h"
-#include "../core/sampleChannel.h"
 #include "../core/midiChannel.h"
+#include "../core/clock.h"
 #include "../core/kernelMidi.h"
-#include "../core/patch_DEPR_.h"
+#include "../core/kernelAudio.h"
 #include "../core/conf.h"
+#ifdef WITH_VST
+#include "../core/pluginHost.h"
+#endif
 #include "main.h"
 
 
 extern gdMainWindow *G_MainWin;
-extern Mixer                    G_Mixer;
-extern Recorder                         G_Recorder;
-extern KernelAudio   G_KernelAudio;
-extern KernelMidi    G_KernelMidi;
-extern Patch_DEPR_   G_Patch_DEPR_;
-extern Conf                             G_Conf;
-extern bool                             G_audio_status;
-#ifdef WITH_VST
-extern PluginHost    G_PluginHost;
-#endif
+
+
+using namespace giada::m;
 
 
 void glue_setBpm(const char *v1, const char *v2)
 {
   /* Never change this stuff while recording audio */
 
-  if (G_Mixer.recording)
+  if (mixer::recording)
     return;
 
-       char  buf[6];
-       float value = atof(v1) + (atof(v2)/10);
-       if (value < 20.0f)      {
-               value = 20.0f;
-               sprintf(buf, "20.0");
+       char  bpmS[6];
+       float bpmF = atof(v1) + (atof(v2)/10);
+       if (bpmF < 20.0f) {
+               bpmF = 20.0f;
+               sprintf(bpmS, "20.0");
        }
        else
-               sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
+               sprintf(bpmS, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
 
        /* a value such as atof("120.1") will never be 120.1 but 120.0999999,
         * because of the rounding error. So we pass the real "wrong" value to
         * G_Mixer and we show the nice looking (but fake) one to the GUI. */
 
-       float old_bpm = G_Mixer.bpm;
-       G_Mixer.bpm = value;
-       G_Mixer.updateFrameBars();
+       float oldBpmF = clock::getBpm();
+       clock::setBpm(bpmF);
+  recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto());
 
-       /* inform recorder and actionEditor of the change */
+#ifdef __linux__
+  kernelAudio::jackSetBpm(clock::getBpm());
+#endif
 
-       G_Recorder.updateBpm(old_bpm, value, G_Mixer.quanto);
-       gu_refreshActionEditor();
+  gu_refreshActionEditor();
+  G_MainWin->mainTimer->setBpm(bpmS);
 
-       G_MainWin->mainTimer->setBpm(buf);
-       gu_log("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm);
+       gu_log("[glue] Bpm changed to %s (real=%f)\n", bpmS, clock::getBpm());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setBeats(int beats, int bars, bool expand)
+void glue_setBpm(float v)
 {
-  /* Never change this stuff while recording audio */
-
-  if (G_Mixer.recording)
-    return;
-
-       /* Temp vars to store old data (they are necessary) */
-
-       int      oldvalue = G_Mixer.beats;
-       unsigned oldfpb         = G_Mixer.totalFrames;
-
-       if (beats > MAX_BEATS)
-               G_Mixer.beats = MAX_BEATS;
-       else if (beats < 1)
-               G_Mixer.beats = 1;
-       else
-               G_Mixer.beats = beats;
-
-       /* update bars - bars cannot be greate than beats and must be a sub
-        * multiple of beats. If not, approximation to the nearest (and greater)
-        * value available. */
-
-       if (bars > G_Mixer.beats)
-               G_Mixer.bars = G_Mixer.beats;
-       else if (bars <= 0)
-               G_Mixer.bars = 1;
-       else if (beats % bars != 0) {
-               G_Mixer.bars = bars + (beats % bars);
-               if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it)
-                       G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars);
-       }
-       else
-               G_Mixer.bars = bars;
-
-       G_Mixer.updateFrameBars();
-
-       /* update recorded actions */
-
-       if (expand) {
-               if (G_Mixer.beats > oldvalue)
-                       G_Recorder.expand(oldfpb, G_Mixer.totalFrames);
-               //else if (G_Mixer.beats < oldvalue)
-               //      G_Recorder.shrink(G_Mixer.totalFrames);
-       }
-
-       G_MainWin->mainTimer->setMeter(G_Mixer.beats, G_Mixer.bars);
-       gu_refreshActionEditor();  // in case the action editor is open
+  if (v < G_MIN_BPM || v > G_MAX_BPM)
+    v = G_DEFAULT_BPM;
+  double fIpart;
+  double fPpart = modf(v, &fIpart);
+  int iIpart = fIpart;
+  int iPpart = ceilf(fPpart);
+  glue_setBpm(gu_itoa(iIpart).c_str(), gu_itoa(iPpart).c_str());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_startStopSeq(bool gui)
+void glue_setBeats(int beats, int bars, bool expand)
 {
-       G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui);
-}
+  /* Never change this stuff while recording audio */
 
+  if (mixer::recording)
+    return;
 
-/* -------------------------------------------------------------------------- */
+       /* Temp vars to store old data (they are necessary) */
 
+       int oldBeats = clock::getBeats();
+       unsigned oldTotalFrames = clock::getTotalFrames();
 
-void glue_startSeq(bool gui)
-{
-       G_Mixer.running = true;
+       clock::setBeats(beats);
+       clock::setBars(bars);
+       clock::updateFrameBars();
 
-       if (gui) {
-#ifdef __linux__
-               G_KernelAudio.jackStart();
-#endif
-       }
+       /* Update recorded actions, if 'expand' required and an expansion is taking
+  place. */
 
-       if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
-               G_KernelMidi.send(MIDI_START, -1, -1);
-               G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
-       }
+       if (expand && clock::getBeats() > oldBeats)
+               recorder::expand(oldTotalFrames, clock::getTotalFrames());
 
-       if (!gui) {
-    Fl::lock();
-    G_MainWin->mainTransport->updatePlay(1);
-    Fl::unlock();
-  }
+       G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
+       gu_refreshActionEditor();  // in case the action editor is open
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_stopSeq(bool gui)
+void glue_rewindSeq(bool gui, bool notifyJack)
 {
-       mh_stopSequencer();
+       mh::rewindSequencer();
 
-       if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
-               G_KernelMidi.send(MIDI_STOP, -1, -1);
+  /* FIXME - potential desync when Quantizer is enabled from this point on.
+  Mixer would wait, while the following calls would be made regardless of its
+  state. */
 
 #ifdef __linux__
-       if (gui)
-               G_KernelAudio.jackStop();
+  if (notifyJack)
+         kernelAudio::jackSetPosition(0);
 #endif
 
-       /* what to do if we stop the sequencer and some action recs are active?
-        * Deactivate the button and delete any 'rec on' status */
-
-       if (G_Recorder.active) {
-               G_Recorder.active = false;
-    Fl::lock();
-         G_MainWin->mainTransport->updateRecAction(0);
-         Fl::unlock();
-       }
-
-       /* if input recs are active (who knows why) we must deactivate them.
-        * One might stop the sequencer while an input rec is running. */
-
-       if (G_Mixer.recording) {
-               mh_stopInputRec();
-    Fl::lock();
-         G_MainWin->mainTransport->updateRecInput(0);
-         Fl::unlock();
-       }
-
-       if (!gui) {
-    Fl::lock();
-         G_MainWin->mainTransport->updatePlay(0);
-         Fl::unlock();
-  }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_rewindSeq()
-{
-       mh_rewindSequencer();
-       if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
-               G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopReadingRecs(SampleChannel *ch, bool gui)
-{
-       /* When you call glue_startReadingRecs with G_Conf.treatRecsAsLoops, the
-       member value ch->readActions actually is not set to true immediately, because
-       the channel is in wait mode (REC_WAITING). ch->readActions will become true on
-       the next first beat. So a 'stop rec' command should occur also when
-       ch->readActions is false but the channel is in wait mode; this check will
-       handle the case of when you press 'R', the channel goes into REC_WAITING and
-       then you press 'R' again to undo the status. */
-
-       if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING))
-               glue_stopReadingRecs(ch, gui);
-       else
-               glue_startReadingRecs(ch, gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startReadingRecs(SampleChannel *ch, bool gui)
-{
-       if (G_Conf.treatRecsAsLoops)
-               ch->recStatus = REC_WAITING;
-       else
-               ch->setReadActions(true, G_Conf.recsStopOnChanHalt);
-       if (!gui) {
-               Fl::lock();
-               ((geSampleChannel*)ch->guiChannel)->readActions->value(1);
-               Fl::unlock();
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_stopReadingRecs(SampleChannel *ch, bool gui)
-{
-       /* First of all, if the mixer is not running just stop and disable everything.
-       Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
-       channel in REC_ENDING status. */
-
-       if (!G_Mixer.running) {
-               ch->recStatus = REC_STOPPED;
-               ch->readActions = false;
-       }
-       else
-       if (G_Conf.treatRecsAsLoops)
-               ch->recStatus = REC_ENDING;
-       else
-               ch->setReadActions(false, G_Conf.recsStopOnChanHalt);
-
-       if (!gui) {
-               Fl::lock();
-               ((geSampleChannel*)ch->guiChannel)->readActions->value(0);
-               Fl::unlock();
-       }
+       if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+               kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
 }
 
 
@@ -321,8 +160,7 @@ void glue_stopReadingRecs(SampleChannel *ch, bool gui)
 
 void glue_quantize(int val)
 {
-       G_Mixer.quantize = val;
-       G_Mixer.updateQuanto();
+       clock::setQuantize(val);
 }
 
 
@@ -331,7 +169,7 @@ void glue_quantize(int val)
 
 void glue_setOutVol(float v, bool gui)
 {
-       G_Mixer.outVol = v;
+       mixer::outVol = v;
        if (!gui) {
                Fl::lock();
                G_MainWin->mainIO->setOutVol(v);
@@ -345,7 +183,7 @@ void glue_setOutVol(float v, bool gui)
 
 void glue_setInVol(float v, bool gui)
 {
-       G_Mixer.inVol = v;
+       mixer::inVol = v;
        if (!gui) {
                Fl::lock();
                G_MainWin->mainIO->setInVol(v);
@@ -359,12 +197,12 @@ void glue_setInVol(float v, bool gui)
 
 void glue_clearAllSamples()
 {
-       G_Mixer.running = false;
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               G_Mixer.channels.at(i)->empty();
-               G_Mixer.channels.at(i)->guiChannel->reset();
+       clock::stop();
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               mixer::channels.at(i)->empty();
+               mixer::channels.at(i)->guiChannel->reset();
        }
-       G_Recorder.init();
+       recorder::init();
        return;
 }
 
@@ -374,7 +212,7 @@ void glue_clearAllSamples()
 
 void glue_clearAllRecs()
 {
-       G_Recorder.init();
+       recorder::init();
        gu_updateControls();
 }
 
@@ -384,12 +222,11 @@ void glue_clearAllRecs()
 
 void glue_resetToInitState(bool resetGui, bool createColumns)
 {
-       G_Patch_DEPR_.setDefault();
-       G_Mixer.close();
-       G_Mixer.init();
-       G_Recorder.init();
+       mixer::close();
+       mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+       recorder::init();
 #ifdef WITH_VST
-       G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
+       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
 #endif
 
        G_MainWin->keyboard->clear();
@@ -406,29 +243,15 @@ void glue_resetToInitState(bool resetGui, bool createColumns)
 /* -------------------------------------------------------------------------- */
 
 
-void glue_startStopMetronome(bool gui)
-{
-       G_Mixer.metronome = !G_Mixer.metronome;
-       if (!gui) {
-               Fl::lock();
-               G_MainWin->mainTransport->updateMetronome(G_Mixer.metronome);
-               Fl::unlock();
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 /* never expand or shrink recordings (last param of setBeats = false):
  * this is live manipulation */
 
 void glue_beatsMultiply()
 {
-       glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false);
+       glue_setBeats(clock::getBeats() * 2, clock::getBars(), false);
 }
 
 void glue_beatsDivide()
 {
-       glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false);
+       glue_setBeats(clock::getBeats() / 2, clock::getBars(), false);
 }
index 422f422c3be304702d12baa0dc10c7450021592f..8357e28e38d3d9183785dff060da88f2869d742e 100644 (file)
@@ -1,4 +1,4 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
@@ -9,9 +9,9 @@
  * new action will ever be called via MIDI or keyboard/mouse. If yes,
  * put it here.
  *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
-#ifndef GLUE_H
-#define GLUE_H
+#ifndef G_GLUE_MAIN_H
+#define G_GLUE_MAIN_H
 
 
 void glue_setBpm(const char *v1, const char *v2);
+void glue_setBpm(float v);
 void glue_setBeats(int beats, int bars, bool expand);
-
-/* start, stop, rewind sequencer
-If gui == true the signal comes from an user interaction on the GUI,
-otherwise it's a MIDI/Jack/external signal. */
-
-void glue_startStopSeq(bool gui=true);
-void glue_startSeq    (bool gui=true);
-void glue_stopSeq     (bool gui=true);
-void glue_rewindSeq   ();
-
-/* start/stopReadingRecs
-Handles the 'R' button. If gui == true the signal comes from an user interaction
-on the GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true);
-void glue_startReadingRecs    (class SampleChannel *ch, bool gui=true);
-void glue_stopReadingRecs     (class SampleChannel *ch, bool gui=true);
-
 void glue_quantize(int val);
-
-void glue_setOutVol (float v, bool gui=true);
-void glue_setInVol  (float v, bool gui=true);
-
+void glue_setOutVol(float v, bool gui=true);
+void glue_setInVol(float v, bool gui=true);
 void glue_clearAllSamples();
 void glue_clearAllRecs();
 
@@ -70,8 +51,6 @@ void glue_clearAllRecs();
 
 void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
 
-void glue_startStopMetronome(bool gui=true);
-
 /* beatsDivide/Multiply
  * shrinks or enlarges the number of beats by 2. */
 
index 8c4ee23a89282e1d6e1e30af29d62ee991f24d76..d8320c6a856e5f52e12cd2c23e9db92ce0afb2bc 100644 (file)
@@ -2,12 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "plugin.h"
 
 
-extern Mixer       G_Mixer;
-extern PluginHost G_PluginHost;
+using namespace giada::m;
 
 
 Plugin *glue_addPlugin(Channel *ch, int index, int stackType)
 {
-  if (index >= G_PluginHost.countAvailablePlugins())
+  if (index >= pluginHost::countAvailablePlugins())
     return nullptr;
 
-  return G_PluginHost.addPlugin(index, stackType,  &G_Mixer.mutex_plugins, ch);
+  return pluginHost::addPlugin(index, stackType,  &mixer::mutex_plugins, ch);
 }
 
 
@@ -56,7 +52,7 @@ Plugin *glue_addPlugin(Channel *ch, int index, int stackType)
 
 void glue_swapPlugins(Channel *ch, int index1, int index2, int stackType)
 {
-  G_PluginHost.swapPlugin(index1, index2, stackType, &G_Mixer.mutex_plugins,
+  pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex_plugins,
     ch);
 }
 
@@ -66,7 +62,7 @@ void glue_swapPlugins(Channel *ch, int index1, int index2, int stackType)
 
 void glue_freePlugin(Channel *ch, int index, int stackType)
 {
-  G_PluginHost.freePlugin(index, stackType, &G_Mixer.mutex_plugins, ch);
+  pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch);
 }
 
 
index 02e8278328cb4d4920d2493ba41d6e1fccb35921..9f95d0c91e3400b305c51e373283fd6f78e964e3 100644 (file)
@@ -7,7 +7,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -28,8 +28,8 @@
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __GLUE_PLUGIN_H__
-#define __GLUE_PLUGIN_H__
+#ifndef G_GLUE_PLUGIN_H
+#define G_GLUE_PLUGIN_H
 
 
 #ifdef WITH_VST
diff --git a/src/glue/recorder.cpp b/src/glue/recorder.cpp
new file mode 100644 (file)
index 0000000..2bab39c
--- /dev/null
@@ -0,0 +1,99 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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/dialogs/gd_warnings.h"
+#include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "../core/const.h"
+#include "../core/channel.h"
+#include "../core/recorder.h"
+#include "../utils/gui.h"
+#include "recorder.h"
+
+
+using namespace giada::m;
+
+
+namespace
+{
+void updateChannel(geChannel *gch)
+{
+  gch->ch->hasActions = recorder::hasActions(gch->ch->index);
+  if (gch->ch->type == CHANNEL_SAMPLE && !gch->ch->hasActions)
+    static_cast<geSampleChannel*>(gch)->hideActionButton();
+  /* TODO - set mute=false */
+  gu_refreshActionEditor(); // refresh a.editor window, it could be open
+}
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllActions(geChannel *gch)
+{
+  if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+    return;
+  recorder::clearChan(gch->ch->index);
+  updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearVolumeActions(geChannel *gch)
+{
+  if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+    return;
+  recorder::clearAction(gch->ch->index, G_ACTION_VOLUME);
+  updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearStartStopActions(geChannel *gch)
+{
+  if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+    return;
+  recorder::clearAction(gch->ch->index, G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL);
+  updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearMuteActions(geChannel *gch)
+{
+  if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
+    return;
+  recorder::clearAction(gch->ch->index, G_ACTION_MUTEON | G_ACTION_MUTEOFF);
+  updateChannel(gch);
+}
diff --git a/src/glue/recorder.h b/src/glue/recorder.h
new file mode 100644 (file)
index 0000000..390f7fb
--- /dev/null
@@ -0,0 +1,41 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_GLUE_RECORDER_H
+#define G_GLUE_RECORDER_H
+
+
+class geChannel;
+
+
+void glue_clearAllActions(geChannel *gch);
+void glue_clearMuteActions(geChannel *gch);
+void glue_clearVolumeActions(geChannel *gch);
+void glue_clearStartStopActions(geChannel *gch);
+
+
+#endif
index 997389a5bd5585e0426cc7e63cb1ea6010788bd4..61a3c393f4f28d67fc747165e8ab6916d24dafa1 100644 (file)
@@ -2,12 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../core/plugin.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/clock.h"
 #include "../core/wave.h"
 #include "../utils/gui.h"
 #include "../utils/log.h"
+#include "../utils/fs.h"
+#include "../gui/elems/basics/progress.h"
 #include "../gui/elems/mainWindow/keyboard/column.h"
 #include "../gui/elems/mainWindow/keyboard/keyboard.h"
 #include "../gui/dialogs/gd_mainWindow.h"
 #include "../gui/dialogs/gd_warnings.h"
-#include "../gui/dialogs/gd_browser.h"
-#include "main.h" // TODO - remove, used only for DEPR calls
+#include "../gui/dialogs/browser/browserSave.h"
+#include "../gui/dialogs/browser/browserLoad.h"
+#include "main.h"
 #include "channel.h"
 #include "storage.h"
 
 
-using std::string;
-using std::vector;
+extern gdMainWindow *G_MainWin;
 
 
-extern gdMainWindow *G_MainWin;
-extern Mixer                    G_Mixer;
-extern Recorder                         G_Recorder;
-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
+using std::string;
+using std::vector;
+using namespace giada::m;
 
 
 #ifdef WITH_VST
 
-static void __glue_fillPatchGlobalsPlugins__(vector <Plugin *> *host, vector<Patch::plugin_t> *patch)
+static void __glue_fillPatchGlobalsPlugins__(vector <Plugin *> *host, vector<patch::plugin_t> *patch)
 {
        for (unsigned i=0; i<host->size(); i++) {
                Plugin *pl = host->at(i);
-               Patch::plugin_t ppl;
+               patch::plugin_t ppl;
                ppl.path = pl->getUniqueId();
                ppl.bypass = pl->isBypassed();
                int numParams = pl->getNumParameters();
@@ -92,20 +85,20 @@ static void __glue_fillPatchColumns__()
 {
        for (unsigned i=0; i<G_MainWin->keyboard->getTotalColumns(); i++) {
                geColumn *gCol = G_MainWin->keyboard->getColumn(i);
-               Patch::column_t pCol;
+               patch::column_t pCol;
                pCol.index = gCol->getIndex();
                pCol.width = gCol->w();
                for (int 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);
+                       for (unsigned j=0; j<mixer::channels.size(); j++) {
+                               Channel *mixerChannel = mixer::channels.at(j);
                                if (colChannel == mixerChannel) {
                                        pCol.channels.push_back(mixerChannel->index);
                                        break;
                                }
                        }
                }
-               G_Patch.columns.push_back(pCol);
+               patch::columns.push_back(pCol);
        }
 }
 
@@ -115,8 +108,8 @@ static void __glue_fillPatchColumns__()
 
 static void __glue_fillPatchChannels__(bool isProject)
 {
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-               G_Mixer.channels.at(i)->writePatch(i, isProject, &G_Patch);
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
+               mixer::channels.at(i)->writePatch(i, isProject);
        }
 }
 
@@ -126,25 +119,25 @@ static void __glue_fillPatchChannels__(bool isProject)
 
 static 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;
+       patch::version      = G_VERSION_STR;
+       patch::versionMajor = G_VERSION_MAJOR;
+       patch::versionMinor = G_VERSION_MINOR;
+       patch::versionPatch = G_VERSION_PATCH;
+       patch::name         = name;
+       patch::bpm          = clock::getBpm();
+       patch::bars         = clock::getBars();
+       patch::beats        = clock::getBeats();
+       patch::quantize     = clock::getQuantize();
+       patch::masterVolIn  = mixer::inVol;
+  patch::masterVolOut = mixer::outVol;
+  patch::metronome    = mixer::metronome;
 
 #ifdef WITH_VST
 
-       __glue_fillPatchGlobalsPlugins__(G_PluginHost.getStack(PluginHost::MASTER_IN),
-                       &G_Patch.masterInPlugins);
-       __glue_fillPatchGlobalsPlugins__(G_PluginHost.getStack(PluginHost::MASTER_OUT),
-                       &G_Patch.masterOutPlugins);
+       __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_IN),
+                       &patch::masterInPlugins);
+       __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_OUT),
+                       &patch::masterOutPlugins);
 
 #endif
 }
@@ -156,13 +149,13 @@ static void __glue_fillPatchGlobals__(const string &name)
 static bool __glue_savePatch__(const string &fullPath, const string &name,
                bool isProject)
 {
-       G_Patch.init();
+       patch::init();
 
        __glue_fillPatchGlobals__(name);
        __glue_fillPatchChannels__(isProject);
        __glue_fillPatchColumns__();
 
-       if (G_Patch.write(fullPath)) {
+       if (patch::write(fullPath)) {
                gu_updateMainWinLabel(name);
                gu_log("[glue_savePatch] patch saved as %s\n", fullPath.c_str());
                return true;
@@ -176,7 +169,7 @@ static bool __glue_savePatch__(const string &fullPath, const string &name,
 
 void glue_savePatch(void *data)
 {
-       gdSaveBrowser *browser = (gdSaveBrowser*) data;
+       gdBrowserSave *browser = (gdBrowserSave*) data;
        string name            = browser->getName();
        string fullPath        = browser->getCurrentPath() + G_SLASH + gu_stripExt(name) + ".gptc";
 
@@ -190,7 +183,7 @@ void glue_savePatch(void *data)
                        return;
 
        if (__glue_savePatch__(fullPath, name, false)) {  // false == not a project
-               G_Conf.patchPath = gu_dirname(fullPath);
+               conf::patchPath = gu_dirname(fullPath);
                browser->do_callback();
        }
        else
@@ -203,7 +196,7 @@ void glue_savePatch(void *data)
 
 void glue_loadPatch(void *data)
 {
-       gdLoadBrowser *browser = (gdLoadBrowser*) data;
+       gdBrowserLoad *browser = (gdBrowserLoad*) data;
        string fullPath        = browser->getSelectedItem();
        bool isProject         = gu_isProject(browser->getSelectedItem());
 
@@ -218,34 +211,16 @@ void glue_loadPatch(void *data)
                basePath   = fullPath + G_SLASH;
        }
 
-       /* try to load the new JSON-based patch. If it fails, fall back to deprecated
-       * one. */
-
-       int  res = G_Patch.read(fileToLoad);
-       bool deprecated = false;
-
-       if (res == PATCH_UNREADABLE) {
-               gu_log("[glue] failed reading JSON-based patch. Trying with the deprecated method\n");
-               deprecated = true;
-               res = glue_loadPatch__DEPR__(gu_basename(fileToLoad).c_str(), fileToLoad.c_str(),
-                               browser->getStatusBar(), isProject);
-       }
-
+       int res = patch::read(fileToLoad);
        if (res != PATCH_READ_OK) {
                if (res == PATCH_UNREADABLE)
                        isProject ? gdAlert("This project is unreadable.") : gdAlert("This patch is unreadable.");
                else
                if (res == PATCH_INVALID)
                        isProject ? gdAlert("This project is not valid.") : gdAlert("This patch is not valid.");
-
                browser->hideStatusBar();
                return;
        }
-       else
-       if (deprecated) {
-               browser->do_callback();
-               return;
-       }
 
        /* close all other windows. This prevents segfault if plugin
         * windows GUIs are on. */
@@ -263,40 +238,39 @@ void glue_loadPatch(void *data)
        /* 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);
+       float steps = 0.8 / patch::channels.size();
+       for (unsigned i=0; i<patch::columns.size(); i++) {
+               patch::column_t *col = &patch::columns.at(i);
                G_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, &G_Patch, &G_Mixer.mutex_plugins,
-                                               G_Conf.samplerate, G_Conf.rsmpQuality);
+               for (unsigned k=0; k<patch::channels.size(); k++) {
+                       if (patch::channels.at(k).column == col->index) {
+                               Channel *ch = glue_addChannel(patch::channels.at(k).column,
+                                       patch::channels.at(k).type);
+                               ch->readPatch(basePath, k, &mixer::mutex_plugins, conf::samplerate,
+          conf::rsmpQuality);
                        }
-                       //__glue_setProgressBar__(status, steps);
                        browser->setStatusBar(steps);
                }
        }
 
        /* fill Mixer */
 
-       mh_readPatch();
+       mh::readPatch();
 
        /* let recorder recompute the actions' positions if the current
         * samplerate != patch samplerate */
 
-       G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
+       recorder::updateSamplerate(conf::samplerate, patch::samplerate);
 
        /* save patchPath by taking the last dir of the broswer, in order to
         * reuse it the next time */
 
-       G_Conf.patchPath = gu_dirname(fullPath);
+       conf::patchPath = gu_dirname(fullPath);
 
        /* refresh GUI */
 
        gu_updateControls();
-       gu_updateMainWinLabel(G_Patch.name);
+       gu_updateMainWinLabel(patch::name);
 
        browser->setStatusBar(0.1f);
        //__glue_setProgressBar__(status, 1.0f);
@@ -305,7 +279,7 @@ void glue_loadPatch(void *data)
 
 #ifdef WITH_VST
 
-       if (G_PluginHost.hasMissingPlugins())
+       if (pluginHost::hasMissingPlugins())
                gdAlert("Some plugins were not loaded successfully.\nCheck the plugin browser to know more.");
 
 #endif
@@ -317,98 +291,9 @@ void glue_loadPatch(void *data)
 /* -------------------------------------------------------------------------- */
 
 
-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, gu_dirname(fpath).c_str());
-
-       /* take the patch name and update the main window's title */
-
-       G_Patch_DEPR_.getName();
-       gu_updateMainWinLabel(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 */
-
-       G_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.patchPath = gu_dirname(fpath).c_str();
-
-       gu_log("[glue] patch %s loaded\n", fname);
-
-#ifdef WITH_VST
-       if (resPlugins != 1)
-               gdAlert("Some VST plugins were not loaded successfully.");
-#endif
-
-       gdAlert("This patch is using a deprecated format.\nPlease save it again to store it properly.");
-
-       return res;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void glue_saveProject(void *data)
 {
-       gdSaveBrowser *browser = (gdSaveBrowser*) data;
+       gdBrowserSave *browser = (gdBrowserSave*) data;
        string name            = browser->getName();
        string folderPath      = browser->getCurrentPath(); //browser->getSelectedItem();
        string fullPath        = folderPath + G_SLASH + gu_stripExt(name) + ".gprj";
@@ -431,14 +316,14 @@ void glue_saveProject(void *data)
        /* 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++) {
+       for (unsigned i=0; i<mixer::channels.size(); i++) {
 
-               if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+               if (mixer::channels.at(i)->type == CHANNEL_MIDI)
                        continue;
 
-               SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+               SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
 
-               if (ch->wave == NULL)
+               if (ch->wave == nullptr)
                        continue;
 
                /* update the new samplePath: everything now comes from the project
@@ -465,7 +350,7 @@ void glue_saveProject(void *data)
 
 void glue_loadSample(void *data)
 {
-       gdLoadBrowser *browser = (gdLoadBrowser*) data;
+       gdBrowserLoad *browser = (gdBrowserLoad*) data;
        string fullPath        = browser->getSelectedItem();
 
        if (fullPath.empty())
@@ -474,7 +359,7 @@ void glue_loadSample(void *data)
        int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str());
 
        if (res == SAMPLE_LOADED_OK) {
-               G_Conf.samplePath = gu_dirname(fullPath);
+               conf::samplePath = gu_dirname(fullPath);
                browser->do_callback();
                G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
        }
@@ -488,9 +373,9 @@ void glue_loadSample(void *data)
 
 void glue_saveSample(void *data)
 {
-       gdSaveBrowser *browser = (gdSaveBrowser*) data;
+       gdBrowserSave *browser = (gdBrowserSave*) data;
        string name            = browser->getName();
-       string folderPath      = browser->getSelectedItem();
+       string folderPath      = browser->getCurrentPath();
 
        if (name == "") {
                gdAlert("Please choose a file name.");
@@ -505,8 +390,9 @@ void glue_saveSample(void *data)
                if (!gdConfirmWin("Warning", "File exists: overwrite?"))
                        return;
 
-       if (((SampleChannel*)browser->getChannel())->save(filePath.c_str())) {
-               G_Conf.samplePath = gu_dirname(folderPath);
+       if (static_cast<SampleChannel*>(browser->getChannel())->save(filePath.c_str())) {
+    gu_log("[glue_saveSample] sample saved to %s\n", filePath.c_str());
+               conf::samplePath = gu_dirname(filePath);
                browser->do_callback();
        }
        else
index a95ec0a294c24ddcc6a966951b72f54d27a7a04f..83f92911fe5bd579a63c8663fe5251010e481923 100644 (file)
@@ -2,12 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef GLUE_STORAGE_H
-#define GLUE_STORAGE_H
+#ifndef G_GLUE_STORAGE_H
+#define G_GLUE_STORAGE_H
 
 
 void glue_loadPatch  (void *data);
-int glue_loadPatch__DEPR__(const char *fname, const char *fpath, class gProgress *status, bool isProject);
 void glue_savePatch  (void *data);
 void glue_saveProject(void *data);
 void glue_saveSample (void *data);
diff --git a/src/glue/transport.cpp b/src/glue/transport.cpp
new file mode 100644 (file)
index 0000000..c3bf56e
--- /dev/null
@@ -0,0 +1,120 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../gui/elems/mainWindow/mainTransport.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../core/clock.h"
+#include "../core/kernelAudio.h"
+#include "../core/mixerHandler.h"
+#include "../core/mixer.h"
+#include "../core/recorder.h"
+#include "transport.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+void glue_startStopSeq(bool gui)
+{
+       clock::isRunning() ? glue_stopSeq(gui) : glue_startSeq(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startSeq(bool gui)
+{
+       clock::start();
+
+#ifdef __linux__
+       kernelAudio::jackStart();
+#endif
+
+       if (!gui) {
+    Fl::lock();
+    G_MainWin->mainTransport->updatePlay(1);
+    Fl::unlock();
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopSeq(bool gui)
+{
+       mh::stopSequencer();
+
+#ifdef __linux__
+       kernelAudio::jackStop();
+#endif
+
+       /* what to do if we stop the sequencer and some action recs are active?
+        * Deactivate the button and delete any 'rec on' status */
+
+       if (recorder::active) {
+               recorder::active = false;
+    Fl::lock();
+         G_MainWin->mainTransport->updateRecAction(0);
+         Fl::unlock();
+       }
+
+       /* if input recs are active (who knows why) we must deactivate them.
+        * One might stop the sequencer while an input rec is running. */
+
+       if (mixer::recording) {
+               mh::stopInputRec();
+    Fl::lock();
+         G_MainWin->mainTransport->updateRecInput(0);
+         Fl::unlock();
+       }
+
+       if (!gui) {
+    Fl::lock();
+         G_MainWin->mainTransport->updatePlay(0);
+         Fl::unlock();
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopMetronome(bool gui)
+{
+       mixer::metronome = !mixer::metronome;
+       if (!gui) {
+               Fl::lock();
+               G_MainWin->mainTransport->updateMetronome(mixer::metronome);
+               Fl::unlock();
+       }
+}
diff --git a/src/glue/transport.h b/src/glue/transport.h
new file mode 100644 (file)
index 0000000..24fff6f
--- /dev/null
@@ -0,0 +1,43 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_GLUE_TRANSPORT_H
+#define G_GLUE_TRANSPORT_H
+
+
+/* start, stop, rewind sequencer
+If gui == true the signal comes from an user interaction on the GUI,
+otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopSeq(bool gui=true);
+void glue_startSeq(bool gui=true);
+void glue_stopSeq(bool gui=true);
+void glue_rewindSeq(bool gui=true, bool notifyJack=true);
+void glue_startStopMetronome(bool gui=true);
+
+
+#endif
diff --git a/src/gui/dialogs/browser/browserBase.cpp b/src/gui/dialogs/browser/browserBase.cpp
new file mode 100644 (file)
index 0000000..f9bedb9
--- /dev/null
@@ -0,0 +1,189 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/graphics.h"
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "../../elems/basics/progress.h"
+#include "../../elems/basics/check.h"
+#include "browserBase.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+gdBrowserBase::gdBrowserBase(int x, int y, int w, int h, const string &title,
+               const string &path, void (*callback)(void*))
+       :       gdWindow(x, y, w, h, title.c_str()), callback(callback)
+{
+       set_non_modal();
+
+       groupTop = new Fl_Group(8, 8, w-16, 40);
+    hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
+               where = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 152, 20);
+               updir   = new geButton(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
+       groupTop->end();
+       groupTop->resizable(where);
+
+  hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
+
+       where->readonly(true);
+       where->cursor_color(COLOR_BG_DARK);
+       where->value(path.c_str());
+
+       updir->callback(cb_up, (void*) this);
+
+       browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w-16, h-93);
+       browser->loadDir(path);
+       if (path == conf::browserLastPath)
+               browser->preselect(conf::browserPosition, conf::browserLastValue);
+
+       Fl_Group *groupButtons = new Fl_Group(8, browser->y()+browser->h()+8, w-16, 20);
+               ok        = new geButton(w-88, groupButtons->y(), 80, 20);
+               cancel  = new geButton(w-ok->w()-96, groupButtons->y(), 80, 20, "Cancel");
+               status  = new geProgress(8, groupButtons->y(), cancel->x()-16, 20);
+               status->minimum(0);
+               status->maximum(1);
+               status->hide();   // show the bar only if necessary
+       groupButtons->resizable(status);
+       groupButtons->end();
+
+       end();
+
+       cancel->callback(cb_close, (void*) this);
+
+       resizable(browser);
+       size_range(320, 200);
+
+       gu_setFavicon(this);
+       show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdBrowserBase::~gdBrowserBase()
+{
+       conf::browserX = x();
+       conf::browserY = y();
+       conf::browserW = w();
+       conf::browserH = h();
+       conf::browserPosition = browser->position();
+       conf::browserLastPath = gu_dirname(browser->getSelectedItem());
+       conf::browserLastValue = browser->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::cb_up   (Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_up(); }
+void gdBrowserBase::cb_close(Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_close(); }
+void gdBrowserBase::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_toggleHiddenFiles(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::__cb_up()
+{
+       string dir = browser->getCurrentDir();
+       dir = dir.substr(0, dir.find_last_of(G_SLASH_STR));  // remove up to the next slash
+       browser->loadDir(dir);
+       where->value(browser->getCurrentDir().c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::__cb_close()
+{
+       do_callback();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::__cb_toggleHiddenFiles()
+{
+       browser->toggleHiddenFiles();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::setStatusBar(float v)
+{
+       status->value(status->value() + v);
+       Fl::wait(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::showStatusBar()
+{
+  status->show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::hideStatusBar()
+{
+  status->hide();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserBase::getCurrentPath()
+{
+  return where->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserBase::getSelectedItem()
+{
+       return browser->getSelectedItem();
+}
diff --git a/src/gui/dialogs/browser/browserBase.h b/src/gui/dialogs/browser/browserBase.h
new file mode 100644 (file)
index 0000000..f96dab1
--- /dev/null
@@ -0,0 +1,98 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GD_BROWSER_BASE_H
+#define GD_BROWSER_BASE_H
+
+
+#include "../window.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+class gdBrowserBase : public gdWindow
+{
+protected:
+
+  Channel *channel;
+
+       Fl_Group   *groupTop;
+  geCheck    *hiddenFiles;
+       geBrowser  *browser;
+       geButton   *ok;
+       geButton   *cancel;
+       geInput    *where;
+       geButton   *updir;
+       geProgress *status;
+
+       static void cb_up               (Fl_Widget *v, void *p);
+       static void cb_close            (Fl_Widget *w, void *p);
+       static void cb_toggleHiddenFiles(Fl_Widget *w, void *p);
+
+       inline void __cb_up               ();
+       inline void __cb_close            ();
+       inline void __cb_toggleHiddenFiles();
+
+       /* Callback
+        * Fired when the save/load button is pressed. */
+
+       void (*callback)(void*);
+
+  gdBrowserBase(int x, int y, int w, int h, const std::string &title,
+               const std::string &path,        void (*callback)(void*));
+
+public:
+
+       ~gdBrowserBase();
+
+       /* getSelectedItem
+        * Return the full path of the selected file. */
+
+       std::string getSelectedItem();
+
+       /* setStatusBar
+        * Increment status bar for progress tracking. */
+
+       void setStatusBar(float v);
+
+       void showStatusBar();
+       void hideStatusBar();
+  std::string getCurrentPath();
+
+       Channel *getChannel() { return channel; }
+       void fireCallback()   { callback((void*) this); }
+};
+
+
+#endif
diff --git a/src/gui/dialogs/browser/browserLoad.cpp b/src/gui/dialogs/browser/browserLoad.cpp
new file mode 100644 (file)
index 0000000..4717fdd
--- /dev/null
@@ -0,0 +1,82 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "browserLoad.h"
+
+
+using std::string;
+
+
+gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string &title,
+               const string &path, void (*cb)(void*), Channel *ch)
+       :       gdBrowserBase(x, y, w, h, title, path, cb)
+{
+       channel = ch;
+
+       where->size(groupTop->w()-updir->w()-8, 20);
+
+       browser->callback(cb_down, (void*) this);
+
+       ok->label("Load");
+       ok->callback(cb_load, (void*) this);
+       ok->shortcut(FL_ENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserLoad::cb_load(Fl_Widget *v, void *p) { ((gdBrowserLoad*)p)->__cb_load(); }
+void gdBrowserLoad::cb_down(Fl_Widget *v, void *p) { ((gdBrowserLoad*)p)->__cb_down(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserLoad::__cb_load()
+{
+       callback((void*) this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserLoad::__cb_down()
+{
+       string path = browser->getSelectedItem();
+
+       if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
+               return;
+
+       browser->loadDir(path);
+       where->value(browser->getCurrentDir().c_str());
+}
diff --git a/src/gui/dialogs/browser/browserLoad.h b/src/gui/dialogs/browser/browserLoad.h
new file mode 100644 (file)
index 0000000..b1ad762
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GD_BROWSER_LOAD_H
+#define GD_BROWSER_LOAD_H
+
+
+#include "../window.h"
+#include "browserBase.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+class gdBrowserLoad : public gdBrowserBase
+{
+private:
+
+       static void cb_load(Fl_Widget *w, void *p);
+       static void cb_down(Fl_Widget *v, void *p);
+
+       inline void __cb_load();
+       inline void __cb_down();
+
+public:
+
+       gdBrowserLoad(int x, int y, int w, int h, const std::string &title,
+                       const std::string &path,        void (*callback)(void*), Channel *ch);
+};
+
+
+#endif
diff --git a/src/gui/dialogs/browser/browserSave.cpp b/src/gui/dialogs/browser/browserSave.cpp
new file mode 100644 (file)
index 0000000..7011bc1
--- /dev/null
@@ -0,0 +1,102 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "browserSave.h"
+
+
+using std::string;
+
+
+gdBrowserSave::gdBrowserSave(int x, int y, int w, int h, const string &title,
+               const string &path, const string &_name, void (*cb)(void*), Channel *ch)
+       :       gdBrowserBase(x, y, w, h, title, path, cb)
+{
+       channel = ch;
+
+       where->size(groupTop->w()-236, 20);
+
+       name = new geInput(where->x()+where->w()+8, where->y(), 200, 20);
+       name->value(_name.c_str());
+       groupTop->add(name);
+
+       browser->callback(cb_down, (void*) this);
+
+       ok->label("Save");
+       ok->callback(cb_save, (void*) this);
+       ok->shortcut(FL_ENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserSave::cb_save(Fl_Widget *v, void *p) { ((gdBrowserSave*)p)->__cb_save(); }
+void gdBrowserSave::cb_down(Fl_Widget *v, void *p) { ((gdBrowserSave*)p)->__cb_down(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserSave::__cb_down()
+{
+       string path = browser->getSelectedItem();
+
+       if (path.empty())  // when click on an empty area
+               return;
+
+       /* if the selected item is a directory just load its content. If it's a file
+        * use it as the file name (i.e. fill name->value()). */
+
+       if (gu_isDir(path)) {
+               browser->loadDir(path);
+               where->value(browser->getCurrentDir().c_str());
+       }
+       else
+               name->value(browser->getSelectedItem(false).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserSave::getName()
+{
+  return name->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserSave::__cb_save()
+{
+       callback((void*) this);
+}
diff --git a/src/gui/dialogs/browser/browserSave.h b/src/gui/dialogs/browser/browserSave.h
new file mode 100644 (file)
index 0000000..3b7f818
--- /dev/null
@@ -0,0 +1,67 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GD_BROWSER_SAVE_H
+#define GD_BROWSER_SAVE_H
+
+
+#include "../window.h"
+#include "browserBase.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+class gdBrowserSave : public gdBrowserBase
+{
+private:
+
+       geInput *name;
+
+       static void cb_down(Fl_Widget *v, void *p);
+       static void cb_save(Fl_Widget *w, void *p);
+
+       inline void __cb_down();
+       inline void __cb_save();
+
+public:
+
+       gdBrowserSave(int x, int y, int w, int h, const std::string &title,
+                       const std::string &path,        const std::string &name, void (*callback)(void*),
+                       Channel *ch);
+
+       std::string getName();
+};
+
+
+#endif
index e47076d80d8c680c0d04b088d383bad5d948750b..dd416b6a0920c2a6f968e89baeb6a3f0d24e243f 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_about
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/Fl_Pixmap.H>
+#include <FL/fl_draw.H>
 #include <jansson.h>
 #include "../../core/conf.h"
 #include "../../core/const.h"
-#include "../../core/kernelAudio.h"
-#include "../../core/kernelMidi.h"
 #include "../../core/graphics.h"
 #ifdef WITH_VST
   #include "../../deps/juce-config.h"
 #endif
 #include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
+#include "../../utils/deps.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
 #include "gd_about.h"
 
 
-extern Conf        G_Conf;
-extern KernelAudio G_KernelAudio;
-extern KernelMidi  G_KernelMidi;
+using namespace giada::m;
+using namespace giada::u;
 
 
 gdAbout::gdAbout()
 #ifdef WITH_VST
-: gWindow(340, 435, "About Giada")
+: gdWindow(340, 435, "About Giada")
 {
 #else
-: gWindow(340, 350, "About Giada")
+: gdWindow(340, 350, "About Giada")
 {
 #endif
 
-       if (G_Conf.aboutX)
-               resize(G_Conf.aboutX, G_Conf.aboutY, w(), h());
+       if (conf::aboutX)
+               resize(conf::aboutX, conf::aboutY, w(), h());
 
        set_modal();
 
-       logo  = new gBox(8, 10, 324, 86);
-       text  = new gBox(8, 120, 324, 145);
-       close = new gClick(252, h()-28, 80, 20, "Close");
+       logo  = new geBox(8, 10, 324, 86);
+       text  = new geBox(8, 120, 324, 145);
+       close = new geButton(252, h()-28, 80, 20, "Close");
 #ifdef WITH_VST
-       vstLogo = new gBox(8, 265, 324, 50);
-       vstText = new gBox(8, 315, 324, 46);
+       vstLogo = new geBox(8, 265, 324, 50);
+       vstText = new geBox(8, 315, 324, 46);
 #endif
        end();
 
@@ -79,7 +78,7 @@ gdAbout::gdAbout()
                "Developed by Monocasual\n"
                "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
                "RtMidi (%s), Libsamplerate, Jansson (%s),\n"
-               "Libsndfile"
+               "Libsndfile (%s)"
 #ifdef WITH_VST
                ", JUCE (%d.%d.%d)\n\n"
 #else
@@ -90,9 +89,9 @@ gdAbout::gdAbout()
                "News, infos, contacts and documentation:\n"
                "www.giadamusic.com",
                FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
-               G_KernelAudio.getRtAudioVersion().c_str(),
-               G_KernelMidi.getRtMidiVersion().c_str(),
-               JANSSON_VERSION
+               deps::getRtAudioVersion().c_str(),
+               deps::getRtMidiVersion().c_str(),
+               JANSSON_VERSION, deps::getLibsndfileVersion().c_str()
 #ifdef WITH_VST
                , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER);
 #else
@@ -128,8 +127,8 @@ gdAbout::gdAbout()
 
 gdAbout::~gdAbout()
 {
-       G_Conf.aboutX = x();
-       G_Conf.aboutY = y();
+       conf::aboutX = x();
+       conf::aboutY = y();
 }
 
 
index 2b8b6d333441c276bc5f08eb9a9817d767bab8ba..eeb11d27f355fb193966471e8b0b11a07c99e920 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_about
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #define GD_ABOUT_H
 
 
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geBox;
+class geButton;
 
 
-class gdAbout : public gWindow
+class gdAbout : public gdWindow
 {
 private:
 
-       class gBox       *logo;
-       class gBox       *text;
-       class gClick *close;
+       geBox      *logo;
+       geBox      *text;
+       geButton *close;
 
 #ifdef WITH_VST
-       class gBox  *vstText;
-       class gBox  *vstLogo;
+       geBox  *vstText;
+       geBox  *vstLogo;
 #endif
 
 public:
index cf860be98e600ee75090c5d95d6282447187cd22..6dba08e50daff3d83b44d79b0effe4e9425dfc8b 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_actionEditor
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <math.h>
+#include <cmath>
 #include "../../utils/gui.h"
 #include "../../core/graphics.h"
-#include "../../core/mixer.h"
-#include "../../core/recorder.h"
 #include "../../core/conf.h"
-#include "../../core/channel.h"
+#include "../../core/const.h"
+#include "../../core/clock.h"
 #include "../../core/sampleChannel.h"
-#include "../elems/actionEditor.h"
-#include "../elems/envelopeEditor.h"
-#include "../elems/muteEditor.h"
-#include "../elems/noteEditor.h"
-#include "../elems/ge_mixed.h"
 #include "../elems/basics/scroll.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/resizerBar.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/box.h"
+#include "../elems/actionEditor/actionEditor.h"
+#include "../elems/actionEditor/envelopeEditor.h"
+#include "../elems/actionEditor/muteEditor.h"
+#include "../elems/actionEditor/noteEditor.h"
+#include "../elems/actionEditor/gridTool.h"
 #include "gd_actionEditor.h"
 
 
-extern Mixer G_Mixer;
-extern Conf     G_Conf;
+using namespace giada::m;
 
 
 gdActionEditor::gdActionEditor(Channel *chan)
-       :       gWindow(640, 284),
+       :       gdWindow(640, 284),
                chan   (chan),
                zoom   (100),
                coverX (0)
 {
-       if (G_Conf.actionEditorW) {
-               resize(G_Conf.actionEditorX, G_Conf.actionEditorY, G_Conf.actionEditorW, G_Conf.actionEditorH);
-               zoom = G_Conf.actionEditorZoom;
+       if (conf::actionEditorW) {
+               resize(conf::actionEditorX, conf::actionEditorY, conf::actionEditorW, conf::actionEditorH);
+               zoom = conf::actionEditorZoom;
        }
 
-       totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+       totalWidth = (int) std::ceil(clock::getFramesInSequencer() / (float) zoom);
 
        /* container with zoom buttons and the action type selector. Scheme of
         * the resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
@@ -69,8 +69,8 @@ gdActionEditor::gdActionEditor(Channel *chan)
        upperArea->begin();
 
        if (chan->type == CHANNEL_SAMPLE) {
-         actionType = new gChoice(8, 8, 80, 20);
-         gridTool   = new gGridTool(actionType->x()+actionType->w()+4, 8, this);
+         actionType = new geChoice(8, 8, 80, 20);
+         gridTool   = new geGridTool(actionType->x()+actionType->w()+4, 8, this);
                actionType->add("key press");
                actionType->add("key release");
                actionType->add("kill chan");
@@ -81,12 +81,12 @@ gdActionEditor::gdActionEditor(Channel *chan)
                actionType->deactivate();
        }
        else {
-               gridTool = new gGridTool(8, 8, this);
+               gridTool = new geGridTool(8, 8, this);
        }
 
-               gBox *b1   = new gBox(gridTool->x()+gridTool->w()+4, 8, 300, 20);    // padding actionType - zoomButtons
-               zoomIn     = new gClick(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
-               zoomOut    = new gClick(w()-8-20,   8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+               geBox *b1   = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20);    // padding actionType - zoomButtons
+               zoomIn     = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+               zoomOut    = new geButton(w()-8-20,   8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
        upperArea->end();
        upperArea->resizable(b1);
 
@@ -103,13 +103,13 @@ gdActionEditor::gdActionEditor(Channel *chan)
 
                ac = new geActionEditor  (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
                mc = new geMuteEditor    (scroller->x(), ac->y()+ac->h()+8, this);
-               vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume");
+               vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, G_ACTION_VOLUME, G_RANGE_FLOAT, "volume");
                scroller->add(ac);
-               //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
+               //scroller->add(new geResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
                scroller->add(mc);
-               //scroller->add(new gResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
+               //scroller->add(new geResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
                scroller->add(vc);
-               //scroller->add(new gResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
+               //scroller->add(new geResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
 
                /* fill volume envelope with actions from recorder */
 
@@ -124,7 +124,7 @@ gdActionEditor::gdActionEditor(Channel *chan)
        else {
                pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this);
                scroller->add(pr);
-               scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
+               scroller->add(new geResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
        }
 
        end();
@@ -151,12 +151,13 @@ gdActionEditor::gdActionEditor(Channel *chan)
 /* -------------------------------------------------------------------------- */
 
 
-gdActionEditor::~gdActionEditor() {
-       G_Conf.actionEditorX = x();
-       G_Conf.actionEditorY = y();
-       G_Conf.actionEditorW = w();
-       G_Conf.actionEditorH = h();
-       G_Conf.actionEditorZoom = zoom;
+gdActionEditor::~gdActionEditor()
+{
+       conf::actionEditorX = x();
+       conf::actionEditorY = y();
+       conf::actionEditorW = w();
+       conf::actionEditorH = h();
+       conf::actionEditorZoom = zoom;
 
        /** CHECKME - missing clear() ? */
 
@@ -173,8 +174,8 @@ void gdActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->_
 /* -------------------------------------------------------------------------- */
 
 
-void gdActionEditor::__cb_zoomIn() {
-
+void gdActionEditor::__cb_zoomIn()
+{
        /* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed
         * (32767 max), unsupported by FLTK 1.3.x */
 
@@ -213,8 +214,8 @@ void gdActionEditor::__cb_zoomIn() {
 /* -------------------------------------------------------------------------- */
 
 
-void gdActionEditor::__cb_zoomOut() {
-
+void gdActionEditor::__cb_zoomOut()
+{
        zoom *= 2;
 
        update();
@@ -249,11 +250,12 @@ void gdActionEditor::__cb_zoomOut() {
 /* -------------------------------------------------------------------------- */
 
 
-void gdActionEditor::update() {
-       totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+void gdActionEditor::update()
+{
+       totalWidth = (int) ceilf(clock::getFramesInSequencer() / (float) zoom);
        if (totalWidth < scroller->w()) {
                totalWidth = scroller->w();
-               zoom = (int) ceilf(G_Mixer.framesInSequencer / (float) totalWidth);
+               zoom = (int) ceilf(clock::getFramesInSequencer() / (float) totalWidth);
                scroller->scroll_to(0, scroller->yposition());
        }
 }
@@ -262,7 +264,8 @@ void gdActionEditor::update() {
 /* -------------------------------------------------------------------------- */
 
 
-int gdActionEditor::handle(int e) {
+int gdActionEditor::handle(int e)
+{
        int ret = Fl_Group::handle(e);
        switch (e) {
                case FL_MOUSEWHEEL: {
@@ -278,196 +281,16 @@ int gdActionEditor::handle(int e) {
 /* -------------------------------------------------------------------------- */
 
 
-int gdActionEditor::getActionType() {
+int gdActionEditor::getActionType()
+{
        if (actionType->value() == 0)
-               return ACTION_KEYPRESS;
+               return G_ACTION_KEYPRESS;
        else
        if (actionType->value() == 1)
-               return ACTION_KEYREL;
+               return G_ACTION_KEYREL;
        else
        if (actionType->value() == 2)
-               return ACTION_KILLCHAN;
+               return G_ACTION_KILL;
        else
                return -1;
 }
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gGridTool::gGridTool(int x, int y, gdActionEditor *parent)
-       :       Fl_Group(x, y, 80, 20), parent(parent)
-{
-       gridType = new gChoice(x, y, 40, 20);
-       gridType->add("1");
-       gridType->add("2");
-       gridType->add("3");
-       gridType->add("4");
-       gridType->add("6");
-       gridType->add("8");
-       gridType->add("16");
-       gridType->add("32");
-       gridType->value(0);
-       gridType->callback(cb_changeType, (void*)this);
-
-       active = new gCheck (x+44, y+4, 12, 12);
-
-       gridType->value(G_Conf.actionEditorGridVal);
-       active->value(G_Conf.actionEditorGridOn);
-
-       end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gGridTool::~gGridTool() {
-       G_Conf.actionEditorGridVal = gridType->value();
-       G_Conf.actionEditorGridOn  = active->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::cb_changeType(Fl_Widget *w, void *p)  { ((gGridTool*)p)->__cb_changeType(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::__cb_changeType() {
-       calc();
-       parent->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gGridTool::isOn() {
-       return active->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getValue() {
-       switch (gridType->value()) {
-               case 0: return 1;
-               case 1: return 2;
-               case 2: return 3;
-               case 3: return 4;
-               case 4: return 6;
-               case 5: return 8;
-               case 6: return 16;
-               case 7: return 32;
-       }
-       return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::calc() {
-
-       points.clear();
-       frames.clear();
-       bars.clear();
-       beats.clear();
-
-       /* find beats, bars and grid. The method is the same of the waveform in sample
-        * editor. Take totalwidth (the width in pixel of the area to draw), knowing
-        * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
-        * put a concentrate of each block (which is totalFrames / zoom) */
-
-       int  j   = 0;
-       int fpgc = floor(G_Mixer.framesPerBeat / getValue());  // frames per grid cell
-
-       for (int i=1; i<parent->totalWidth; i++) {   // if i=0, step=0 -> useless cycle
-               int step = parent->zoom*i;
-               while (j < step && j < G_Mixer.totalFrames) {
-                       if (j % fpgc == 0) {
-                               points.push_back(i);
-                               frames.push_back(j);
-                       }
-                       if (j % G_Mixer.framesPerBeat == 0)
-                               beats.push_back(i);
-                       if (j % G_Mixer.framesPerBar == 0 && i != 1)
-                               bars.push_back(i);
-                       if (j == G_Mixer.totalFrames-1)
-                               parent->coverX = i;
-                       j++;
-               }
-               j = step;
-       }
-
-       /* fix coverX if == 0, which means G_Mixer.beats == 32 */
-
-       if (G_Mixer.beats == 32)
-               parent->coverX = parent->totalWidth;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getSnapPoint(int v) {
-
-       if (v == 0) return 0;
-
-       for (int i=0; i<(int)points.size(); i++) {
-
-               if (i == (int) points.size()-1)
-                       return points.at(i);
-
-               int gp  = points.at(i);
-               int gpn = points.at(i+1);
-
-               if (v >= gp && v < gpn)
-                       return gp;
-       }
-       return v;  // default value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getSnapFrame(int v) {
-
-       v *= parent->zoom;  // transformation pixel -> frame
-
-       for (int i=0; i<(int)frames.size(); i++) {
-
-               if (i == (int) frames.size()-1)
-                       return frames.at(i);
-
-               int gf  = frames.at(i);     // grid frame
-               int gfn = frames.at(i+1);   // grid frame next
-
-               if (v >= gf && v < gfn) {
-
-                       /* which one is the closest? gf < v < gfn */
-
-                       if ((gfn - v) < (v - gf))
-                               return gfn;
-                       else
-                               return gf;
-               }
-       }
-       return v;  // default value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getCellSize() {
-       return (parent->coverX - parent->ac->x()) / G_Mixer.beats / getValue();
-}
index c7f60f25067031b399da93dce9ace41940aa7a22..2ee9b81573a082b10f98c5338cf729c25b08fb80 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_actionEditor
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
 #ifndef GD_ACTIONEDITOR_H
 #define GD_ACTIONEDITOR_H
 
+
 #include <vector>
 #include <FL/Fl.H>
 #include <FL/Fl_Double_Window.H>
 #include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
 
 
-using std::vector;
+class Channel;
+class geChoice;
+class geGridTool;
+class geButton;
+class geButton;
+class geScroll;
+class geActionEditor;
+class geMuteEditor;
+class geEnvelopeEditor;
+class geNoteEditor;
 
 
 /* gActionEditor
- * main window which contains the tools for dealing with actions.
- * This class calculates chan, zoom, frames per beat, and so on. Each
- * sub-widget contains a pointer to this window to query those data. */
+Main window which contains the tools for dealing with actions. This class
+calculates chan, zoom, frames per beat, and so on. Each sub-widget contains a
+pointer to this window to query those data. */
 
-class gdActionEditor : public gWindow
+class gdActionEditor : public gdWindow
 {
 private:
 
        /* update
-        * compute total width, in pixel. */
+  Computes total width, in pixel. */
 
        void update();
 
 public:
 
-       gdActionEditor(class Channel *chan);
+       gdActionEditor(Channel *chan);
        ~gdActionEditor();
 
        int handle(int e);
@@ -68,20 +76,18 @@ public:
        inline void __cb_zoomIn();
        inline void __cb_zoomOut();
 
-       class gChoice   *actionType;
-       class gGridTool *gridTool;
-       class gClick    *zoomIn;
-       class gClick    *zoomOut;
-       class geScroll  *scroller;       // widget container
-
-       class geActionEditor   *ac;
-       class geMuteEditor     *mc;
-       class geEnvelopeEditor *vc;
-       class geNoteEditor     *pr;
+       geChoice    *actionType;
+       geGridTool *gridTool;
+       geButton   *zoomIn;
+       geButton   *zoomOut;
+       geScroll   *scroller;       // widget container
 
-       vector <class gActionWidget*> widgets;
+       geActionEditor   *ac;
+       geMuteEditor     *mc;
+       geEnvelopeEditor *vc;
+       geNoteEditor     *pr;
 
-       class Channel *chan;
+       Channel *chan;
 
        int zoom;
        int totalWidth;  // total width of the widget, in pixel (zoom affected)
@@ -89,47 +95,4 @@ public:
 };
 
 
-/* ------------------------------------------------------------------ */
-
-
-class gGridTool : public Fl_Group {
-
-private:
-       class gChoice  *gridType;
-       class gCheck   *active;
-
-       class gdActionEditor *parent;
-
-       static void cb_changeType(Fl_Widget *w, void *p);
-       inline void __cb_changeType();
-
-public:
-
-       gGridTool(int x, int y, gdActionEditor *parent);
-       ~gGridTool();
-
-       int  getValue();
-       bool isOn();
-       void calc();
-
-       /* getSnapPoint
-        * given a cursor position in input, return the x coordinates of the
-        * nearest snap point (in pixel, clean, ie. not x()-shifted) */
-
-       int getSnapPoint(int v);
-       int getSnapFrame(int v);
-
-       /* getCellSize
-        * return the size in pixel of a single cell of the grid. */
-
-       int getCellSize();
-
-       vector<int> points;   // points of the grid
-       vector<int> frames;   // frames of the grid
-
-       vector<int> bars;
-       vector<int> beats;
-};
-
-
 #endif
index 35ca62e4855e03cf0f5df0617adde3bad0c7b52b..a00c2d7c214ff7bdbadf5e456b1eec15d7129a32 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_beatsInput
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
 #include "../../utils/gui.h"
 #include "../../core/mixer.h"
+#include "../../core/clock.h"
 #include "../../core/conf.h"
 #include "../../core/const.h"
 #include "../../glue/main.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/input.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/check.h"
 #include "gd_beatsInput.h"
 #include "gd_mainWindow.h"
 
 
-extern Mixer                            G_Mixer;
-extern Conf          G_Conf;
 extern gdMainWindow *mainWin;
 
 
+using namespace giada::m;
+
+
 gdBeatsInput::gdBeatsInput()
-       : gWindow(164, 60, "Beats")
+       : gdWindow(164, 60, "Beats")
 {
-       if (G_Conf.beatsX)
-               resize(G_Conf.beatsX, G_Conf.beatsY, w(), h());
+       if (conf::beatsX)
+               resize(conf::beatsX, conf::beatsY, w(), h());
 
        set_modal();
 
-       beats     = new gInput(8,  8,  35, 20);
-       bars      = new gInput(47, 8,  35, 20);
-       ok                  = new gClick(86, 8,  70, 20, "Ok");
-       resizeRec = new gCheck(8,  40, 12, 12, "resize recorded actions");
+       beats     = new geInput(8,  8,  35, 20);
+       bars      = new geInput(47, 8,  35, 20);
+       ok                  = new geButton(86, 8,  70, 20, "Ok");
+       resizeRec = new geCheck(8,  40, 12, 12, "resize recorded actions");
        end();
 
-       char buf_bars[3]; sprintf(buf_bars, "%d", G_Mixer.bars);
-       char buf_beats[3]; sprintf(buf_beats, "%d", G_Mixer.beats);
+       char buf_bars[3]; sprintf(buf_bars, "%d", clock::getBars());
+       char buf_beats[3]; sprintf(buf_beats, "%d", clock::getBeats());
        beats->maximum_size(2);
        beats->value(buf_beats);
        beats->type(FL_INT_INPUT);
@@ -66,7 +68,7 @@ gdBeatsInput::gdBeatsInput()
        bars->type(FL_INT_INPUT);
        ok->shortcut(FL_Enter);
        ok->callback(cb_update_batt, (void*)this);
-       resizeRec->value(G_Conf.resizeRecordings);
+       resizeRec->value(conf::resizeRecordings);
 
        gu_setFavicon(this);
        setId(WID_BEATS);
@@ -79,9 +81,9 @@ gdBeatsInput::gdBeatsInput()
 
 gdBeatsInput::~gdBeatsInput()
 {
-       G_Conf.beatsX = x();
-       G_Conf.beatsY = y();
-       G_Conf.resizeRecordings =       resizeRec->value();
+       conf::beatsX = x();
+       conf::beatsY = y();
+       conf::resizeRecordings =        resizeRec->value();
 }
 
 
index a20fc4ae38bca52d21ddabb56ad9a119b1bf378d..90150ea75962d7eec72eb85a39384b4c91231e07 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_beatsInput
- *
  * ---------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include <FL/Fl.H>
 #include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geInput;
+class geButton;
+class geCheck;
 
 
-class gdBeatsInput : public gWindow
+class gdBeatsInput : public gdWindow
 {
 private:
 
        static void cb_update_batt(Fl_Widget *w, void *p);
        inline void __cb_update_batt();
 
-       class gInput *beats;
-       class gInput *bars;
-       class gClick *ok;
-       class gCheck *resizeRec;
+       geInput  *beats;
+       geInput  *bars;
+       geButton *ok;
+       geCheck   *resizeRec;
 
 public:
 
index 4ad1da2e0c1824658120a24552f599a65da6b1e6..c3fbe0a8c19171285d14ebe6b6f99750c01cf681 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_bpmInput
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
+#include <cstring>
 #include "../../core/conf.h"
 #include "../../core/const.h"
 #include "../../core/mixer.h"
+#include "../../core/clock.h"
 #include "../../glue/main.h"
 #include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/input.h"
 #include "gd_bpmInput.h"
 #include "gd_mainWindow.h"
 
 
-extern Mixer                    G_Mixer;
-extern Conf          G_Conf;
 extern gdMainWindow *mainWin;
 
 
-gdBpmInput::gdBpmInput(const char *label)
-: gWindow(144, 36, "Bpm") {
+using namespace giada::m;
 
-       if (G_Conf.bpmX)
-               resize(G_Conf.bpmX, G_Conf.bpmY, w(), h());
+
+gdBpmInput::gdBpmInput(const char *label)
+: gdWindow(144, 36, "Bpm")
+{
+       if (conf::bpmX)
+               resize(conf::bpmX, conf::bpmY, w(), h());
 
        set_modal();
 
-       input_a = new gInput(8,  8, 30, 20);
-       input_b = new gInput(42, 8, 20, 20);
-       ok                = new gClick(66, 8, 70, 20, "Ok");
+       input_a = new geInput(8,  8, 30, 20);
+       input_b = new geInput(42, 8, 20, 20);
+       ok                = new geButton(66, 8, 70, 20, "Ok");
        end();
 
        char   a[4];
-       snprintf(a, 4, "%d", (int) G_Mixer.bpm);
+       snprintf(a, 4, "%d", (int) clock::getBpm());
        char   b[2];
        for (unsigned i=0; i<strlen(label); i++)        // looking for the dot
                if (label[i] == '.') {
@@ -80,25 +82,27 @@ gdBpmInput::gdBpmInput(const char *label)
 }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
-gdBpmInput::~gdBpmInput() {
-       G_Conf.bpmX = x();
-       G_Conf.bpmY = y();
+gdBpmInput::~gdBpmInput()
+{
+       conf::bpmX = x();
+       conf::bpmY = y();
 }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
 void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); }
 
 
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
 
 
-void gdBpmInput::__cb_update_bpm() {
+void gdBpmInput::__cb_update_bpm()
+{
        if (strcmp(input_a->value(), "") == 0)
                return;
        glue_setBpm(input_a->value(), input_b->value());
index cfca7d80b14cc07eb83bb8433e3bba09896f5fb4..0f7a16656fb37363a4fbdba41be8614898f5d223 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_bpmInput
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
 
 #ifndef GD_BPMINPUT_H
 #define GD_BPMINPUT_H
 
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
 
+#include "window.h"
+
+
+class geInput;
+class geButton;
 
-class gdBpmInput : public gWindow {
+
+class gdBpmInput : public gdWindow
+{
 private:
+
        static void cb_update_bpm(Fl_Widget *w, void *p);
        inline void __cb_update_bpm();
 
-       class gInput *input_a;
-       class gInput *input_b;
-       class gClick *ok;
+  geInput  *input_a;
+  geInput  *input_b;
+  geButton *ok;
 
 public:
+
        gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label()
        ~gdBpmInput();
 };
diff --git a/src/gui/dialogs/gd_browser.cpp b/src/gui/dialogs/gd_browser.cpp
deleted file mode 100644 (file)
index cbb7e46..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_browser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../core/graphics.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/browser.h"
-#include "gd_browser.h"
-
-
-using std::string;
-
-
-extern Conf G_Conf;
-
-
-gdBaseBrowser::gdBaseBrowser(int x, int y, int w, int h, const string &title,
-               const string &path, void (*callback)(void*))
-       :       gWindow(x, y, w, h, title.c_str()), callback(callback)
-{
-       set_non_modal();
-
-       groupTop = new Fl_Group(8, 8, w-16, 40);
-    hiddenFiles = new gCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
-               where = new gInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 152, 20);
-               updir   = new gClick(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
-       groupTop->end();
-       groupTop->resizable(where);
-
-  hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
-
-       where->readonly(true);
-       where->cursor_color(COLOR_BG_DARK);
-       where->value(path.c_str());
-
-       updir->callback(cb_up, (void*) this);
-
-       browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w-16, h-93);
-       browser->loadDir(path);
-       if (path == G_Conf.browserLastPath)
-               browser->preselect(G_Conf.browserPosition, G_Conf.browserLastValue);
-
-       Fl_Group *groupButtons = new Fl_Group(8, browser->y()+browser->h()+8, w-16, 20);
-               ok        = new gClick(w-88, groupButtons->y(), 80, 20);
-               cancel  = new gClick(w-ok->w()-96, groupButtons->y(), 80, 20, "Cancel");
-               status  = new gProgress(8, groupButtons->y(), cancel->x()-16, 20);
-               status->minimum(0);
-               status->maximum(1);
-               status->hide();   // show the bar only if necessary
-       groupButtons->resizable(status);
-       groupButtons->end();
-
-       end();
-
-       cancel->callback(cb_close, (void*) this);
-
-       resizable(browser);
-       size_range(320, 200);
-
-       gu_setFavicon(this);
-       show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdBaseBrowser::~gdBaseBrowser()
-{
-       G_Conf.browserX = x();
-       G_Conf.browserY = y();
-       G_Conf.browserW = w();
-       G_Conf.browserH = h();
-       G_Conf.browserPosition = browser->position();
-       G_Conf.browserLastPath = gu_dirname(browser->getSelectedItem());
-       G_Conf.browserLastValue = browser->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::cb_up   (Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_up(); }
-void gdBaseBrowser::cb_close(Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_close(); }
-void gdBaseBrowser::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_toggleHiddenFiles(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_up()
-{
-       string dir = browser->getCurrentDir();
-       dir = dir.substr(0, dir.find_last_of(G_SLASH_STR));  // remove up to the next slash
-       browser->loadDir(dir);
-       where->value(browser->getCurrentDir().c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_close()
-{
-       do_callback();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_toggleHiddenFiles()
-{
-       browser->toggleHiddenFiles();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::setStatusBar(float v)
-{
-       status->value(status->value() + v);
-       Fl::wait(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::showStatusBar()
-{
-  status->show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::hideStatusBar()
-{
-  status->hide();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdBaseBrowser::getCurrentPath()
-{
-  return where->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdBaseBrowser::getSelectedItem()
-{
-       return browser->getSelectedItem();
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gdSaveBrowser::gdSaveBrowser(int x, int y, int w, int h, const string &title,
-               const string &path, const string &_name, void (*cb)(void*), Channel *ch)
-       :       gdBaseBrowser(x, y, w, h, title, path, cb)
-{
-       channel = ch;
-
-       where->size(groupTop->w()-236, 20);
-
-       name = new gInput(where->x()+where->w()+8, where->y(), 200, 20);
-       name->value(_name.c_str());
-       groupTop->add(name);
-
-       browser->callback(cb_down, (void*) this);
-
-       ok->label("Save");
-       ok->callback(cb_save, (void*) this);
-       ok->shortcut(FL_ENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::cb_save(Fl_Widget *v, void *p) { ((gdSaveBrowser*)p)->__cb_save(); }
-void gdSaveBrowser::cb_down(Fl_Widget *v, void *p) { ((gdSaveBrowser*)p)->__cb_down(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::__cb_down()
-{
-       string path = browser->getSelectedItem();
-
-       if (path.empty())  // when click on an empty area
-               return;
-
-       /* if the selected item is a directory just load its content. If it's a file
-        * use it as the file name (i.e. fill name->value()). */
-
-       if (gu_isDir(path)) {
-               browser->loadDir(path);
-               where->value(browser->getCurrentDir().c_str());
-       }
-       else
-               name->value(browser->getSelectedItem(false).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdSaveBrowser::getName()
-{
-  return name->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::__cb_save()
-{
-       callback((void*) this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gdLoadBrowser::gdLoadBrowser(int x, int y, int w, int h, const string &title,
-               const string &path, void (*cb)(void*), Channel *ch)
-       :       gdBaseBrowser(x, y, w, h, title, path, cb)
-{
-       channel = ch;
-
-       where->size(groupTop->w()-updir->w()-8, 20);
-
-       browser->callback(cb_down, (void*) this);
-
-       ok->label("Load");
-       ok->callback(cb_load, (void*) this);
-       ok->shortcut(FL_ENTER);
-}
-
-
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::cb_load(Fl_Widget *v, void *p) { ((gdLoadBrowser*)p)->__cb_load(); }
-void gdLoadBrowser::cb_down(Fl_Widget *v, void *p) { ((gdLoadBrowser*)p)->__cb_down(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::__cb_load()
-{
-       callback((void*) this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::__cb_down()
-{
-       string path = browser->getSelectedItem();
-
-       if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
-               return;
-
-       browser->loadDir(path);
-       where->value(browser->getCurrentDir().c_str());
-}
diff --git a/src/gui/dialogs/gd_browser.h b/src/gui/dialogs/gd_browser.h
deleted file mode 100644 (file)
index 5606b52..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_browser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GD_BROWSER_H
-#define GD_BROWSER_H
-
-
-#include "../elems/ge_window.h"
-
-
-class gdBaseBrowser : public gWindow
-{
-protected:
-
-       class Fl_Group  *groupTop;
-  class gCheck    *hiddenFiles;
-       class geBrowser *browser;
-       class gClick    *ok;
-       class gClick    *cancel;
-       class gInput    *where;
-       class gClick    *updir;
-       class gProgress *status;
-
-       static void cb_up               (Fl_Widget *v, void *p);
-       static void cb_close            (Fl_Widget *w, void *p);
-       static void cb_toggleHiddenFiles(Fl_Widget *w, void *p);
-
-       inline void __cb_up               ();
-       inline void __cb_close            ();
-       inline void __cb_toggleHiddenFiles();
-
-       /* Callback
-        * Fired when the save/load button is pressed. */
-
-       void (*callback)(void*);
-
-       class Channel *channel;
-
-public:
-
-       gdBaseBrowser(int x, int y, int w, int h, const string &title,
-                       const string &path,     void (*callback)(void*));
-
-       ~gdBaseBrowser();
-
-       /* getSelectedItem
-        * Return the full path of the selected file. */
-
-       string getSelectedItem();
-
-       /* setStatusBar
-        * Increment status bar for progress tracking. */
-
-       void setStatusBar(float v);
-
-       gProgress *getStatusBar() { return status; }  // TODO - remove with Patch_DEPR_
-       void showStatusBar();
-       void hideStatusBar();
-  string getCurrentPath();
-
-       Channel *getChannel() { return channel; }
-       void fireCallback()   { callback((void*) this); }
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gdSaveBrowser : public gdBaseBrowser
-{
-private:
-
-       class gInput *name;
-
-       static void cb_down(Fl_Widget *v, void *p);
-       static void cb_save(Fl_Widget *w, void *p);
-
-       inline void __cb_down();
-       inline void __cb_save();
-
-public:
-
-       gdSaveBrowser(int x, int y, int w, int h, const string &title,
-                       const string &path,     const string &name, void (*callback)(void*),
-                       class Channel *ch);
-
-       string getName();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gdLoadBrowser : public gdBaseBrowser
-{
-private:
-
-       static void cb_load(Fl_Widget *w, void *p);
-       static void cb_down(Fl_Widget *v, void *p);
-
-       inline void __cb_load();
-       inline void __cb_down();
-
-public:
-
-       gdLoadBrowser(int x, int y, int w, int h, const string &title,
-                       const string &path,     void (*callback)(void*), class Channel *ch);
-};
-
-#endif
index 10c49ad23beb1ee2fcd322dba3be64830ef8c944..82f998723d1737fda4e1f1275f0993c69ee4c227 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_config
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <RtMidi.h>
 #include <FL/Fl_Tabs.H>
 #include "../../core/conf.h"
-#include "../../core/midiMapConf.h"
-#include "../../core/patch_DEPR_.h"
-#include "../../core/kernelAudio.h"
-#include "../../core/kernelMidi.h"
-#include "../../core/pluginHost.h"
+#include "../../core/const.h"
 #include "../../utils/gui.h"
-#include "../../utils/log.h"
-#include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
 #include "../elems/basics/boxtypes.h"
+#include "../elems/basics/button.h"
+#include "../elems/config/tabMisc.h"
+#include "../elems/config/tabMidi.h"
+#include "../elems/config/tabAudio.h"
+#include "../elems/config/tabBehaviors.h"
+#include "../elems/config/tabPlugins.h"
 #include "gd_config.h"
-#include "gd_keyGrabber.h"
-#include "gd_devInfo.h"
-#include "gd_browser.h"
-
-
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf           G_Conf;
-extern KernelAudio G_KernelAudio;
-extern KernelMidi  G_KernelMidi;
-extern bool        G_audio_status;
-extern MidiMapConf G_MidiMap;
-
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
-using std::string;
-
-
-gTabMisc::gTabMisc(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Misc")
-{
-       begin();
-       debugMsg = new gChoice(x()+92,  y()+9,  253, 20, "Debug messages");
-       end();
-
-       debugMsg->add("(disabled)");
-       debugMsg->add("To standard output");
-       debugMsg->add("To file");
-
-       labelsize(GUI_FONT_SIZE_BASE);
-
-       switch (G_Conf.logMode) {
-               case LOG_MODE_MUTE:
-                       debugMsg->value(0);
-                       break;
-               case LOG_MODE_STDOUT:
-                       debugMsg->value(1);
-                       break;
-               case LOG_MODE_FILE:
-                       debugMsg->value(2);
-                       break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMisc::save()
-{
-       switch(debugMsg->value()) {
-               case 0:
-                       G_Conf.logMode = LOG_MODE_MUTE;
-                       break;
-               case 1:
-                       G_Conf.logMode = LOG_MODE_STDOUT;
-                       break;
-               case 2:
-                       G_Conf.logMode = LOG_MODE_FILE;
-                       break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabAudio::gTabAudio(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Sound System")
-{
-       begin();
-       soundsys    = new gChoice(x()+92,  y()+9,  253, 20, "System");
-       buffersize  = new gChoice(x()+92,  y()+37, 55,  20, "Buffer size");
-       samplerate  = new gChoice(x()+290, y()+37, 55,  20, "Sample rate");
-       sounddevOut = new gChoice(x()+92,  y()+65, 225, 20, "Output device");
-       devOutInfo  = new gClick (x()+325, y()+65, 20,  20, "?");
-       channelsOut = new gChoice(x()+92,  y()+93, 55,  20, "Output channels");
-       limitOutput = new gCheck (x()+155, y()+97, 55,  20, "Limit output");
-       sounddevIn  = new gChoice(x()+92,  y()+121, 225, 20, "Input device");
-       devInInfo   = new gClick (x()+325, y()+121, 20,  20, "?");
-       channelsIn  = new gChoice(x()+92,  y()+149, 55,  20, "Input channels");
-       delayComp   = new gInput (x()+290, y()+149, 55,  20, "Rec delay comp.");
-       rsmpQuality = new gChoice(x()+92, y()+177, 253, 20, "Resampling");
-                new gBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92,
-                                                                               "Restart Giada for the changes to take effect.");
-       end();
-       labelsize(GUI_FONT_SIZE_BASE);
-
-       soundsys->add("(none)");
-
-#if defined(__linux__)
-
-       if (G_KernelAudio.hasAPI(RtAudio::LINUX_ALSA))
-               soundsys->add("ALSA");
-       if (G_KernelAudio.hasAPI(RtAudio::UNIX_JACK))
-               soundsys->add("Jack");
-       if (G_KernelAudio.hasAPI(RtAudio::LINUX_PULSE))
-               soundsys->add("PulseAudio");
-
-       switch (G_Conf.soundSystem) {
-               case SYS_API_NONE:
-                       soundsys->showItem("(none)");
-                       break;
-               case SYS_API_ALSA:
-                       soundsys->showItem("ALSA");
-                       break;
-               case SYS_API_JACK:
-                       soundsys->showItem("Jack");
-                       buffersize->deactivate();
-                       samplerate->deactivate();
-                       break;
-               case SYS_API_PULSE:
-                       soundsys->showItem("PulseAudio");
-                       break;
-       }
-
-#elif defined(_WIN32)
-
-       if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_DS))
-               soundsys->add("DirectSound");
-       if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_ASIO))
-               soundsys->add("ASIO");
-       if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_WASAPI))
-               soundsys->add("WASAPI");
-
-       switch (G_Conf.soundSystem) {
-               case SYS_API_NONE:
-                       soundsys->showItem("(none)");
-                       break;
-               case SYS_API_DS:
-                       soundsys->showItem("DirectSound");
-                       break;
-               case SYS_API_ASIO:
-                       soundsys->showItem("ASIO");
-                       break;
-               case SYS_API_WASAPI:
-                       soundsys->showItem("WASAPI");
-                       break;
-       }
-
-#elif defined (__APPLE__)
-
-       if (G_KernelAudio.hasAPI(RtAudio::MACOSX_CORE))
-               soundsys->add("CoreAudio");
-
-       switch (G_Conf.soundSystem) {
-               case SYS_API_NONE:
-                       soundsys->showItem("(none)");
-                       break;
-               case SYS_API_CORE:
-                       soundsys->showItem("CoreAudio");
-                       break;
-       }
-
-#endif
-
-       soundsysInitValue = soundsys->value();
-
-       soundsys->callback(cb_deactivate_sounddev, (void*)this);
-
-       sounddevIn->callback(cb_fetchInChans, this);
-       sounddevOut->callback(cb_fetchOutChans, this);
-
-       devOutInfo->callback(cb_showOutputInfo, this);
-       devInInfo->callback(cb_showInputInfo, this);
-
-       if (G_Conf.soundSystem != SYS_API_NONE) {
-               fetchSoundDevs();
-               fetchOutChans(sounddevOut->value());
-               fetchInChans(sounddevIn->value());
-
-               /* fill frequency dropdown menu */
-               /* TODO - add fetchFrequencies() */
-
-               int nfreq = G_KernelAudio.getTotalFreqs(sounddevOut->value());
-               for (int i=0; i<nfreq; i++) {
-                       int freq = G_KernelAudio.getFreq(sounddevOut->value(), i);
-                       samplerate->add(gu_itoa(freq).c_str());
-                       if (freq == G_Conf.samplerate)
-                               samplerate->value(i);
-               }
-       }
-       else {
-               sounddevIn->deactivate();
-               sounddevOut->deactivate();
-               channelsIn->deactivate();
-               channelsOut->deactivate();
-               devOutInfo->deactivate();
-               devInInfo->deactivate();
-               samplerate->deactivate();
-       }
-
-       buffersize->add("8");
-       buffersize->add("16");
-       buffersize->add("32");
-       buffersize->add("64");
-       buffersize->add("128");
-       buffersize->add("256");
-       buffersize->add("512");
-       buffersize->add("1024");
-       buffersize->add("2048");
-       buffersize->add("4096");
-       buffersize->showItem(gu_itoa(G_Conf.buffersize).c_str());
-
-       rsmpQuality->add("Sinc best quality (very slow)");
-       rsmpQuality->add("Sinc medium quality (slow)");
-       rsmpQuality->add("Sinc basic quality (medium)");
-       rsmpQuality->add("Zero Order Hold (fast)");
-       rsmpQuality->add("Linear (very fast)");
-       rsmpQuality->value(G_Conf.rsmpQuality);
-
-       delayComp->value(gu_itoa(G_Conf.delayComp).c_str());
-       delayComp->type(FL_INT_INPUT);
-       delayComp->maximum_size(5);
-
-       limitOutput->value(G_Conf.limitOutput);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_deactivate_sounddev(); }
-void gTabAudio::cb_fetchInChans(Fl_Widget *w, void *p)        { ((gTabAudio*)p)->__cb_fetchInChans(); }
-void gTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p)       { ((gTabAudio*)p)->__cb_fetchOutChans(); }
-void gTabAudio::cb_showInputInfo(Fl_Widget *w, void *p)       { ((gTabAudio*)p)->__cb_showInputInfo(); }
-void gTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p)      { ((gTabAudio*)p)->__cb_showOutputInfo(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_fetchInChans()
-{
-       fetchInChans(sounddevIn->value());
-       channelsIn->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_fetchOutChans()
-{
-       fetchOutChans(sounddevOut->value());
-       channelsOut->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_showInputInfo()
-{
-       unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
-       new gdDevInfo(dev);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_showOutputInfo()
-{
-       unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
-       new gdDevInfo(dev);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_deactivate_sounddev()
-{
-       /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
-        * If it returns to the original sound system, we re-fill the list by
-        * querying kernelAudio. Watch out if soundsysInitValue == 0: you don't want
-        * to query kernelAudio for '(none)' soundsystem! */
-
-       if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
-               sounddevOut->clear();
-               sounddevIn->clear();
-
-               fetchSoundDevs();
-
-               /* the '?' button is added by fetchSoundDevs */
-
-               fetchOutChans(sounddevOut->value());
-               sounddevOut->activate();
-               channelsOut->activate();
-
-               /* chan menus and '?' button are activated by fetchInChans(...) */
-
-               fetchInChans(sounddevIn->value());
-               sounddevIn->activate();
-               samplerate->activate();
-       }
-       else {
-               sounddevOut->deactivate();
-               sounddevOut->clear();
-               sounddevOut->add("-- restart to fetch device(s) --");
-               sounddevOut->value(0);
-               channelsOut->deactivate();
-               devOutInfo->deactivate();
-               samplerate->deactivate();
-
-               sounddevIn->deactivate();
-               sounddevIn->clear();
-               sounddevIn->add("-- restart to fetch device(s) --");
-               sounddevIn->value(0);
-               channelsIn->deactivate();
-               devInInfo->deactivate();
-       }
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchInChans(int menuItem)
-{
-       /* if menuItem==0 device in input is disabled. */
-
-       if (menuItem == 0) {
-               devInInfo ->deactivate();
-               channelsIn->deactivate();
-               delayComp ->deactivate();
-               return;
-       }
-
-       devInInfo ->activate();
-       channelsIn->activate();
-       delayComp ->activate();
-
-       channelsIn->clear();
-
-       unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
-       unsigned chs = G_KernelAudio.getMaxInChans(dev);
-
-       if (chs == 0) {
-               channelsIn->add("none");
-               channelsIn->value(0);
-               return;
-       }
-       for (unsigned i=0; i<chs; i+=2) {
-               char str[16];
-               sprintf(str, "%d-%d", (i+1), (i+2));
-               channelsIn->add(str);
-       }
-       channelsIn->value(G_Conf.channelsIn);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchOutChans(int menuItem)
-{
-       channelsOut->clear();
-
-       unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
-       unsigned chs = G_KernelAudio.getMaxOutChans(dev);
-
-       if (chs == 0) {
-               channelsOut->add("none");
-               channelsOut->value(0);
-               return;
-       }
-       for (unsigned i=0; i<chs; i+=2) {
-               char str[16];
-               sprintf(str, "%d-%d", (i+1), (i+2));
-               channelsOut->add(str);
-       }
-       channelsOut->value(G_Conf.channelsOut);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gTabAudio::findMenuDevice(gChoice *m, int device)
-{
-       if (device == -1)
-               return 0;
-
-       if (G_audio_status == false)
-               return 0;
-
-       for (int i=0; i<m->size(); i++) {
-               if (G_KernelAudio.getDeviceName(device) == "")
-                       continue;
-               if (m->text(i) == NULL)
-                       continue;
-               if (m->text(i) == G_KernelAudio.getDeviceName(device))
-                       return i;
-       }
-
-       return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchSoundDevs()
-{
-       if (G_KernelAudio.numDevs == 0) {
-               sounddevOut->add("-- no devices found --");
-               sounddevOut->value(0);
-               sounddevIn->add("-- no devices found --");
-               sounddevIn->value(0);
-               devInInfo ->deactivate();
-               devOutInfo->deactivate();
-       }
-       else {
-
-               devInInfo ->activate();
-               devOutInfo->activate();
-
-               /* input device may be disabled: now device number -1 is the disabled
-                * one. KernelAudio knows how to handle it. */
-
-               sounddevIn->add("(disabled)");
-
-               for (unsigned i=0; i<G_KernelAudio.numDevs; i++) {
-
-                       /* escaping '/', very dangerous in FLTK (it creates a submenu) */
-
-                       string tmp = G_KernelAudio.getDeviceName(i);
-                       for (unsigned k=0; k<tmp.size(); k++)
-                               if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
-                                       tmp[k] = '-';
-
-                       /* add to list devices with at least 1 channel available. In this
-                        * way we can filter devices only for input or output, e.g. an input
-                        * devices has 0 output channels. */
-
-                       if (G_KernelAudio.getMaxOutChans(i) > 0)
-                               sounddevOut->add(tmp.c_str());
-
-                       if (G_KernelAudio.getMaxInChans(i) > 0)
-                               sounddevIn->add(tmp.c_str());
-               }
-
-               /* we show the device saved in the configuration file. */
-
-               if (sounddevOut->size() == 0) {
-                       sounddevOut->add("-- no devices found --");
-                       sounddevOut->value(0);
-                       devOutInfo->deactivate();
-               }
-               else {
-                       int outMenuValue = findMenuDevice(sounddevOut, G_Conf.soundDeviceOut);
-                       sounddevOut->value(outMenuValue);
-               }
-
-               if (sounddevIn->size() == 0) {
-                       sounddevIn->add("-- no devices found --");
-                       sounddevIn->value(0);
-                       devInInfo->deactivate();
-               }
-               else {
-                       int inMenuValue = findMenuDevice(sounddevIn, G_Conf.soundDeviceIn);
-                       sounddevIn->value(inMenuValue);
-               }
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::save()
-{
-       string text = soundsys->text(soundsys->value());
-
-       if (text == "(none)") {
-               G_Conf.soundSystem = SYS_API_NONE;
-               return;
-       }
-
-#if defined(__linux__)
-
-       else if (text == "ALSA")
-               G_Conf.soundSystem = SYS_API_ALSA;
-       else if (text == "Jack")
-               G_Conf.soundSystem = SYS_API_JACK;
-       else if (text == "PulseAudio")
-               G_Conf.soundSystem = SYS_API_PULSE;
-
-#elif defined(_WIN32)
-
-       else if (text == "DirectSound")
-               G_Conf.soundSystem = SYS_API_DS;
-       else if (text == "ASIO")
-               G_Conf.soundSystem = SYS_API_ASIO;
-       else if (text == "WASAPI")
-               G_Conf.soundSystem = SYS_API_WASAPI;
-
-#elif defined (__APPLE__)
-
-       else if (text == "CoreAudio")
-               G_Conf.soundSystem = SYS_API_CORE;
-
-#endif
-
-       /* use the device name to search into the drop down menu's */
-
-       G_Conf.soundDeviceOut = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
-       G_Conf.soundDeviceIn  = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
-       G_Conf.channelsOut    = channelsOut->value();
-       G_Conf.channelsIn     = channelsIn->value();
-       G_Conf.limitOutput    = limitOutput->value();
-       G_Conf.rsmpQuality    = rsmpQuality->value();
-
-       /* if sounddevOut is disabled (because of system change e.g. alsa ->
-        * jack) its value is equal to -1. Change it! */
-
-       if (G_Conf.soundDeviceOut == -1)
-               G_Conf.soundDeviceOut = 0;
-
-       int bufsize = atoi(buffersize->text());
-       if (bufsize % 2 != 0) bufsize++;
-       if (bufsize < 8)                  bufsize = 8;
-       if (bufsize > 8192)             bufsize = 8192;
-       G_Conf.buffersize = bufsize;
-
-       const Fl_Menu_Item *i = NULL;
-       i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
-       if (i)
-               G_Conf.samplerate = atoi(i->label());
-
-       G_Conf.delayComp = atoi(delayComp->value());
-}
 
 
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabMidi::gTabMidi(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "MIDI")
-{
-       begin();
-       system    = new gChoice(x()+92, y()+9, 253, 20, "System");
-       portOut   = new gChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port");
-       portIn    = new gChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port");
-       noNoteOff = new gCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff");
-       midiMap   = new gChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map");
-       sync        = new gChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync");
-       new gBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect.");
-       end();
-
-       labelsize(GUI_FONT_SIZE_BASE);
-
-       system->callback(cb_changeSystem, (void*)this);
-
-       fetchSystems();
-       fetchOutPorts();
-       fetchInPorts();
-       fetchMidiMaps();
-
-       noNoteOff->value(G_Conf.noNoteOff);
-
-       sync->add("(disabled)");
-       sync->add("MIDI Clock (master)");
-       sync->add("MTC (master)");
-       if      (G_Conf.midiSync == MIDI_SYNC_NONE)
-               sync->value(0);
-       else if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
-               sync->value(1);
-       else if (G_Conf.midiSync == MIDI_SYNC_MTC_M)
-               sync->value(2);
-
-       systemInitValue = system->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchOutPorts() {
-
-       if (G_KernelMidi.numOutPorts == 0) {
-               portOut->add("-- no ports found --");
-               portOut->value(0);
-               portOut->deactivate();
-       }
-       else {
-
-               portOut->add("(disabled)");
-
-               for (unsigned i=0; i<G_KernelMidi.numOutPorts; i++)
-                       portOut->add(gu_removeFltkChars(G_KernelMidi.getOutPortName(i)).c_str());
-
-               portOut->value(G_Conf.midiPortOut+1);    // +1 because midiPortOut=-1 is '(disabled)'
-       }
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchInPorts()
-{
-       if (G_KernelMidi.numInPorts == 0) {
-               portIn->add("-- no ports found --");
-               portIn->value(0);
-               portIn->deactivate();
-       }
-       else {
-
-               portIn->add("(disabled)");
-
-               for (unsigned i=0; i<G_KernelMidi.numInPorts; i++)
-                       portIn->add(gu_removeFltkChars(G_KernelMidi.getInPortName(i)).c_str());
-
-               portIn->value(G_Conf.midiPortIn+1);    // +1 because midiPortIn=-1 is '(disabled)'
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchMidiMaps()
-{
-       if (G_MidiMap.maps.size() == 0) {
-               midiMap->add("(no MIDI maps available)");
-               midiMap->value(0);
-               midiMap->deactivate();
-               return;
-       }
-
-       for (unsigned i=0; i<G_MidiMap.maps.size(); i++) {
-               const char *imap = G_MidiMap.maps.at(i).c_str();
-               midiMap->add(imap);
-               if (G_Conf.midiMapPath == imap)
-                       midiMap->value(i);
-       }
-
-       /* Preselect the 0 midimap if nothing is selected but midimaps exist. */
-
-       if (midiMap->value() == -1 && G_MidiMap.maps.size() > 0)
-               midiMap->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::save()
-{
-       string text = system->text(system->value());
-
-       if      (text == "ALSA")
-               G_Conf.midiSystem = RtMidi::LINUX_ALSA;
-       else if (text == "Jack")
-               G_Conf.midiSystem = RtMidi::UNIX_JACK;
-       else if (text == "Multimedia MIDI")
-               G_Conf.midiSystem = RtMidi::WINDOWS_MM;
-       else if (text == "OSX Core MIDI")
-               G_Conf.midiSystem = RtMidi::MACOSX_CORE;
-
-       G_Conf.midiPortOut = portOut->value()-1;   // -1 because midiPortOut=-1 is '(disabled)'
-       G_Conf.midiPortIn  = portIn->value()-1;    // -1 because midiPortIn=-1 is '(disabled)'
-
-       G_Conf.noNoteOff   = noNoteOff->value();
-       G_Conf.midiMapPath = G_MidiMap.maps.size() == 0 ? "" : midiMap->text(midiMap->value());
-
-       if      (sync->value() == 0)
-               G_Conf.midiSync = MIDI_SYNC_NONE;
-       else if (sync->value() == 1)
-               G_Conf.midiSync = MIDI_SYNC_CLOCK_M;
-       else if (sync->value() == 2)
-               G_Conf.midiSync = MIDI_SYNC_MTC_M;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchSystems()
-{
-#if defined(__linux__)
-
-       if (G_KernelMidi.hasAPI(RtMidi::LINUX_ALSA))
-               system->add("ALSA");
-       if (G_KernelMidi.hasAPI(RtMidi::UNIX_JACK))
-               system->add("Jack");
-
-#elif defined(_WIN32)
-
-       if (G_KernelMidi.hasAPI(RtMidi::WINDOWS_MM))
-               system->add("Multimedia MIDI");
-
-#elif defined (__APPLE__)
-
-       system->add("OSX Core MIDI");
-
-#endif
-
-       switch (G_Conf.midiSystem) {
-               case RtMidi::LINUX_ALSA:  system->showItem("ALSA"); break;
-               case RtMidi::UNIX_JACK:   system->showItem("Jack"); break;
-               case RtMidi::WINDOWS_MM:  system->showItem("Multimedia MIDI"); break;
-               case RtMidi::MACOSX_CORE: system->showItem("OSX Core MIDI"); break;
-               default: system->value(0); break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((gTabMidi*)p)->__cb_changeSystem(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::__cb_changeSystem()
-{
-       /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
-        * If it returns to the original system, we re-fill the list by
-        * querying kernelMidi. */
-
-       if (systemInitValue == system->value()) {
-               portOut->clear();
-               fetchOutPorts();
-               portOut->activate();
-               portIn->clear();
-               fetchInPorts();
-               portIn->activate();
-               noNoteOff->activate();
-               sync->activate();
-       }
-       else {
-               portOut->deactivate();
-               portOut->clear();
-               portOut->add("-- restart to fetch device(s) --");
-               portOut->value(0);
-               portIn->deactivate();
-               portIn->clear();
-               portIn->add("-- restart to fetch device(s) --");
-               portIn->value(0);
-               noNoteOff->deactivate();
-               sync->deactivate();
-       }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabBehaviors::gTabBehaviors(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Behaviors")
-{
-       begin();
-       Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
-               new gBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
-               recsStopOnChanHalt_1 = new gRadio(x()+25, y()+35, 280, 20, "stop it immediately");
-               recsStopOnChanHalt_0 = new gRadio(x()+25, y()+55, 280, 20, "play it until finished");
-       radioGrp_1->end();
-
-       Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex
-               new gBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
-               chansStopOnSeqHalt_1 = new gRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
-               chansStopOnSeqHalt_0 = new gRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished");
-       radioGrp_2->end();
-
-       treatRecsAsLoops  = new gCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops");
-
-       end();
-       labelsize(GUI_FONT_SIZE_BASE);
-
-       G_Conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
-       G_Conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
-       G_Conf.treatRecsAsLoops   == 1 ? treatRecsAsLoops->value(1)  : treatRecsAsLoops->value(0);
-
-       recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
-       recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
-       chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
-       chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((gTabBehaviors*)p)->__cb_radio_mutex(w); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
-{
-       ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::save()
-{
-       G_Conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
-       G_Conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
-       G_Conf.treatRecsAsLoops   = treatRecsAsLoops->value() == 1 ? 1 : 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-gTabPlugins::gTabPlugins(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Plugins")
-{
-       folderPath = new gInput(x()+w()-250, y()+8, 250, 20);
-       scanButton = new gClick(x()+w()-120, folderPath->y()+folderPath->h()+8, 120, 20);
-       info       = new gBox(x(), scanButton->y()+scanButton->h()+8, w(), 242);
-
-       end();
-
-       labelsize(GUI_FONT_SIZE_BASE);
-
-       info->label("Scan in progress. Please wait...");
-       info->hide();
-
-       folderPath->value(G_Conf.pluginPath.c_str());
-       folderPath->label("Plugins folder");
-
-       scanButton->callback(cb_scan, (void*) this);
-
-       updateCount();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::updateCount()
-{
-       string scanLabel = "Scan (" + gu_itoa(G_PluginHost.countAvailablePlugins()) + " found)";
-       scanButton->label(scanLabel.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((gTabPlugins*)p)->__cb_scan(w); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::cb_onScan(float progress, void *p)
-{
-       string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
-       ((gTabPlugins *)p)->info->label(l.c_str());
-       Fl::wait();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::__cb_scan(Fl_Widget *w)
-{
-       info->show();
-       G_PluginHost.scanDir(folderPath->value(), cb_onScan, (void*) this);
-       G_PluginHost.saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
-       info->hide();
-       updateCount();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::save()
-{
-       G_Conf.pluginPath = folderPath->value();
-}
-
-
-#endif // if WITH_VST
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
+using namespace giada::m;
 
 
-gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration")
+gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
 {
        set_modal();
 
-       if (G_Conf.configX)
-               resize(G_Conf.configX, G_Conf.configY, this->w(), this->h());
+       if (conf::configX)
+               resize(conf::configX, conf::configY, this->w(), this->h());
 
        Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44);
   tabs->box(G_CUSTOM_BORDER_BOX);
   tabs->labelcolor(COLOR_TEXT_0);
   tabs->begin();
 
-               tabAudio     = new gTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
-               tabMidi      = new gTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
-               tabBehaviors = new gTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
-               tabMisc      = new gTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+               tabAudio     = new geTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+               tabMidi      = new geTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+               tabBehaviors = new geTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+               tabMisc      = new geTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
 #ifdef WITH_VST
-               tabPlugins   = new gTabPlugins(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+               tabPlugins   = new geTabPlugins(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
 #endif
 
        tabs->end();
 
-       save     = new gClick (w-88, h-28, 80, 20, "Save");
-       cancel = new gClick (w-176, h-28, 80, 20, "Cancel");
+       save     = new geButton (w-88, h-28, 80, 20, "Save");
+       cancel = new geButton (w-176, h-28, 80, 20, "Cancel");
 
        end();
 
@@ -988,8 +83,8 @@ gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration")
 
 gdConfig::~gdConfig()
 {
-       G_Conf.configX = x();
-       G_Conf.configY = y();
+       conf::configX = x();
+       conf::configY = y();
 }
 
 
index fc7b64ae30ac0c37b6ecdd87b84efa69358ced86..5af49f6fe2c85bdadb0b15473d7efd60d3b7c59d 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_config
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #define GD_CONFIG_H
 
 
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geTabAudio;
+class geTabBehaviors;
+class geTabMidi;
+class geTabMisc;
+#ifdef WITH_VST
+class geTabPlugins;
+#endif
+class geButton;
+class geChoice;
+class geCheck;
+class geInput;
+class geRadio;
+class geBox;
 
 
-class gdConfig : public gWindow
+class gdConfig : public gdWindow
 {
 private:
 
-       static void cb_save_config        (Fl_Widget *w, void *p);
-       static void cb_cancel             (Fl_Widget *w, void *p);
+       static void cb_save_config(Fl_Widget *w, void *p);
+       static void cb_cancel     (Fl_Widget *w, void *p);
        inline void __cb_save_config();
        inline void __cb_cancel();
 
@@ -48,161 +61,16 @@ public:
        gdConfig(int w, int h);
        ~gdConfig();
 
-       class gTabAudio     *tabAudio;
-       class gTabBehaviors *tabBehaviors;
-       class gTabMidi      *tabMidi;
-       class gTabMisc      *tabMisc;
+       geTabAudio     *tabAudio;
+       geTabBehaviors *tabBehaviors;
+       geTabMidi      *tabMidi;
+       geTabMisc      *tabMisc;
 #ifdef WITH_VST
-       class gTabPlugins   *tabPlugins;
+       geTabPlugins   *tabPlugins;
 #endif
-       class gClick          *save;
-       class gClick          *cancel;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabMidi : public Fl_Group
-{
-private:
-
-       void fetchSystems();
-       void fetchOutPorts();
-       void fetchInPorts();
-       void fetchMidiMaps();
-
-       static void cb_changeSystem  (Fl_Widget *w, void *p);
-       inline void __cb_changeSystem();
-
-       int systemInitValue;
-
-public:
-
-       class gChoice *system;
-       class gChoice *portOut;
-       class gChoice *portIn;
-       class gCheck  *noNoteOff;
-       class gChoice *midiMap;
-       class gChoice *sync;
-
-       gTabMidi(int x, int y, int w, int h);
-
-       void save();
+       geButton            *save;
+       geButton            *cancel;
 };
 
 
-/* -------------------------------------------------------------------------- */
-
-
-class gTabAudio : public Fl_Group
-{
-private:
-
-       static void cb_deactivate_sounddev(Fl_Widget *w, void *p);
-       static void cb_fetchInChans       (Fl_Widget *w, void *p);
-       static void cb_fetchOutChans      (Fl_Widget *w, void *p);
-       static void cb_showInputInfo      (Fl_Widget *w, void *p);
-       static void cb_showOutputInfo     (Fl_Widget *w, void *p);
-       inline void __cb_deactivate_sounddev();
-       inline void __cb_fetchInChans();
-       inline void __cb_fetchOutChans();
-       inline void __cb_showInputInfo();
-       inline void __cb_showOutputInfo();
-
-       void fetchSoundDevs();
-       void fetchInChans(int menuItem);
-       void fetchOutChans(int menuItem);
-       int  findMenuDevice(class gChoice *m, int device);
-
-       int soundsysInitValue;
-
-public:
-
-       class gChoice *soundsys;
-       class gChoice *samplerate;
-       class gChoice *rsmpQuality;
-       class gChoice *sounddevIn;
-       class gClick  *devInInfo;
-       class gChoice *channelsIn;
-       class gChoice *sounddevOut;
-       class gClick  *devOutInfo;
-       class gChoice *channelsOut;
-       class gCheck  *limitOutput;
-       class gChoice *buffersize;
-       class gInput  *delayComp;
-
-       gTabAudio(int x, int y, int w, int h);
-
-       void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabBehaviors : public Fl_Group
-{
-private:
-
-       static void cb_radio_mutex  (Fl_Widget *w, void *p);
-       inline void __cb_radio_mutex(Fl_Widget *w);
-
-public:
-
-       class gRadio *recsStopOnChanHalt_1;
-       class gRadio *recsStopOnChanHalt_0;
-       class gRadio *chansStopOnSeqHalt_1;
-       class gRadio *chansStopOnSeqHalt_0;
-       class gCheck *treatRecsAsLoops;
-
-       gTabBehaviors(int x, int y, int w, int h);
-
-       void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabMisc : public Fl_Group
-{
-public:
-
-       class gChoice *debugMsg;
-
-       gTabMisc(int x, int y, int w, int h);
-
-       void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-class gTabPlugins : public Fl_Group
-{
-private:
-
-       static void cb_scan  (Fl_Widget *w, void *p);
-       static void cb_onScan(float progress, void *p);
-       inline void __cb_scan(Fl_Widget *w);
-
-       void updateCount();
-
-public:
-
-       class gInput *folderPath;
-       class gClick *scanButton;
-       class gBox   *info;
-
-       gTabPlugins(int x, int y, int w, int h);
-
-       void save();
-};
-
-#endif
-
 #endif
index 3f901a34d57194a601997fbf592df168553b4957..59b1f4e88cfb4defad003069f0e00ea560ed62e9 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_devInfo
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
+#include <FL/fl_draw.H>
 #include "../../core/kernelAudio.h"
 #include "../../utils/gui.h"
 #include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
+#include "window.h"
 #include "gd_devInfo.h"
 
 
-extern KernelAudio G_KernelAudio;
-
-
 using std::string;
+using namespace giada::m;
 
 
 gdDevInfo::gdDevInfo(unsigned dev)
@@ -45,21 +44,21 @@ gdDevInfo::gdDevInfo(unsigned dev)
 {
        set_modal();
 
-       text  = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-       close = new gClick(252, h()-28, 80, 20, "Close");
+       text  = new geBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+       close = new geButton(252, h()-28, 80, 20, "Close");
        end();
 
        string body  = "";
        int    lines = 7;
 
-       body  = "Device name: " + G_KernelAudio.getDeviceName(dev) + "\n";
-       body += "Total output(s): " + gu_itoa(G_KernelAudio.getMaxOutChans(dev)) + "\n";
-       body += "Total intput(s): " + gu_itoa(G_KernelAudio.getMaxInChans(dev)) + "\n";
-       body += "Duplex channel(s): " + gu_itoa(G_KernelAudio.getDuplexChans(dev)) + "\n";
-       body += "Default output: " + string(G_KernelAudio.isDefaultOut(dev) ? "yes" : "no") + "\n";
-       body += "Default input: " + string(G_KernelAudio.isDefaultIn(dev) ? "yes" : "no") + "\n";
+       body  = "Device name: " + kernelAudio::getDeviceName(dev) + "\n";
+       body += "Total output(s): " + gu_itoa(kernelAudio::getMaxOutChans(dev)) + "\n";
+       body += "Total intput(s): " + gu_itoa(kernelAudio::getMaxInChans(dev)) + "\n";
+       body += "Duplex channel(s): " + gu_itoa(kernelAudio::getDuplexChans(dev)) + "\n";
+       body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
+       body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
 
-       int totalFreq = G_KernelAudio.getTotalFreqs(dev);
+       int totalFreq = kernelAudio::getTotalFreqs(dev);
        body += "Supported frequencies: " + gu_itoa(totalFreq);
 
        for (int i=0; i<totalFreq; i++) {
@@ -67,7 +66,7 @@ gdDevInfo::gdDevInfo(unsigned dev)
                        body += "\n    ";  // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
                        lines++;
                }
-               body += gu_itoa( G_KernelAudio.getFreq(dev, i)) + "  ";
+               body += gu_itoa( kernelAudio::getFreq(dev, i)) + "  ";
        }
 
        text->copy_label(body.c_str());
index 79a4ad272134863fc979a1098a18d757d6fffaf5..f5d10d044c04dadc98e273017f13b23463421867 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_devInfo
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
 #ifndef GD_DEV_INFO_H
 #define GD_DEV_INFO_H
 
-#include <FL/Fl.H>
+
 #include <FL/Fl_Window.H>
 
 
-class gdDevInfo : public Fl_Window {
+class geBox;
+class geButton;
+
+
+class gdDevInfo : public Fl_Window
+{
 private:
-       class gBox       *text;
-       class gClick *close;
+
+       geBox            *text;
+       geButton *close;
 
 public:
+
        gdDevInfo(unsigned dev);
        ~gdDevInfo();
 };
diff --git a/src/gui/dialogs/gd_editor.cpp b/src/gui/dialogs/gd_editor.cpp
deleted file mode 100644 (file)
index 881b4ab..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_editor
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../glue/channel.h"
-#include "../../core/waveFx.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/graphics.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/mixer.h"
-#include "../../core/wave.h"
-#include "../elems/ge_waveform.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/ge_waveTools.h"
-#include "../elems/mainWindow/keyboard/channel.h"
-#include "gd_warnings.h"
-#include "gd_editor.h"
-
-
-extern Mixer G_Mixer;
-extern Conf  G_Conf;
-
-
-gdEditor::gdEditor(SampleChannel *ch)
-  : gWindow(640, 480),
-    ch(ch)
-{
-  set_non_modal();
-
-  if (G_Conf.sampleEditorX)
-    resize(G_Conf.sampleEditorX, G_Conf.sampleEditorY, G_Conf.sampleEditorW, G_Conf.sampleEditorH);
-
-  /* top bar: grid and zoom tools */
-
-  Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
-  bar->begin();
-    grid    = new gChoice(bar->x(), bar->y(), 50, 20);
-    snap    = new gCheck(grid->x()+grid->w()+4, bar->y()+4, 12, 12);
-    zoomOut = new gClick(bar->x()+bar->w()-20, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
-    zoomIn  = new gClick(zoomOut->x()-24, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
-  bar->end();
-  bar->resizable(new gBox(grid->x()+grid->w()+4, bar->y(), 80, bar->h()));
-
-  /* waveform */
-
-  waveTools = new gWaveTools(8, 36, w()-16, h()-120, ch);
-  waveTools->end();
-
-  /* other tools */
-
-  Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130);
-  tools->begin();
-    volume        = new gDial (tools->x()+50,                    tools->y(), 20, 20, "Volume");
-    volumeNum     = new gInput(volume->x()+volume->w()+4,        tools->y(), 46, 20, "dB");
-
-    boost         = new gDial (volumeNum->x()+volumeNum->w()+108, tools->y(), 20, 20, "Boost");
-    boostNum      = new gInput(boost->x()+boost->w()+4,           tools->y(), 44, 20, "dB");
-
-    normalize     = new gClick(boostNum->x()+boostNum->w()+54,   tools->y(), 70, 20, "Normalize");
-    pan           = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan");
-    panNum        = new gInput(pan->x()+pan->w()+4,              tools->y(), 45, 20, "%");
-
-    pitch         = new gDial (tools->x()+50,                       volume->y()+volume->h()+4, 20, 20, "Pitch");
-    pitchNum      = new gInput(pitch->x()+pitch->w()+4,             volume->y()+volume->h()+4, 46, 20);
-    pitchToBar    = new gClick(pitchNum->x()+pitchNum->w()+4,       volume->y()+volume->h()+4, 60, 20, "To bar");
-    pitchToSong   = new gClick(pitchToBar->x()+pitchToBar->w()+4,   volume->y()+volume->h()+4, 60, 20, "To song");
-    pitchHalf     = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 20, 20, "", divideOff_xpm, divideOn_xpm);
-    pitchDouble   = new gClick(pitchHalf->x()+pitchHalf->w()+4,     volume->y()+volume->h()+4, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
-    pitchReset    = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset");
-    reload        = new gClick(pitchReset->x()+pitchReset->w()+4,   volume->y()+volume->h()+4, 70, 20, "Reload");
-
-    chanStart     = new gInput(tools->x()+60,                   pitch->y()+pitch->h()+4, 60, 20, "Range");
-    chanEnd       = new gInput(chanStart->x()+chanStart->w()+4, pitch->y()+pitch->h()+4, 60, 20, "");
-    resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4,     pitch->y()+pitch->h()+4, 60, 20, "Reset");
-
-  tools->end();
-  tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h()));
-
-  /* grid tool setup */
-
-  grid->add("(off)");
-  grid->add("2");
-  grid->add("3");
-  grid->add("4");
-  grid->add("6");
-  grid->add("8");
-  grid->add("16");
-  grid->add("32");
-  grid->add("64");
-  grid->value(G_Conf.sampleEditorGridVal);
-  grid->callback(cb_changeGrid, (void*)this);
-
-  snap->value(G_Conf.sampleEditorGridOn);
-  snap->callback(cb_enableSnap, (void*)this);
-
-  /* TODO - redraw grid if != (off) */
-
-  char buf[16];
-  sprintf(buf, "%d", ch->begin / 2); // divided by 2 because stereo
-  chanStart->value(buf);
-  chanStart->type(FL_INT_INPUT);
-  chanStart->callback(cb_setChanPos, this);
-
-  /* inputs callback: fire when they lose focus or Enter is pressed. */
-
-  chanStart->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-  chanEnd  ->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-
-  sprintf(buf, "%d", ch->end / 2);  // divided by 2 because stereo
-  chanEnd->value(buf);
-  chanEnd->type(FL_INT_INPUT);
-  chanEnd->callback(cb_setChanPos, this);
-
-  resetStartEnd->callback(cb_resetStartEnd, this);
-
-  volume->callback(cb_setVolume, (void*)this);
-  volume->value(ch->guiChannel->vol->value());
-
-  float dB = 20*log10(ch->volume);   // dB = 20*log_10(linear value)
-  if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
-  else                sprintf(buf, "-inf");
-  volumeNum->value(buf);
-  volumeNum->align(FL_ALIGN_RIGHT);
-  volumeNum->callback(cb_setVolumeNum, (void*)this);
-
-  boost->range(1.0f, 10.0f);
-  boost->callback(cb_setBoost, (void*)this);
-  if (ch->boost > 10.f)
-    boost->value(10.0f);
-  else
-    boost->value(ch->boost);
-  boost->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
-
-  float boost = 20*log10(ch->boost); // dB = 20*log_10(linear value)
-  sprintf(buf, "%.2f", boost);
-  boostNum->value(buf);
-  boostNum->align(FL_ALIGN_RIGHT);
-  boostNum->callback(cb_setBoostNum, (void*)this);
-
-  normalize->callback(cb_normalize, (void*)this);
-
-  pan->range(0.0f, 2.0f);
-  pan->callback(cb_panning, (void*)this);
-
-  pitch->range(0.01f, 4.0f);
-  pitch->value(ch->pitch);
-  pitch->callback(cb_setPitch, (void*)this);
-  pitch->when(FL_WHEN_RELEASE);
-
-  sprintf(buf, "%.4f", ch->pitch); // 4 digits
-  pitchNum->value(buf);
-  pitchNum->align(FL_ALIGN_RIGHT);
-  pitchNum->callback(cb_setPitchNum, (void*)this);
-  pitchNum->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-
-  pitchToBar->callback(cb_setPitchToBar, (void*)this);
-  pitchToSong->callback(cb_setPitchToSong, (void*)this);
-  pitchHalf->callback(cb_setPitchHalf, (void*)this);
-  pitchDouble->callback(cb_setPitchDouble, (void*)this);
-  pitchReset->callback(cb_resetPitch, (void*)this);
-
-  reload->callback(cb_reload, (void*)this);
-
-  zoomOut->callback(cb_zoomOut, (void*)this);
-  zoomIn->callback(cb_zoomIn, (void*)this);
-
-  /* logical samples (aka takes) cannot be reloaded. So far. */
-
-  if (ch->wave->isLogical)
-    reload->deactivate();
-
-  if (ch->panRight < 1.0f) {
-    char buf[8];
-    sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
-    pan->value(ch->panRight);
-    panNum->value(buf);
-  }
-  else if (ch->panRight == 1.0f && ch->panLeft == 1.0f) {
-    pan->value(1.0f);
-    panNum->value("C");
-  }
-  else {
-    char buf[8];
-    sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
-    pan->value(2.0f - ch->panLeft);
-    panNum->value(buf);
-  }
-
-  panNum->align(FL_ALIGN_RIGHT);
-  panNum->readonly(1);
-  panNum->cursor_color(FL_WHITE);
-
-  gu_setFavicon(this);
-  size_range(640, 480);
-  resizable(waveTools);
-
-  label(ch->wave->name.c_str());
-
-  show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdEditor::~gdEditor()
-{
-  G_Conf.sampleEditorX = x();
-  G_Conf.sampleEditorY = y();
-  G_Conf.sampleEditorW = w();
-  G_Conf.sampleEditorH = h();
-  G_Conf.sampleEditorGridVal = grid->value();
-  G_Conf.sampleEditorGridOn  = snap->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::cb_setChanPos      (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); }
-void gdEditor::cb_resetStartEnd   (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetStartEnd(); }
-void gdEditor::cb_setVolume       (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolume(); }
-void gdEditor::cb_setVolumeNum    (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolumeNum(); }
-void gdEditor::cb_setBoost        (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoost(); }
-void gdEditor::cb_setBoostNum     (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoostNum(); }
-void gdEditor::cb_normalize       (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_normalize(); }
-void gdEditor::cb_panning         (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_panning(); }
-void gdEditor::cb_reload          (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_reload(); }
-void gdEditor::cb_setPitch        (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitch(); }
-void gdEditor::cb_setPitchToBar   (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToBar(); }
-void gdEditor::cb_setPitchToSong  (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToSong(); }
-void gdEditor::cb_setPitchHalf    (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchHalf(); }
-void gdEditor::cb_setPitchDouble  (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchDouble(); }
-void gdEditor::cb_resetPitch      (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetPitch(); }
-void gdEditor::cb_setPitchNum     (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchNum(); }
-void gdEditor::cb_zoomIn          (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomIn(); }
-void gdEditor::cb_zoomOut         (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomOut(); }
-void gdEditor::cb_changeGrid      (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_changeGrid(); }
-void gdEditor::cb_enableSnap      (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_enableSnap()
-{
-  waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchToBar()
-{
-  glue_setPitch(this, ch, ch->end/(float)G_Mixer.framesPerBar, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchToSong()
-{
-  glue_setPitch(this, ch, ch->end/(float)G_Mixer.totalFrames, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_resetPitch()
-{
-  glue_setPitch(this, ch, 1.0f, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setChanPos()
-{
-  glue_setBeginEndChannel(
-    this,
-    ch,
-    atoi(chanStart->value())*2,  // glue halves printed values
-    atoi(chanEnd->value())*2,
-    true
-  );
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_resetStartEnd()
-{
-  glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setVolume()
-{
-  glue_setVolEditor(this, ch, volume->value(), false);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setVolumeNum()
-{
-  glue_setVolEditor(this, ch, atof(volumeNum->value()), true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setBoost()
-{
-  if (Fl::event() == FL_DRAG)
-    glue_setBoost(this, ch, boost->value(), false);
-  else if (Fl::event() == FL_RELEASE) {
-    glue_setBoost(this, ch, boost->value(), false);
-  waveTools->updateWaveform();
-  }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setBoostNum()
-{
-  glue_setBoost(this, ch, atof(boostNum->value()), true);
-  waveTools->updateWaveform();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_normalize()
-{
-  float val = wfx_normalizeSoft(ch->wave);
-  glue_setBoost(this, ch, val, false); // we pretend that a fake user turns the dial (numeric=false)
-  if (val < 0.0f)
-    boost->value(0.0f);
-  else
-  if (val > 20.0f) // a dial > than it's max value goes crazy
-    boost->value(10.0f);
-  else
-    boost->value(val);
-  waveTools->updateWaveform();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_panning()
-{
-  glue_setPanning(this, ch, pan->value());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_reload()
-{
-  if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
-    return;
-
-  /* no need for glue_loadChan, there's no gui to refresh */
-
-  ch->load(ch->wave->pathfile.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality);
-
-  glue_setBoost(this, ch, DEFAULT_BOOST, true);
-  glue_setPitch(this, ch, G_DEFAULT_PITCH, true);
-  glue_setPanning(this, ch, 1.0f);
-  pan->value(1.0f);  // glue_setPanning doesn't do it
-  pan->redraw();     // glue_setPanning doesn't do it
-
-  waveTools->waveform->stretchToWindow();
-  waveTools->updateWaveform();
-
-  glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
-
-  redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitch()
-{
-  glue_setPitch(this, ch, pitch->value(), false);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchNum()
-{
-  glue_setPitch(this, ch, atof(pitchNum->value()), true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchHalf()
-{
-  glue_setPitch(this, ch, pitch->value()/2, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchDouble()
-{
-  glue_setPitch(this, ch, pitch->value()*2, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_zoomIn()
-{
-  waveTools->waveform->setZoom(-1);
-  waveTools->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_zoomOut()
-{
-  waveTools->waveform->setZoom(0);
-  waveTools->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_changeGrid()
-{
-  waveTools->waveform->setGridLevel(atoi(grid->text()));
-}
diff --git a/src/gui/dialogs/gd_editor.h b/src/gui/dialogs/gd_editor.h
deleted file mode 100644 (file)
index c9ffe74..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_editor
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GD_EDITOR_H
-#define GD_EDITOR_H
-
-
-#include "../elems/ge_window.h"
-
-
-class gdEditor : public gWindow
-{
-private:
-
-       static void cb_setChanPos    (Fl_Widget *w, void *p);
-       static void cb_resetStartEnd (Fl_Widget *w, void *p);
-       static void cb_setVolume     (Fl_Widget *w, void *p);
-       static void cb_setVolumeNum  (Fl_Widget *w, void *p);
-       static void cb_setBoost      (Fl_Widget *w, void *p);
-       static void cb_setBoostNum   (Fl_Widget *w, void *p);
-       static void cb_normalize     (Fl_Widget *w, void *p);
-       static void cb_panning       (Fl_Widget *w, void *p);
-       static void cb_reload        (Fl_Widget *w, void *p);
-       static void cb_setPitch      (Fl_Widget *w, void *p);
-       static void cb_setPitchToBar (Fl_Widget *w, void *p);
-       static void cb_setPitchToSong(Fl_Widget *w, void *p);
-       static void cb_setPitchHalf  (Fl_Widget *w, void *p);
-       static void cb_setPitchDouble(Fl_Widget *w, void *p);
-       static void cb_resetPitch    (Fl_Widget *w, void *p);
-       static void cb_setPitchNum   (Fl_Widget *w, void *p);
-       static void cb_zoomIn        (Fl_Widget *w, void *p);
-       static void cb_zoomOut       (Fl_Widget *w, void *p);
-       static void cb_changeGrid    (Fl_Widget *w, void *p);
-       static void cb_enableSnap    (Fl_Widget *w, void *p);
-       inline void __cb_setChanPos();
-       inline void __cb_resetStartEnd();
-       inline void __cb_setVolume();
-       inline void __cb_setVolumeNum();
-       inline void __cb_setBoost();
-       inline void __cb_setBoostNum();
-       inline void __cb_normalize();
-       inline void __cb_panning();
-       inline void __cb_reload();
-       inline void __cb_setPitch();
-       inline void __cb_setPitchToBar();
-       inline void __cb_setPitchToSong();
-       inline void __cb_setPitchHalf();
-       inline void __cb_setPitchDouble();
-       inline void __cb_resetPitch();
-       inline void __cb_setPitchNum();
-       inline void __cb_zoomIn();
-       inline void __cb_zoomOut();
-       inline void __cb_changeGrid();
-       inline void __cb_enableSnap();
-
-public:
-
-       gdEditor(class SampleChannel *ch);
-       ~gdEditor();
-
-       class gClick     *zoomIn;
-       class gClick     *zoomOut;
-       class gWaveTools *waveTools;
-       class gInput     *chanStart;
-       class gInput     *chanEnd;
-       class gClick             *resetStartEnd;
-       class gDial      *volume;
-       class gInput     *volumeNum;
-       class gDial      *boost;
-       class gInput     *boostNum;
-       class gClick     *normalize;
-       class gDial      *pan;
-       class gInput     *panNum;
-       class gClick             *reload;
-       class gDial              *pitch;
-       class gInput     *pitchNum;
-       class gClick     *pitchToBar;
-       class gClick     *pitchToSong;
-       class gClick     *pitchHalf;
-       class gClick     *pitchDouble;
-       class gClick     *pitchReset;
-       class gClick     *close;
-       class gChoice    *grid;
-       class gCheck     *snap;
-
-       class SampleChannel *ch;
-};
-
-
-#endif
index d76e41f412ace521c756f998414496340f278ef9..f7bbd9706a8a866ae377466148aed8231118f1d8 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_keyGrabber
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -33,7 +31,7 @@
 #include "../../core/sampleChannel.h"
 #include "../../core/midiChannel.h"
 #include "../../utils/log.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/box.h"
 #include "../elems/mainWindow/keyboard/keyboard.h"
 #include "../elems/mainWindow/keyboard/channel.h"
 #include "../elems/mainWindow/keyboard/channelButton.h"
 #include "gd_mainWindow.h"
 
 
-extern Conf             G_Conf;
 extern gdMainWindow *mainWin;
 
 
 gdKeyGrabber::gdKeyGrabber(Channel *ch)
-       : gWindow(300, 126, "Key configuration"), ch(ch)
+       : gdWindow(300, 126, "Key configuration"), ch(ch)
 {
        set_modal();
-       text   = new gBox(8, 8, 284, 80, "");
-       clear  = new gClick(w()-88, text->y()+text->h()+8, 80, 20, "Clear");
-       cancel = new gClick(clear->x()-88, clear->y(), 80, 20, "Close");
+       text   = new geBox(8, 8, 284, 80, "");
+       clear  = new geButton(w()-88, text->y()+text->h()+8, 80, 20, "Clear");
+       cancel = new geButton(clear->x()-88, clear->y(), 80, 20, "Close");
        end();
 
        clear->callback(cb_clear, (void*)this);
index ddee3a6247a82e1d4f452f607abe199b84742731..736fc7803d7578cf37fac684d14ce40fb73572dd 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_keyGrabber
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include <FL/Fl.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
 
+class Channel;
+class geBox;
+class geButton;
 
-class gdKeyGrabber : public gWindow
+
+class gdKeyGrabber : public gdWindow
 {
 private:
 
-       class Channel *ch;
+       Channel *ch;
 
-       class gBox   *text;
-       class gClick *clear;
-       class gClick *cancel;
+       geBox    *text;
+       geButton *clear;
+       geButton *cancel;
 
        static void cb_clear (Fl_Widget *w, void *p);
        static void cb_cancel(Fl_Widget *w, void *p);
@@ -51,12 +54,11 @@ private:
        inline void __cb_cancel();
 
        void setButtonLabel(int key);
-
        void updateText(int key);
 
 public:
 
-       gdKeyGrabber(class Channel *ch);
+       gdKeyGrabber(Channel *ch);
        int handle(int e);
 };
 
index 0220f7a8fb0564804a03a20641333dc3261335c6..f7fbbe1a1367149c0b68d181a2edba6c7cc9b1b4 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_mainWindow
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -31,7 +29,6 @@
 #include "../../core/const.h"
 #include "../../core/init.h"
 #include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
 #include "../elems/basics/boxtypes.h"
 #include "../elems/mainWindow/mainIO.h"
 #include "../elems/mainWindow/mainMenu.h"
@@ -47,7 +44,7 @@ extern gdMainWindow *G_MainWin;
 
 
 gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **argv)
-       : gWindow(W, H, title)
+       : gdWindow(W, H, title)
 {
        Fl::visible_focus(0);
 
index 6b079171b945772e71a6dcb2f2d9e83eefe8e527..c7dbae80bd0abc0cc2cc08d80e8b25acc450a3dc 100644 (file)
@@ -1,11 +1,10 @@
 /* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
- * gd_mainWindow
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #define GD_MAINWINDOW_H
 
 
-#include "../elems/ge_window.h"
+#include "window.h"
 
 
-class gdMainWindow : public gWindow
+class Fl_Widget;
+class geKeyboard;
+class geBeatMeter;
+class geMainMenu;
+class geMainIO;
+class geMainTimer;
+class geMainTransport;
+
+
+class gdMainWindow : public gdWindow
 {
 private:
 
-       static void cb_endprogram  (class Fl_Widget *v, void *p);
+       static void cb_endprogram  (Fl_Widget *v, void *p);
        inline void __cb_endprogram();
 
 public:
 
-       class geKeyboard       *keyboard;
-       class geBeatMeter     *beatMeter;
-       class geMainMenu      *mainMenu;
-       class geMainIO        *mainIO;
-  class geMainTimer     *mainTimer;
-       class geMainTransport *mainTransport;
+       geKeyboard      *keyboard;
+       geBeatMeter     *beatMeter;
+       geMainMenu      *mainMenu;
+       geMainIO        *mainIO;
+  geMainTimer     *mainTimer;
+       geMainTransport *mainTransport;
 
        gdMainWindow(int w, int h, const char *title, int argc, char **argv);
 };
index 0e20dc1155f889d7d6e6dec508d5296039b54101..8c62705bff998c3f1ed472e2c2e033aebb293f3e 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginChooser
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../../core/channel.h"
 #include "../../core/conf.h"
 #include "../../core/pluginHost.h"
-#include "../elems/ge_pluginBrowser.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/pluginBrowser.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/box.h"
 #include "gd_pluginChooser.h"
 
 
-extern PluginHost G_PluginHost;
-extern Conf       G_Conf;
+using namespace giada::m;
 
 
-gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, class Channel *ch)
-  : gWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(stackType)
+gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, Channel *ch)
+  : gdWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(stackType)
 {
   /* top area */
   Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
-  sortMethod = new gChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
-    gBox *b1 = new gBox(sortMethod->x()+sortMethod->w(), group_top->y(), 100, 20);     // spacer window border <-> menu
+  sortMethod = new geChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
+    geBox *b1 = new geBox(sortMethod->x()+sortMethod->w(), group_top->y(), 100, 20);   // spacer window border <-> menu
   group_top->resizable(b1);
   group_top->end();
 
@@ -59,9 +58,9 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, clas
 
   /* ok/cancel buttons */
   Fl_Group *group_btn = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, h()-browser->h()-16);
-    gBox *b2 = new gBox(8, browser->y()+browser->h(), 100, 20);        // spacer window border <-> buttons
-    addBtn = new gClick(w()-88, group_btn->y(), 80, 20, "Add");
-    cancelBtn = new gClick(addBtn->x()-88, group_btn->y(), 80, 20, "Cancel");
+    geBox *b2 = new geBox(8, browser->y()+browser->h(), 100, 20);      // spacer window border <-> buttons
+    addBtn = new geButton(w()-88, group_btn->y(), 80, 20, "Add");
+    cancelBtn = new geButton(addBtn->x()-88, group_btn->y(), 80, 20, "Cancel");
   group_btn->resizable(b2);
   group_btn->end();
 
@@ -71,7 +70,7 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, clas
   sortMethod->add("Category");
   sortMethod->add("Manufacturer");
   sortMethod->callback(cb_sort, (void*) this);
-  sortMethod->value(G_Conf.pluginSortMethod);
+  sortMethod->value(conf::pluginSortMethod);
 
   addBtn->callback(cb_add, (void*) this);
   addBtn->shortcut(FL_Enter);
@@ -88,11 +87,11 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, clas
 
 gdPluginChooser::~gdPluginChooser()
 {
-  G_Conf.pluginChooserX = x();
-  G_Conf.pluginChooserY = y();
-  G_Conf.pluginChooserW = w();
-  G_Conf.pluginChooserH = h();
-  G_Conf.pluginSortMethod = sortMethod->value();
+  conf::pluginChooserX = x();
+  conf::pluginChooserY = y();
+  conf::pluginChooserW = w();
+  conf::pluginChooserH = h();
+  conf::pluginSortMethod = sortMethod->value();
 }
 
 
@@ -118,7 +117,7 @@ void gdPluginChooser::__cb_close()
 
 void gdPluginChooser::__cb_sort()
 {
-       G_PluginHost.sortPlugins(sortMethod->value());
+       pluginHost::sortPlugins(sortMethod->value());
   browser->refresh();
 }
 
index 5d44985e5216d6c4391bdf3a79a05ba4a312d907..f74c374bc2af2513538945d8ee6ff435ceaa1600 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginChooser
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #ifdef WITH_VST
 
-#ifndef __GD_PLUGIN_CHOOSER_H__
-#define __GD_PLUGIN_CHOOSER_H__
+#ifndef GD_PLUGIN_CHOOSER_H
+#define GD_PLUGIN_CHOOSER_H
+
 
 #include <FL/Fl.H>
 #include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
 
+class Channel;
+class geChoice;
+class geButton;
+class geButton;
+class gePluginBrowser;
 
-class gdPluginChooser : public gWindow {
 
+class gdPluginChooser : public gdWindow
+{
 private:
 
-  class Channel *ch;      // ch == NULL ? masterOut
-       int   stackType;
+  Channel *ch;      // ch == nullptr ? masterOut
+       int stackType;
 
-  class gChoice         *sortMethod;
-  class gClick          *addBtn;
-  class gClick          *cancelBtn;
-       class gePluginBrowser *browser;
+  geChoice         *sortMethod;
+  geButton        *addBtn;
+  geButton        *cancelBtn;
+  gePluginBrowser *browser;
 
        static void cb_close(Fl_Widget *w, void *p);
        static void cb_add  (Fl_Widget *w, void *p);
@@ -58,7 +64,7 @@ private:
 
 public:
 
-       gdPluginChooser(int x, int y, int w, int h, int stackType, class Channel *ch=NULL);
+       gdPluginChooser(int x, int y, int w, int h, int stackType, Channel *ch=nullptr);
   ~gdPluginChooser();
 };
 
index 65d8e6d1c0942870356a7e1e1802025c4a43f7b7..92c91da3edecd4c3b7a8510566362ebed6887da0 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginList
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../../glue/plugin.h"
 #include "../../utils/log.h"
 #include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
 #include "../elems/basics/boxtypes.h"
+#include "../elems/basics/idButton.h"
+#include "../elems/basics/statusButton.h"
+#include "../elems/basics/choice.h"
 #include "../elems/mainWindow/mainIO.h"
 #include "../elems/mainWindow/keyboard/channel.h"
 #include "gd_pluginList.h"
 #include "gd_pluginChooser.h"
 #include "gd_pluginWindow.h"
 #include "gd_pluginWindowGUI.h"
-#include "gd_browser.h"
 #include "gd_mainWindow.h"
 
 
-extern Conf          G_Conf;
-extern Mixer         G_Mixer;
-extern PluginHost    G_PluginHost;
 extern gdMainWindow *G_MainWin;
 
 
 using std::string;
+using namespace giada::m;
 
 
 gdPluginList::gdPluginList(int stackType, Channel *ch)
-  : gWindow(468, 204), ch(ch), stackType(stackType)
+  : gdWindow(468, 204), ch(ch), stackType(stackType)
 {
-       if (G_Conf.pluginListX)
-               resize(G_Conf.pluginListX, G_Conf.pluginListY, w(), h());
+       if (conf::pluginListX)
+               resize(conf::pluginListX, conf::pluginListY, w(), h());
 
        list = new Fl_Scroll(8, 8, 476, 188);
        list->type(Fl_Scroll::VERTICAL);
@@ -86,10 +83,10 @@ gdPluginList::gdPluginList(int stackType, Channel *ch)
   /* TODO - awful stuff... we should subclass into gdPluginListChannel and
   gdPluginListMaster */
 
-       if (stackType == PluginHost::MASTER_OUT)
+       if (stackType == pluginHost::MASTER_OUT)
                label("Master Out Plugins");
        else
-       if (stackType == PluginHost::MASTER_IN)
+       if (stackType == pluginHost::MASTER_IN)
                label("Master In Plugins");
        else {
     string l = "Channel " + gu_itoa(ch->index+1) + " Plugins";
@@ -106,8 +103,8 @@ gdPluginList::gdPluginList(int stackType, Channel *ch)
 
 gdPluginList::~gdPluginList()
 {
-       G_Conf.pluginListX = x();
-       G_Conf.pluginListY = y();
+       conf::pluginListX = x();
+       conf::pluginListY = y();
 }
 
 
@@ -126,8 +123,8 @@ void gdPluginList::cb_refreshList(Fl_Widget *v, void *p)
         * by calling the parent (pluginList) and telling it to delete its
         * subwindow (i.e. gdBrowser). */
 
-       gWindow *child = (gWindow*) v;
-       if (child->getParent() != NULL)
+       gdWindow *child = (gdWindow*) v;
+       if (child->getParent() != nullptr)
                (child->getParent())->delSubWindow(child);
 
        /* finally refresh plugin list: void *p is a pointer to gdPluginList.
@@ -144,13 +141,13 @@ void gdPluginList::cb_refreshList(Fl_Widget *v, void *p)
 
 void gdPluginList::__cb_addPlugin()
 {
-       /* the usual callback that gWindow adds to each subwindow in this case
+       /* the usual callback that gdWindow adds to each subwindow in this case
         * is not enough, because when we close the browser the plugin list
         * must be redrawn. We have a special callback, cb_refreshList, which
         * we add to gdPluginChooser. It does exactly what we need. */
 
-  gdPluginChooser *pc = new gdPluginChooser(G_Conf.pluginChooserX,
-      G_Conf.pluginChooserY, G_Conf.pluginChooserW, G_Conf.pluginChooserH,
+  gdPluginChooser *pc = new gdPluginChooser(conf::pluginChooserX,
+      conf::pluginChooserY, conf::pluginChooserW, conf::pluginChooserH,
       stackType, ch);
   addSubWindow(pc);
   pc->callback(cb_refreshList, (void*)this);   // 'this' refers to gdPluginList
@@ -168,21 +165,21 @@ void gdPluginList::refreshList()
        list->scroll_to(0, 0);
 
        /* add new buttons, as many as the plugin in pluginHost::stack + 1,
-        * the 'add new' button. Warning: if ch == NULL we are working with
+        * the 'add new' button. Warning: if ch == nullptr we are working with
         * master in/master out stacks. */
 
-       int numPlugins = G_PluginHost.countPlugins(stackType, ch);
+       int numPlugins = pluginHost::countPlugins(stackType, ch);
        int i = 0;
 
        while (i<numPlugins) {
-               Plugin   *pPlugin = G_PluginHost.getPluginByIndex(i, stackType, ch);
+               Plugin   *pPlugin = pluginHost::getPluginByIndex(i, stackType, ch);
                gdPlugin *gdp     = new gdPlugin(this, pPlugin, list->x(), list->y()-list->yposition()+(i*24), 800);
                list->add(gdp);
                i++;
        }
 
        int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24);
-       addPlugin = new gClick(8, addPlugY, 452, 20, "-- add new plugin --");
+       addPlugin = new geButton(8, addPlugY, 452, 20, "-- add new plugin --");
        addPlugin->callback(cb_addPlugin, (void*)this);
        list->add(addPlugin);
 
@@ -201,17 +198,17 @@ void gdPluginList::refreshList()
   /* TODO - awful stuff... we should subclass into gdPluginListChannel and
   gdPluginListMaster */
 
-       if (stackType == PluginHost::MASTER_OUT) {
+       if (stackType == pluginHost::MASTER_OUT) {
     G_MainWin->mainIO->setMasterFxOutFull(
-                       G_PluginHost.countPlugins(stackType, ch) > 0);
+                       pluginHost::countPlugins(stackType, ch) > 0);
   }
        else
-       if (stackType == PluginHost::MASTER_IN) {
+       if (stackType == pluginHost::MASTER_IN) {
     G_MainWin->mainIO->setMasterFxInFull(
-                       G_PluginHost.countPlugins(stackType, ch) > 0);
+                       pluginHost::countPlugins(stackType, ch) > 0);
   }
        else {
-    ch->guiChannel->fx->full = G_PluginHost.countPlugins(stackType, ch) > 0;
+    ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0;
     ch->guiChannel->fx->redraw();
   }
 }
@@ -226,12 +223,12 @@ gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W)
        : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p)
 {
        begin();
-       button    = new gButton(8, y(), 220, 20);
-       program   = new gChoice(button->x()+button->w()+4, y(), 132, 20);
-       bypass    = new gButton(program->x()+program->w()+4, y(), 20, 20);
-       shiftUp   = new gButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
-       shiftDown = new gButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
-       remove    = new gButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
+       button    = new geIdButton(8, y(), 220, 20);
+       program   = new geChoice(button->x()+button->w()+4, y(), 132, 20);
+       bypass    = new geIdButton(program->x()+program->w()+4, y(), 20, 20);
+       shiftUp   = new geIdButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
+       shiftDown = new geIdButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
+       remove    = new geIdButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
        end();
 
        button->copy_label(pPlugin->getName().c_str());
@@ -277,10 +274,10 @@ void gdPlugin::__cb_shiftUp()
 {
        /*nothing to do if there's only one plugin */
 
-       if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+       if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
                return;
 
-       int pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(),
+       int pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(),
     pParent->stackType, pParent->ch);
 
        if (pluginIndex == 0)  // first of the stack, do nothing
@@ -298,11 +295,11 @@ void gdPlugin::__cb_shiftDown()
 {
        /*nothing to do if there's only one plugin */
 
-       if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+       if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
                return;
 
-       unsigned pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
-       unsigned stackSize   = (G_PluginHost.getStack(pParent->stackType, pParent->ch))->size();
+       unsigned pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
+       unsigned stackSize   = (pluginHost::getStack(pParent->stackType, pParent->ch))->size();
 
        if (pluginIndex == stackSize-1)  // last one in the stack, do nothing
                return;
@@ -333,7 +330,7 @@ void gdPlugin::__cb_openPluginWindow()
   /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved
   * for the parent window 'add plugin'. */
 
-  gWindow *w;
+  gdWindow *w;
   if (pPlugin->hasEditor()) {
     if (pPlugin->isEditorOpen()) {
       gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n");
index cbe947f7b444b75b1f70bd91bdeb950836befcc5..c8e88ce1b5e7057fccc9983cc7716ca1e521639c 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginList
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #ifdef WITH_VST
 
-#ifndef __GD_PLUGINLIST_H__
-#define __GD_PLUGINLIST_H__
+#ifndef GD_PLUGINLIST_H
+#define GD_PLUGINLIST_H
 
 #include <FL/Fl.H>
 #include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class Plugin;
+class Channel;
+class geButton;
+class gdPluginList;
+class geIdButton;
+class geChoice;
 
 
-class gdPluginList : public gWindow
+class gdPluginList : public gdWindow
 {
 private:
 
-       class gClick *addPlugin;
-       Fl_Scroll    *list;
+  geButton  *addPlugin;
+       Fl_Scroll *list;
 
        static void cb_addPlugin  (Fl_Widget *v, void *p);
        inline void __cb_addPlugin();
 
 public:
 
-       class Channel *ch;      // ch == NULL ? masterOut
-       int   stackType;
+       Channel *ch;      // ch == nullptr ? masterOut
+       int stackType;
 
-       gdPluginList(int stackType, class Channel *ch=NULL);
+       gdPluginList(int stackType, Channel *ch=nullptr);
        ~gdPluginList();
 
        /* special callback, passed to browser. When closed (i.e. plugin
@@ -71,8 +77,8 @@ class gdPlugin : public Fl_Group
 {
 private:
 
-       class  gdPluginList *pParent;
-       class Plugin        *pPlugin;
+  gdPluginList *pParent;
+  Plugin       *pPlugin;
 
        static void cb_removePlugin       (Fl_Widget *v, void *p);
        static void cb_openPluginWindow   (Fl_Widget *v, void *p);
@@ -89,14 +95,14 @@ private:
 
 public:
 
-       class gButton *button;
-       class gChoice *program;
-       class gButton *bypass;
-       class gButton *shiftUp;
-       class gButton *shiftDown;
-       class gButton *remove;
+       geIdButton *button;
+       geChoice    *program;
+       geIdButton *bypass;
+       geIdButton *shiftUp;
+       geIdButton *shiftDown;
+       geIdButton *remove;
 
-       gdPlugin(gdPluginList *gdp, class Plugin *p, int x, int y, int w);
+       gdPlugin(gdPluginList *gdp, Plugin *p, int x, int y, int w);
 };
 
 #endif
index d3b7136ca76d83ee4e535ef42fe716a37e0b715b..23316df2e2ed50ec5441c9beb9f40350f63b7d63 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginWindow
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Scroll.H>
 #include "../../utils/gui.h"
 #include "../../core/plugin.h"
-#include "../elems/ge_mixed.h"
 #include "../elems/basics/boxtypes.h"
+#include "../elems/basics/box.h"
+#include "../elems/basics/liquidScroll.h"
+#include "../elems/basics/slider.h"
 #include "gd_pluginWindow.h"
 
 
@@ -46,15 +46,15 @@ Parameter::Parameter(int paramIndex, Plugin *p, int X, int Y, int W)
 {
        begin();
 
-               label = new gBox(x(), y(), 60, 20);
+               label = new geBox(x(), y(), 60, 20);
                label->copy_label(pPlugin->getParameterName(paramIndex).c_str());
                label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
 
-               slider = new gSlider(label->x()+label->w()+8, y(), W-200, 20);
+               slider = new geSlider(label->x()+label->w()+8, y(), W-200, 20);
                slider->value(pPlugin->getParameter(paramIndex));
                slider->callback(cb_setValue, (void *)this);
 
-               value = new gBox(slider->x()+slider->w()+8, y(), 100, 20);
+               value = new geBox(slider->x()+slider->w()+8, y(), 100, 20);
                value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
                value->box(G_CUSTOM_BORDER_BOX);
                updateValue();
@@ -99,11 +99,11 @@ void Parameter::updateValue()
 
 
 gdPluginWindow::gdPluginWindow(Plugin *p)
- : gWindow(400, 156), pPlugin(p) // 350
+ : gdWindow(400, 156), pPlugin(p) // 350
 {
        set_non_modal();
 
-       gLiquidScroll *list = new gLiquidScroll(8, 8, w()-16, h()-16);
+       geLiquidScroll *list = new geLiquidScroll(8, 8, w()-16, h()-16);
        list->type(Fl_Scroll::VERTICAL_ALWAYS);
        list->begin();
 
index ace781ae3c158654adb434865e6c64599aa6bbc5..ff399abbdb30223ce5328487e897a824fc299573 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginWindow
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #ifdef WITH_VST
 
-#ifndef __GD_PLUGIN_WINDOW_H__
-#define __GD_PLUGIN_WINDOW_H__
+#ifndef GD_PLUGIN_WINDOW_H
+#define GD_PLUGIN_WINDOW_H
 
 
 #include <FL/Fl.H>
 #include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
 
+class Plugin;
+class geBox;
+class geSlider;
 
-class gdPluginWindow : public gWindow
+
+class gdPluginWindow : public gdWindow
 {
 private:
-       class Plugin *pPlugin;
+
+  Plugin *pPlugin;
 
 public:
-       int id;
+
+  int id;
 
        gdPluginWindow(Plugin *pPlugin);
 };
@@ -55,8 +60,9 @@ public:
 class Parameter : public Fl_Group
 {
 private:
-       int   paramIndex;
-       class Plugin *pPlugin;
+
+  int paramIndex;
+       Plugin *pPlugin;
 
        static void cb_setValue(Fl_Widget *v, void *p);
        inline void __cb_setValue();
@@ -64,11 +70,12 @@ private:
        void updateValue();
 
 public:
-       class gBox    *label;
-       class gSlider *slider;
-       class gBox    *value;
 
-       Parameter(int paramIndex, class Plugin *p, int x, int y, int w);
+  geBox    *label;
+       geSlider *slider;
+       geBox    *value;
+
+       Parameter(int paramIndex, Plugin *p, int x, int y, int w);
 };
 
 
index 7a5c772698b56b459170641b0067084e2aee0afc..263d481a206a94661bfb31b4f85e044a0ec4b043 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_pluginWindowGUI
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #ifdef WITH_VST
 
 
+#include <FL/x.H>
 #include "../../utils/log.h"
 #include "../../utils/gui.h"
 #include "../../core/pluginHost.h"
 #include "../../core/plugin.h"
 #include "../../core/const.h"
-#include "../elems/ge_mixed.h"
 #include "gd_pluginWindowGUI.h"
 
 #ifdef __APPLE__
 #endif
 
 
-extern PluginHost G_PluginHost;
+using namespace giada::m;
 
 
 gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
- : gWindow(450, 300), pPlugin(pPlugin)
+ : gdWindow(450, 300), pPlugin(pPlugin)
 {
   show();
 
@@ -107,8 +105,7 @@ void gdPluginWindowGUI::__cb_close()
 
 void gdPluginWindowGUI::__cb_refresh()
 {
-  //gu_log("[gdPluginWindowGUI::__cb_refresh] refresh!\n");
-  G_PluginHost.runDispatchLoop();
+  pluginHost::runDispatchLoop();
   Fl::repeat_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this);
 }
 
index b510d56effc8684218e3770aa922cf16a852359c..f19f0835faf431cd72fc07f870d2cb222b33821b 100644 (file)
@@ -7,7 +7,7 @@
  *
  * ---------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #ifdef WITH_VST
 
 
-#ifndef __GD_PLUGIN_WINDOW_GUI_H__
-#define __GD_PLUGIN_WINDOW_GUI_H__
+#ifndef GD_PLUGIN_WINDOW_GUI_H
+#define GD_PLUGIN_WINDOW_GUI_H
 
 
 #include <FL/Fl.H>
 #include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
 #if defined(__APPLE__)
        #include <Carbon/Carbon.h>
 #endif
 
 
-class gdPluginWindowGUI : public gWindow
+class Plugin;
+
+
+class gdPluginWindowGUI : public gdWindow
 {
 private:
 
-       class Plugin *pPlugin;
+       Plugin *pPlugin;
 
        static void cb_close    (Fl_Widget *v, void *p);
        static void cb_refresh  (void *data);
@@ -61,31 +64,6 @@ public:
 };
 
 
-/* -------------------------------------------------------------------------- */
-
-#if 0
-#if defined(__APPLE__)
-
-class gdPluginWindowGUImac : public gWindow
-{
-private:
-
-       static pascal OSStatus windowHandler(EventHandlerCallRef ehc, EventRef e, void *data);
-       inline pascal OSStatus __wh(EventHandlerCallRef ehc, EventRef e);
-
-       class Plugin *pPlugin;
-       WindowRef carbonWindow;
-       bool open;
-
-public:
-
-       gdPluginWindowGUImac(Plugin *pPlugin);
-       ~gdPluginWindowGUImac();
-};
-
-#endif
-#endif
-
 #endif // include guard
 
 #endif // #ifdef WITH_VST
index c53d5014c0bbeaa67e222719c21914de879df4f4..75e49e5baa776b61f1566d34298bba66d6497ca8 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_warnings
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
 
 
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "../../utils/gui.h"
 #include "../../core/const.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
+#include "window.h"
 #include "gd_warnings.h"
 
 
-void gdAlert(const char *c) {
+void gdAlert(const char *c)
+{
        Fl_Window *modal = new Fl_Window(
                        (Fl::w() / 2) - 150,
                        (Fl::h() / 2) - 47,
                        300, 90, "Alert");
        modal->set_modal();
        modal->begin();
-               gBox *box = new gBox(10, 10, 280, 40, c);
-               gClick *b = new gClick(210, 60, 80, 20, "Close");
+               geBox *box = new geBox(10, 10, 280, 40, c);
+               geButton *b = new geButton(210, 60, 80, 20, "Close");
        modal->end();
        box->labelsize(GUI_FONT_SIZE_BASE);
        b->callback(__cb_window_closer, (void *)modal);
@@ -49,16 +54,17 @@ void gdAlert(const char *c) {
 }
 
 
-int gdConfirmWin(const char *title, const char *msg) {
+int gdConfirmWin(const char *title, const char *msg)
+{
        Fl_Window *win = new Fl_Window(
                        (Fl::w() / 2) - 150,
                        (Fl::h() / 2) - 47,
                        300, 90, title);
        win->set_modal();
        win->begin();
-               new gBox(10, 10, 280, 40, msg);
-               gClick *ok = new gClick(212, 62, 80, 20, "Ok");
-               gClick *ko = new gClick(124, 62, 80, 20, "Cancel");
+               new geBox(10, 10, 280, 40, msg);
+               geButton *ok = new geButton(212, 62, 80, 20, "Ok");
+               geButton *ko = new geButton(124, 62, 80, 20, "Cancel");
        win->end();
        ok->shortcut(FL_Enter);
        gu_setFavicon(win);
index 13d3b4e5a6bbd924bf38719e183a1f0471a27b75..cd770d48bee09bd9a38e1c5f1cb27a3d8a1d31be 100644 (file)
@@ -1,12 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_warnings
+ * -----------------------------------------------------------------------------
  *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
 
 #ifndef GD_WARNINGS_H
 #define GD_WARNINGS_H
 
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include <FL/Fl_Box.H>
-#include "../elems/ge_mixed.h"
-#include "../../utils/gui.h"
-
 
 void gdAlert(const char *c);
-
 int  gdConfirmWin(const char *title, const char *msg);
 
+
 #endif
index 256c9d5c06de1ad9309059782776238c78c7c463..5ec70b42d4377399caeeb11d66f25c26888be620 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiInputBase
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "midiInputBase.h"
 
 
-extern KernelMidi G_KernelMidi;
-
-
 using std::string;
+using namespace giada::m;
 
 
 gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char *title)
-       : gWindow(x, y, w, h, title)
+       : gdWindow(x, y, w, h, title)
 {
 }
 
@@ -50,7 +46,7 @@ gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char *title)
 
 gdMidiInputBase::~gdMidiInputBase()
 {
-       G_KernelMidi.stopMidiLearn();
+       kernelMidi::stopMidiLearn();
 }
 
 
@@ -59,7 +55,7 @@ gdMidiInputBase::~gdMidiInputBase()
 
 void gdMidiInputBase::stopMidiLearn(geMidiLearner *learner)
 {
-       G_KernelMidi.stopMidiLearn();
+       kernelMidi::stopMidiLearn();
        learner->updateValue();
 }
 
index 48f213b5474467dce1b563e7d5d443c0769a67cf..54b09f825c11a3905985930c62736a351a1f244f 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiInputBase
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #define GD_MIDI_INPUT_BASE_H
 
 
-#include "../../elems/ge_window.h"
+#include "../window.h"
+
+
+class geButton;
+class geMidiLearner;
 
 
-class gdMidiInputBase : public gWindow
+class gdMidiInputBase : public gdWindow
 {
 protected:
 
-       class gClick *ok;
+       geButton *ok;
 
-       void stopMidiLearn(class geMidiLearner *l);
+       void stopMidiLearn(geMidiLearner *l);
 
        /* cb_learn
         * callback attached to kernelMidi to learn various actions. */
index 6bb75e3bf1194e80c7f038a101502f07c61afdee..224d40308adad4a77686758d5c5a906d9c4c9f67 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiInputChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
   #include "../../../core/plugin.h"
 #endif
 #include "../../../utils/string.h"
-#include "../../elems/ge_mixed.h"
 #include "../../elems/midiLearner.h"
 #include "../../elems/basics/scroll.h"
+#include "../../elems/basics/box.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
 #include "midiInputChannel.h"
 
 
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-extern Conf       G_Conf;
-
-
 using std::string;
+using std::vector;
+using namespace giada::m;
 
 
 gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
-       :       gdMidiInputBase(G_Conf.midiInputX, G_Conf.midiInputY, G_Conf.midiInputW,
-      G_Conf.midiInputH, "MIDI Input Setup"),
+       :       gdMidiInputBase(conf::midiInputX, conf::midiInputY, conf::midiInputW,
+      conf::midiInputH, "MIDI Input Setup"),
                ch(ch)
 {
   string title = "MIDI Input Setup (channel " + gu_itoa(ch->index+1) + ")";
        label(title.c_str());
   size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
 
-       enable = new gCheck(8, 8, 120, 20, "enable MIDI input");
+       enable = new geCheck(8, 8, 120, 20, "enable MIDI input");
 
-  container = new geScroll(8, enable->y()+enable->h()+4, w()-16, h()-70);
+  container = new geScroll(8, enable->y()+enable->h()+4, w()-16, h()-68);
   container->begin();
 
     addChannelLearners();
@@ -73,10 +69,18 @@ gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
 
   container->end();
 
-       ok = new gButton(w()-88, container->y()+container->h()+8, 80, 20, "Close");
-       ok->callback(cb_close, (void*)this);
+  Fl_Group *groupButtons = new Fl_Group(8, container->y()+container->h()+8, container->w(), 20);
+  groupButtons->begin();
+
+    geBox *spacer = new geBox(groupButtons->x(), groupButtons->y(), 100, 20);  // spacer window border <-> buttons
+         ok = new geButton(w()-88, groupButtons->y(), 80, 20, "Close");
+
+  groupButtons->resizable(spacer);
+  groupButtons->end();
+
+  ok->callback(cb_close, (void*)this);
 
-       enable->value(ch->midiIn);
+  enable->value(ch->midiIn);
        enable->callback(cb_enable, (void*)this);
 
   resizable(container);
@@ -92,10 +96,10 @@ gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
 
 gdMidiInputChannel::~gdMidiInputChannel()
 {
-  G_Conf.midiInputX = x();
-  G_Conf.midiInputY = y();
-       G_Conf.midiInputW = w();
-       G_Conf.midiInputH = h();
+  conf::midiInputX = x();
+  conf::midiInputY = y();
+       conf::midiInputW = w();
+       conf::midiInputH = h();
 }
 
 
@@ -108,7 +112,7 @@ void gdMidiInputChannel::addChannelLearners()
   pack->spacing(4);
   pack->begin();
 
-    gBox *header = new gBox(0, 0, LEARNER_WIDTH, 20, "channel");
+    geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, "channel");
     header->box(FL_BORDER_BOX);
     new geMidiLearner(0, 0, LEARNER_WIDTH, "key press",   cb_learn, &ch->midiInKeyPress);
     new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", cb_learn, &ch->midiInKeyRel);
@@ -133,7 +137,7 @@ void gdMidiInputChannel::addChannelLearners()
 
 void gdMidiInputChannel::addPluginLearners()
 {
-  vector <Plugin *> *plugins = G_PluginHost.getStack(PluginHost::CHANNEL, ch);
+  vector <Plugin *> *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
   for (unsigned i=0; i<plugins->size(); i++) {
 
     Fl_Pack *pack = new Fl_Pack(container->x() + ((i + 1) * (LEARNER_WIDTH + 8)),
@@ -143,7 +147,7 @@ void gdMidiInputChannel::addPluginLearners()
 
       Plugin *plugin = plugins->at(i);
 
-      gBox *header = new gBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
+      geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
       header->box(FL_BORDER_BOX);
 
       for (int k=0; k<plugin->getNumParameters(); k++)
index 462a1580cfc99762dbc0c2cd93f59631819d013d..f2e9d33febbba0b639010c69501252814144faa1 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -43,7 +43,7 @@ private:
        class Channel *ch;
 
   class geScroll *container;
-       class gCheck   *enable;
+       class geCheck   *enable;
 
        //gVector <geMidiLearner *> items; // plugins parameters
 
index e7a8defbc6982ff6fd7c506f1e82890d5d40667a..694f585c5b9e33d44aab557d712d8f35407df4c3 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiInputMaster
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include "../../../utils/gui.h"
 #include "../../../core/conf.h"
-#include "../../elems/ge_mixed.h"
 #include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
 #include "midiInputMaster.h"
 
 
-extern Conf G_Conf;
+using namespace giada::m;
 
 
 gdMidiInputMaster::gdMidiInputMaster()
@@ -42,16 +40,16 @@ gdMidiInputMaster::gdMidiInputMaster()
 {
        set_modal();
 
-       new geMidiLearner(8,   8, w()-16, "rewind",           &cb_learn, &G_Conf.midiInRewind);
-       new geMidiLearner(8,  32, w()-16, "play/stop",        &cb_learn, &G_Conf.midiInStartStop);
-       new geMidiLearner(8,  56, w()-16, "action recording", &cb_learn, &G_Conf.midiInActionRec);
-       new geMidiLearner(8,  80, w()-16, "input recording",  &cb_learn, &G_Conf.midiInInputRec);
-       new geMidiLearner(8, 104, w()-16, "metronome",        &cb_learn, &G_Conf.midiInMetronome);
-       new geMidiLearner(8, 128, w()-16, "input volume",     &cb_learn, &G_Conf.midiInVolumeIn);
-       new geMidiLearner(8, 152, w()-16, "output volume",    &cb_learn, &G_Conf.midiInVolumeOut);
-       new geMidiLearner(8, 176, w()-16, "sequencer ×2",     &cb_learn, &G_Conf.midiInBeatDouble);
-       new geMidiLearner(8, 200, w()-16, "sequencer ÷2",     &cb_learn, &G_Conf.midiInBeatHalf);
-       ok = new gButton(w()-88, 228, 80, 20, "Close");
+       new geMidiLearner(8,   8, w()-16, "rewind",           &cb_learn, &conf::midiInRewind);
+       new geMidiLearner(8,  32, w()-16, "play/stop",        &cb_learn, &conf::midiInStartStop);
+       new geMidiLearner(8,  56, w()-16, "action recording", &cb_learn, &conf::midiInActionRec);
+       new geMidiLearner(8,  80, w()-16, "input recording",  &cb_learn, &conf::midiInInputRec);
+       new geMidiLearner(8, 104, w()-16, "metronome",        &cb_learn, &conf::midiInMetronome);
+       new geMidiLearner(8, 128, w()-16, "input volume",     &cb_learn, &conf::midiInVolumeIn);
+       new geMidiLearner(8, 152, w()-16, "output volume",    &cb_learn, &conf::midiInVolumeOut);
+       new geMidiLearner(8, 176, w()-16, "sequencer ×2",     &cb_learn, &conf::midiInBeatDouble);
+       new geMidiLearner(8, 200, w()-16, "sequencer ÷2",     &cb_learn, &conf::midiInBeatHalf);
+       ok = new geButton(w()-88, 228, 80, 20, "Close");
 
        ok->callback(cb_close, (void*)this);
 
index 55a4cd537b5a236d0610982cc5814fa32e54336e..52bd54666940119c2bcf70f4530cfdbb50318524 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index d8afd77dbccf5864d760471f3f5a59513d2a1b0f..e293d5862774da82fba60c66f6dc283e503836fe 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_midiOutput
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "midiOutputBase.h"
 
 
-extern KernelMidi G_KernelMidi;
+using namespace giada::m;
 
 
 gdMidiOutputBase::gdMidiOutputBase(int w, int h)
-       : gWindow(w, h, "Midi Output Setup")
+       : gdWindow(w, h, "Midi Output Setup")
 {
 }
 
@@ -46,7 +44,7 @@ gdMidiOutputBase::gdMidiOutputBase(int w, int h)
 
 void gdMidiOutputBase::stopMidiLearn(geMidiLearner *learner)
 {
-       G_KernelMidi.stopMidiLearn();
+       kernelMidi::stopMidiLearn();
        learner->updateValue();
 }
 
index fd639d4033e34aa87e81181422f9e5a142c15f21..13a037503a549695920b1b143cabd6ab990f7802 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gd_midiOutput
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include <FL/Fl.H>
-#include "../../elems/ge_window.h"
+#include "../window.h"
+
+
+class geButton;
+class geCheck;
+class geMidiLearner;
 
 
 /* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI
@@ -45,20 +48,20 @@ In addition MidiOutputMidiCh has the MIDI message output box. */
 /* TODO - gdMidiOutput is almost the same thing of gdMidiInput. Create another
 parent class gdMidiIO to inherit from */
 
-class gdMidiOutputBase : public gWindow
+class gdMidiOutputBase : public gdWindow
 {
 protected:
 
-       class gClick *close;
-       class gCheck *enableLightning;
+       geButton *close;
+       geCheck   *enableLightning;
 
-       void stopMidiLearn(class geMidiLearner *l);
+       void stopMidiLearn(geMidiLearner *l);
 
        /* cb_learn
         * callback attached to kernelMidi to learn various actions. */
 
        static void cb_learn  (uint32_t msg, void *data);
-       inline void __cb_learn(uint32_t *param, uint32_t msg, class geMidiLearner *l);
+       inline void __cb_learn(uint32_t *param, uint32_t msg, geMidiLearner *l);
 
        /* cb_close
        close current window. */
index 08954dda347a134a12b8c525f4104101b5e94830..be4bfd0583a10848fedbed15024fbc6216bf5265 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiOutputMidiCh
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include "../../../core/midiChannel.h"
 #include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
 #include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
+#include "../../elems/basics/choice.h"
 #include "../../elems/mainWindow/keyboard/channel.h"
 #include "midiOutputMidiCh.h"
 
@@ -41,15 +41,15 @@ gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel *ch)
        setTitle(ch->index+1);
        begin();
 
-       enableOut   = new gCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
-       chanListOut = new gChoice(w()-108, y()+8, 100, 20);
+       enableOut   = new geCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
+       chanListOut = new geChoice(w()-108, y()+8, 100, 20);
 
-       enableLightning = new gCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
+       enableLightning = new geCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
        new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+8,  w()-16, "playing", cb_learn, &ch->midiOutLplaying);
        new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",    cb_learn, &ch->midiOutLmute);
        new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",    cb_learn, &ch->midiOutLsolo);
 
-       close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
+       close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
 
        end();
 
index a0deb21b23459c86ee1f94c2eb11727e759c01bf..b6e9694a21367a1e28d58adf21e3dba16d0decc3 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -47,8 +47,8 @@ private:
        static void cb_close  (Fl_Widget *w, void *p);
        inline void __cb_close();
 
-       class gCheck  *enableOut;
-       class gChoice *chanListOut;
+       class geCheck  *enableOut;
+       class geChoice *chanListOut;
 
        class MidiChannel *ch;
 
index 4cbe682bd2ed2ca167b4cf20a6440dc9a472944e..ad68978b0ee67f3f3b099e28fa23f5b0691cbff3 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiOutputSampleCh
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -29,8 +27,9 @@
 
 #include "../../../core/sampleChannel.h"
 #include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
 #include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
 #include "midiOutputSampleCh.h"
 
 
@@ -39,12 +38,12 @@ gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel *ch)
 {
        setTitle(ch->index+1);
 
-       enableLightning = new gCheck(8, 8, 120, 20, "Enable MIDI lightning output");
+       enableLightning = new geCheck(8, 8, 120, 20, "Enable MIDI lightning output");
        new geMidiLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying);
        new geMidiLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",   cb_learn, &ch->midiOutLmute);
        new geMidiLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",   cb_learn, &ch->midiOutLsolo);
 
-       close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
+       close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
        close->callback(cb_close, (void*)this);
 
        enableLightning->value(ch->midiOutL);
index b229ca881ff982eb53594a2469e4723de394b390..7050c95d7503e29686839231054b6c1304f77333 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiOutputSampleCh
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "midiOutputBase.h"
 
 
+class SampleChannel;
+
+
 class gdMidiOutputSampleCh : public gdMidiOutputBase
 {
 private:
 
-       class SampleChannel *ch;
+       SampleChannel *ch;
 
        /* __cb_close
        override parent method, we need to do more stuff on close. */
@@ -48,7 +49,7 @@ private:
 
 public:
 
-       gdMidiOutputSampleCh(class SampleChannel *ch);
+       gdMidiOutputSampleCh(SampleChannel *ch);
 };
 
 #endif
diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp
new file mode 100644 (file)
index 0000000..987b61e
--- /dev/null
@@ -0,0 +1,244 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cmath>
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <FL/fl_draw.H>
+#include "../../glue/channel.h"
+#include "../../core/waveFx.h"
+#include "../../core/conf.h"
+#include "../../core/const.h"
+#include "../../core/graphics.h"
+#include "../../core/sampleChannel.h"
+#include "../../core/mixer.h"
+#include "../../core/wave.h"
+#include "../../core/clock.h"
+#include "../../utils/gui.h"
+#include "../../utils/string.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/input.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/dial.h"
+#include "../elems/basics/box.h"
+#include "../elems/basics/check.h"
+#include "../elems/sampleEditor/waveform.h"
+#include "../elems/sampleEditor/waveTools.h"
+#include "../elems/sampleEditor/volumeTool.h"
+#include "../elems/sampleEditor/boostTool.h"
+#include "../elems/sampleEditor/panTool.h"
+#include "../elems/sampleEditor/pitchTool.h"
+#include "../elems/sampleEditor/rangeTool.h"
+#include "../elems/mainWindow/keyboard/channel.h"
+#include "gd_warnings.h"
+#include "sampleEditor.h"
+
+
+using namespace giada::m;
+
+
+gdSampleEditor::gdSampleEditor(SampleChannel *ch)
+  : gdWindow(640, 480),
+    ch(ch)
+{
+  begin();
+
+  /* top bar: grid and zoom tools */
+
+  Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
+  bar->begin();
+    grid    = new geChoice(bar->x(), bar->y(), 50, 20);
+    snap    = new geCheck(grid->x()+grid->w()+4, bar->y(), 12, 12);
+    sep1    = new geBox(snap->x()+snap->w()+4, bar->y(), 506, 20);
+    zoomOut = new geButton(sep1->x()+sep1->w()+4, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+    zoomIn  = new geButton(zoomOut->x()+zoomOut->w()+4, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+  bar->end();
+  bar->resizable(sep1);
+
+  /* waveform */
+
+  waveTools = new geWaveTools(8, bar->y()+bar->h()+8, w()-16, h()-128, ch);
+  waveTools->end();
+
+  /* other tools */
+
+  Fl_Group *row1 = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 20);
+  row1->begin();
+    volumeTool = new geVolumeTool(row1->x(), row1->y(), ch);
+    boostTool  = new geBoostTool(volumeTool->x()+volumeTool->w()+4, row1->y(), ch);
+    panTool    = new gePanTool(boostTool->x()+boostTool->w()+4, row1->y(), ch);
+  row1->end();
+  row1->resizable(0);
+
+  Fl_Group *row2 = new Fl_Group(8, row1->y()+row1->h()+8, 800, 20);
+  row2->begin();
+    pitchTool = new gePitchTool(row2->x(), row2->y(), ch);
+  row2->end();
+  row2->resizable(0);
+
+  Fl_Group *row3 = new Fl_Group(8, row2->y()+row2->h()+8, w()-16, 20);
+  row3->begin();
+    rangeTool = new geRangeTool(row3->x(), row3->y(), ch);
+    sep2      = new geBox(rangeTool->x()+rangeTool->w()+4, row3->y(), 246, 20);
+    reload    = new geButton(sep2->x()+sep2->w()+4, row3->y(), 70, 20, "Reload");
+  row3->end();
+  row3->resizable(sep2);
+
+  end();
+
+  /* grid tool setup */
+
+  grid->add("(off)");
+  grid->add("2");
+  grid->add("3");
+  grid->add("4");
+  grid->add("6");
+  grid->add("8");
+  grid->add("16");
+  grid->add("32");
+  grid->add("64");
+  grid->value(grid->find_item(gu_itoa(conf::sampleEditorGridVal).c_str()));
+  grid->callback(cb_changeGrid, (void*)this);
+
+  snap->value(conf::sampleEditorGridOn);
+  snap->callback(cb_enableSnap, (void*)this);
+
+  /* TODO - redraw grid if != (off) */
+
+  reload->callback(cb_reload, (void*)this);
+
+  zoomOut->callback(cb_zoomOut, (void*)this);
+  zoomIn->callback(cb_zoomIn, (void*)this);
+
+  /* logical samples (aka takes) cannot be reloaded. So far. */
+
+  if (ch->wave->isLogical)
+    reload->deactivate();
+
+  gu_setFavicon(this);
+  size_range(640, 480);
+  resizable(waveTools);
+
+  label(ch->wave->name.c_str());
+
+  set_non_modal();
+
+  if (conf::sampleEditorX)
+    resize(conf::sampleEditorX, conf::sampleEditorY, conf::sampleEditorW, conf::sampleEditorH);
+
+  show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdSampleEditor::~gdSampleEditor()
+{
+  conf::sampleEditorX = x();
+  conf::sampleEditorY = y();
+  conf::sampleEditorW = w();
+  conf::sampleEditorH = h();
+  conf::sampleEditorGridVal = atoi(grid->text());
+  conf::sampleEditorGridOn  = snap->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::cb_reload    (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_reload(); }
+void gdSampleEditor::cb_zoomIn    (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_zoomIn(); }
+void gdSampleEditor::cb_zoomOut   (Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_zoomOut(); }
+void gdSampleEditor::cb_changeGrid(Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_changeGrid(); }
+void gdSampleEditor::cb_enableSnap(Fl_Widget *w, void *p) { ((gdSampleEditor*)p)->__cb_enableSnap(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_enableSnap()
+{
+  waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_reload()
+{
+  if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
+    return;
+
+  /* no need for glue_loadChan, there's no gui to refresh */
+
+  ch->load(ch->wave->pathfile.c_str(), conf::samplerate, conf::rsmpQuality);
+
+  glue_setBoost(ch, G_DEFAULT_BOOST);
+  glue_setPitch(ch, G_DEFAULT_PITCH);
+  glue_setPanning(ch, 1.0f);
+
+  panTool->refresh();
+  boostTool->refresh();
+
+  waveTools->waveform->stretchToWindow();
+  waveTools->updateWaveform();
+
+  glue_setBeginEndChannel(ch, 0, ch->wave->size);
+
+  redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_zoomIn()
+{
+  waveTools->waveform->setZoom(-1);
+  waveTools->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_zoomOut()
+{
+  waveTools->waveform->setZoom(0);
+  waveTools->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_changeGrid()
+{
+  waveTools->waveform->setGridLevel(atoi(grid->text()));
+}
diff --git a/src/gui/dialogs/sampleEditor.h b/src/gui/dialogs/sampleEditor.h
new file mode 100644 (file)
index 0000000..d0fca54
--- /dev/null
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GD_EDITOR_H
+#define GD_EDITOR_H
+
+
+#include "window.h"
+
+
+class SampleChannel;
+class geButton;
+class geWaveTools;
+class geVolumeTool;
+class geBoostTool;
+class gePanTool;
+class gePitchTool;
+class geRangeTool;
+class geChoice;
+class geCheck;
+class geBox;
+class geButton;
+
+
+class gdSampleEditor : public gdWindow
+{
+private:
+
+       static void cb_reload        (Fl_Widget *w, void *p);
+       static void cb_zoomIn        (Fl_Widget *w, void *p);
+       static void cb_zoomOut       (Fl_Widget *w, void *p);
+       static void cb_changeGrid    (Fl_Widget *w, void *p);
+       static void cb_enableSnap    (Fl_Widget *w, void *p);
+       inline void __cb_reload();
+       inline void __cb_zoomIn();
+       inline void __cb_zoomOut();
+       inline void __cb_changeGrid();
+       inline void __cb_enableSnap();
+
+public:
+
+       gdSampleEditor(SampleChannel *ch);
+       ~gdSampleEditor();
+
+       geChoice     *grid;
+       geCheck      *snap;
+       geBox        *sep1;
+       geButton     *zoomIn;
+       geButton     *zoomOut;
+       
+       geWaveTools  *waveTools;
+
+       geVolumeTool *volumeTool;
+       geBoostTool  *boostTool;
+       gePanTool    *panTool;
+
+       gePitchTool  *pitchTool;
+
+       geRangeTool  *rangeTool;
+       geBox        *sep2;
+       geButton     *reload;
+
+       SampleChannel *ch;
+};
+
+
+#endif
diff --git a/src/gui/dialogs/window.cpp b/src/gui/dialogs/window.cpp
new file mode 100644 (file)
index 0000000..5797feb
--- /dev/null
@@ -0,0 +1,198 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../utils/log.h"
+#include "window.h"
+
+
+void __cb_window_closer(Fl_Widget *v, void *p)
+{
+  delete (Fl_Window*) p;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::gdWindow(int x, int y, int w, int h, const char *title, int id)
+       : Fl_Double_Window(x, y, w, h, title), id(id), parent(nullptr)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::gdWindow(int w, int h, const char *title, int id)
+       : Fl_Double_Window(w, h, title), id(id), parent(nullptr)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::~gdWindow()
+{
+       /* delete all subwindows in order to empty the stack */
+
+       for (unsigned i=0; i<subWindows.size(); i++)
+               delete subWindows.at(i);
+       subWindows.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* this is the default callback of each window, fired when the user closes
+ * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */
+
+void gdWindow::cb_closeChild(Fl_Widget *v, void *p)
+{
+       gdWindow *child = (gdWindow*) v;
+       if (child->getParent() != nullptr)
+               (child->getParent())->delSubWindow(child);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::addSubWindow(gdWindow *w)
+{
+       /** TODO - useless: delete ---------------------------------------- */
+       for (unsigned i=0; i<subWindows.size(); i++)
+               if (w->getId() == subWindows.at(i)->getId()) {
+                       //gu_log("[gdWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
+                       delete w;
+                       return;
+               }
+       /** --------------------------------------------------------------- */
+
+       w->setParent(this);
+       w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
+       subWindows.push_back(w);
+       //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::delSubWindow(gdWindow *w)
+{
+       for (unsigned i=0; i<subWindows.size(); i++)
+               if (w->getId() == subWindows.at(i)->getId()) {
+                       delete subWindows.at(i);
+                       subWindows.erase(subWindows.begin() + i);
+                       //debug();
+                       return;
+               }
+       //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::delSubWindow(int id)
+{
+       for (unsigned i=0; i<subWindows.size(); i++)
+               if (subWindows.at(i)->getId() == id) {
+                       delete subWindows.at(i);
+                       subWindows.erase(subWindows.begin() + i);
+                       //debug();
+                       return;
+               }
+       //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gdWindow::getId()
+{
+       return id;
+}
+
+
+void gdWindow::setId(int id)
+{
+       this->id = id;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::debug()
+{
+       gu_log("---- window stack (id=%d): ----\n", getId());
+       for (unsigned i=0; i<subWindows.size(); i++)
+               gu_log("[gdWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
+       gu_log("----\n");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow *gdWindow::getParent()
+{
+       return parent;
+}
+
+
+void gdWindow::setParent(gdWindow *w)
+{
+       parent = w;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gdWindow::hasWindow(int id)
+{
+       for (unsigned i=0; i<subWindows.size(); i++)
+               if (id == subWindows.at(i)->getId())
+                       return true;
+       return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow *gdWindow::getChild(int id)
+{
+       for (unsigned i=0; i<subWindows.size(); i++)
+               if (id == subWindows.at(i)->getId())
+                       return subWindows.at(i);
+       return nullptr;
+}
diff --git a/src/gui/dialogs/window.h b/src/gui/dialogs/window.h
new file mode 100644 (file)
index 0000000..ec17e5b
--- /dev/null
@@ -0,0 +1,77 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GD_WINDOW_H
+#define GD_WINDOW_H
+
+
+#include <vector>
+#include <FL/Fl_Double_Window.H>
+
+
+/* cb_window_closer
+ * callback for when closing windows. Deletes the widget (delete). */
+
+void __cb_window_closer(Fl_Widget *v, void *p);
+
+
+class gdWindow : public Fl_Double_Window
+{
+protected:
+
+       std::vector <gdWindow*> subWindows;
+       int id;
+       gdWindow *parent;
+
+public:
+
+       gdWindow(int x, int y, int w, int h, const char *title=0, int id=0);
+       gdWindow(int w, int h, const char *title=0, int id=0);
+       ~gdWindow();
+
+       static void cb_closeChild(Fl_Widget *v, void *p);
+
+       void addSubWindow(gdWindow *w);
+       void delSubWindow(gdWindow *w);
+       void delSubWindow(int id);
+
+       int  getId();
+       void setId(int id);
+       void debug();
+
+       void     setParent(gdWindow *);
+       gdWindow *getParent();
+       gdWindow *getChild(int id);
+
+       /* hasWindow
+        * true if the window with id 'id' exists in the stack. */
+
+       bool hasWindow(int id);
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor.cpp b/src/gui/elems/actionEditor.cpp
deleted file mode 100644 (file)
index 065860d..0000000
+++ /dev/null
@@ -1,689 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <FL/fl_draw.H>
-#include "../../core/conf.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../glue/main.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "actionEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-extern Mixer         G_Mixer;
-extern Conf             G_Conf;
-extern Recorder      G_Recorder;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
-  : geBaseActionEditor(x, y, 200, 40, pParent),
-    ch                (ch),
-    selected          (NULL)
-{
-       size(pParent->totalWidth, h());
-
-       /* add actions when the window opens. Their position is zoom-based;
-        * each frame is / 2 because we don't care about stereo infos. */
-
-       for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
-               for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-
-                 Recorder::action *action = G_Recorder.global.at(i).at(j);
-
-      /* Don't show actions:
-      - that don't belong to the displayed channel (!= pParent->chan->index);
-      - that are covered by the grey area (> G_Mixer.totalFrames);
-      - of type ACTION_KILLCHAN in a SINGLE_PRESS channel. They cannot be
-        recorded in such mode, but they can exist if you change from another
-        mode to singlepress;
-      - of type ACTION_KEYREL in a SINGLE_PRESS channel. It's up to gAction to
-        find the other piece (namely frame_b)
-      - not of types ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN */
-
-      if ((action->chan != pParent->chan->index)                            ||
-          (G_Recorder.frames.at(i) > G_Mixer.totalFrames)                    ||
-          (action->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS)     ||
-          (action->type == ACTION_KEYREL && ch->mode == SINGLE_PRESS)       ||
-          (action->type & ~(ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN))
-      )
-        continue;
-
-                       int ax = x + (action->frame / pParent->zoom);
-                       gAction *a = new gAction(
-                                       ax,               // x
-                                       y + 4,            // y
-                                       h() - 8,          // h
-                                       action->frame,    // frame_a
-                                       i,                // n. of recordings
-                                       pParent,          // pointer to the pParent window
-                                       ch,               // pointer to SampleChannel
-                                       false,            // record = false: don't record it, we are just displaying it!
-                                       action->type);    // type of action
-                       add(a);
-               }
-       }
-       end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gAction *geActionEditor::getSelectedAction()
-{
-       for (int i=0; i<children(); i++) {
-               int action_x  = ((gAction*)child(i))->x();
-               int action_w  = ((gAction*)child(i))->w();
-               if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
-                       return (gAction*)child(i);
-       }
-       return NULL;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::updateActions()
-{
-       /* when zooming, don't delete and re-add actions, just MOVE them. This
-        * function shifts the action by a zoom factor. Those singlepress are
-        * stretched, as well */
-
-       gAction *a;
-       for (int i=0; i<children(); i++) {
-
-               a = (gAction*)child(i);
-               int newX = x() + (a->frame_a / pParent->zoom);
-
-               if (ch->mode == SINGLE_PRESS) {
-                       int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
-                       if (newW < gAction::MIN_WIDTH)
-                               newW = gAction::MIN_WIDTH;
-                       a->resize(newX, a->y(), newW, a->h());
-               }
-               else
-                       a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h());
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::draw()
-{
-       /* draw basic boundaries (+ beat bars) and hide the unused area. Then
-        * draw the children (the actions) */
-
-       baseDraw();
-
-       /* print label */
-
-       fl_color(COLOR_BG_1);
-       fl_font(FL_HELVETICA, 12);
-       if (active())
-               fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));  /// FIXME h() is too much!
-       else
-               fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));  /// FIXME h() is too much!
-
-       draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geActionEditor::handle(int e)
-{
-       int ret = Fl_Group::handle(e);
-
-       /* do nothing if the widget is deactivated. It could happen for loopmode
-        * channels */
-
-       if (!active())
-               return 1;
-
-       switch (e) {
-
-               case FL_DRAG: {
-
-      if (selected == NULL) {  // if you drag an empty area
-        ret = 1;
-        break;
-      }
-
-                       /* if onLeftEdge o onRightEdge are true it means that you're resizing
-                        * an action. Otherwise move the widget. */
-
-                       if (selected->onLeftEdge || selected->onRightEdge) {
-
-                               /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
-                                * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
-
-                               if (selected->onRightEdge) {
-
-                                       int aw = Fl::event_x()-selected->x();
-                                       int ah = selected->h();
-
-                                       if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH)
-                                               aw = gAction::MIN_WIDTH;
-                                       else
-                                       if (Fl::event_x() > pParent->coverX)
-                                               aw = pParent->coverX-selected->x();
-
-                                       selected->size(aw, ah);
-                               }
-                               else {
-
-                                       int ax = Fl::event_x();
-                                       int ay = selected->y();
-                                       int aw = selected->x()-Fl::event_x()+selected->w();
-                                       int ah = selected->h();
-
-                                       if (Fl::event_x() < x()) {
-                                               ax = x();
-                                               aw = selected->w()+selected->x()-x();
-                                       }
-                                       else
-                                       if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) {
-                                               ax = selected->x()+selected->w()-gAction::MIN_WIDTH;
-                                               aw = gAction::MIN_WIDTH;
-                                       }
-                                       selected->resize(ax, ay, aw, ah);
-                               }
-                       }
-
-      /* move the widget around */
-
-                       else {
-                               int real_x = Fl::event_x() - actionPickPoint;
-                               if (real_x < x())                                  // don't go beyond the left border
-                                       selected->position(x(), selected->y());
-                               else
-                               if (real_x+selected->w() > pParent->coverX+x())         // don't go beyond the right border
-                                       selected->position(pParent->coverX+x()-selected->w(), selected->y());
-                               else {
-                                       if (pParent->gridTool->isOn()) {
-                                               int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
-                                               selected->position(snpx, selected->y());
-                                       }
-                                       else
-                                               selected->position(real_x, selected->y());
-                               }
-                       }
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_PUSH:   {
-
-                       if (Fl::event_button1()) {
-
-                               /* avoid at all costs two overlapping actions. We use 'selected' because
-                                * the selected action can be reused in FL_DRAG, in case you want to move
-                                * it. */
-
-                               selected = getSelectedAction();
-
-                               if (selected == NULL) {
-
-                                       /* avoid click on grey area */
-
-                                       if (Fl::event_x() >= pParent->coverX) {
-                                               ret = 1;
-                                               break;
-                                       }
-
-                                       /* snap function, if enabled */
-
-                                       int ax = Fl::event_x();
-                                       int fx = (ax - x()) * pParent->zoom;
-                                       if (pParent->gridTool->isOn()) {
-                                               ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
-                                               fx = pParent->gridTool->getSnapFrame(ax-x());
-
-                                               /* with snap=on an action can fall onto another */
-
-                                               if (actionCollides(fx)) {
-                                                       ret = 1;
-                                                       break;
-                                               }
-                                       }
-
-                                       gAction *a = new gAction(
-                                                       ax,                                   // x
-                                                       y()+4,                                // y
-                                                       h()-8,                                // h
-                                                       fx,                                                                                                                                             // frame_a
-                                                       G_Recorder.frames.size()-1,            // n. of actions recorded
-                                                       pParent,                              // pParent window pointer
-                                                       ch,                                   // pointer to SampleChannel
-                                                       true,                                 // record = true: record it!
-                                                       pParent->getActionType());            // type of action
-                                       add(a);
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); // mainWindow update
-                                       redraw();
-                                       ret = 1;
-                               }
-                               else {
-                                       actionOriginalX = selected->x();
-                                       actionOriginalW = selected->w();
-                                       actionPickPoint = Fl::event_x() - selected->x();
-                                       ret = 1;   // for dragging
-                               }
-                       }
-                       else
-                       if (Fl::event_button3()) {
-                               gAction *a = getSelectedAction();
-                               if (a != NULL) {
-                                       a->delAction();
-                                       remove(a);
-                                       delete a;
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel);
-                                       redraw();
-                                       ret = 1;
-                               }
-                       }
-                       break;
-               }
-               case FL_RELEASE: {
-
-                       if (selected == NULL) {
-                               ret = 1;
-                               break;
-                       }
-
-                       /* noChanges = true when you click on an action without doing anything
-                        * (dragging or moving it). */
-
-                       bool noChanges = false;
-                       if (actionOriginalX == selected->x())
-                               noChanges = true;
-                       if (ch->mode == SINGLE_PRESS &&
-                                       actionOriginalX+actionOriginalW != selected->x()+selected->w())
-                               noChanges = false;
-
-                       if (noChanges) {
-                               ret = 1;
-                               selected = NULL;
-                               break;
-                       }
-
-                       /* step 1: check if the action doesn't overlap with another one.
-                        * In case of overlap the moved action returns to the original X
-                        * value ("actionOriginalX"). */
-
-                       bool overlap = false;
-                       for (int i=0; i<children() && !overlap; i++) {
-
-                               /* never check against itself. */
-
-                               if ((gAction*)child(i) == selected)
-                                       continue;
-
-                               int action_x  = ((gAction*)child(i))->x();
-                               int action_w  = ((gAction*)child(i))->w();
-                               if (ch->mode == SINGLE_PRESS) {
-
-                                       /* when 2 segments overlap?
-                                        * start = the highest value between the two starting points
-                                        * end   = the lowest value between the two ending points
-                                        * if start < end then there's an overlap of end-start pixels. */
-
-                                        int start = action_x >= selected->x() ? action_x : selected->x();
-                                        int end   = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
-                                        if (start < end) {
-                                               selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
-                                               redraw();
-                                               overlap = true;   // one overlap: stop checking
-                                       }
-                               }
-                               else {
-                                       if (selected->x() == action_x) {
-                                               selected->position(actionOriginalX, selected->y());
-                                               redraw();
-                                               overlap = true;   // one overlap: stop checking
-                                       }
-                               }
-                       }
-
-                       /* step 2: no overlap? then update the coordinates of the action, ie
-                        * delete the previous rec and create a new one.
-                        * Anyway the selected action becomes NULL, because when you release
-                        * the mouse button the dragging process ends. */
-
-                       if (!overlap) {
-                               if (pParent->gridTool->isOn()) {
-                                       int f = pParent->gridTool->getSnapFrame(selected->absx());
-                                       selected->moveAction(f);
-                               }
-                               else
-                                       selected->moveAction();
-                       }
-                       selected = NULL;
-                       ret = 1;
-                       break;
-               }
-       }
-
-       return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool geActionEditor::actionCollides(int frame)
-{
-       /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
-        * overlap the head (frame) of the new one. First the general case, yet. */
-
-       bool collision = false;
-
-       for (int i=0; i<children() && !collision; i++)
-               if (((gAction*) child(i))->frame_a == frame)
-                       collision = true;
-
-       if (ch->mode == SINGLE_PRESS) {
-               for (int i=0; i<children() && !collision; i++) {
-                       gAction *c = ((gAction*) child(i));
-                       if (frame <= c->frame_b && frame >= c->frame_a)
-                               collision = true;
-               }
-       }
-
-       return collision;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-/** TODO - index is useless?
- *  TODO - pass a record::action pointer and let gAction compute values */
-
-gAction::gAction(int X, int Y, int H, int frame_a, unsigned index, gdActionEditor *parent, SampleChannel *ch, bool record, char type)
-: Fl_Box     (X, Y, MIN_WIDTH, H),
-  selected   (false),
-  index      (index),
-  parent     (parent),
-  ch         (ch),
-  type       (type),
-  frame_a    (frame_a),
-  onRightEdge(false),
-  onLeftEdge (false)
-{
-       /* bool 'record' defines how to understand the action.
-        * record = false: don't record it, just show it. It happens when you
-        * open the editor with some actions to be shown.
-        *
-        * record = true: record it AND show it. It happens when you click on
-        * an empty area in order to add a new actions. First you record it
-        * (addAction()) then you show it (FLTK::Draw()) */
-
-       if (record)
-               addAction();
-
-       /* in order to show a singlepress action we must compute the frame_b. We
-        * do that after the possible recording, otherwise we don't know which
-        * key_release is associated. */
-
-       if (ch->mode == SINGLE_PRESS && type == ACTION_KEYPRESS) {
-               Recorder::action *a2 = NULL;
-               G_Recorder.getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2);
-               if (a2) {
-                       frame_b = a2->frame;
-                       w((frame_b - frame_a)/parent->zoom);
-               }
-               else
-                       gu_log("[geActionEditor] frame_b not found! [%d:???]\n", frame_a);
-
-       /* a singlepress action narrower than 8 pixel is useless. So check it.
-        * Warning: if an action is 8 px narrow, it has no body space to drag
-        * it. It's up to the user to zoom in and drag it. */
-
-               if (w() < MIN_WIDTH)
-                       size(MIN_WIDTH, h());
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::draw()
-{
-       int color;
-       if (selected)  /// && geActionEditor !disabled
-               color = COLOR_BD_1;
-       else
-               color = COLOR_BG_2;
-
-       if (ch->mode == SINGLE_PRESS) {
-               fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
-       }
-       else {
-               if (type == ACTION_KILLCHAN)
-                       fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
-               else {
-                       fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
-                       if (type == ACTION_KEYPRESS)
-                               fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
-                       else
-                       if  (type == ACTION_KEYREL)
-                               fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
-               }
-       }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::handle(int e)
-{
-       /* ret = 0 sends the event to the parent window. */
-
-       int ret = 0;
-
-       switch (e) {
-
-               case FL_ENTER: {
-                       selected = true;
-                       ret = 1;
-                       redraw();
-                       break;
-               }
-               case FL_LEAVE: {
-                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-                       selected = false;
-                       ret = 1;
-                       redraw();
-                       break;
-               }
-               case FL_MOVE: {
-
-                       /* handling of the two margins, left & right. 4 pixels are good enough */
-
-                       if (ch->mode == SINGLE_PRESS) {
-                               onLeftEdge  = false;
-                               onRightEdge = false;
-                               if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
-                                       onLeftEdge = true;
-                                       fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-                               }
-                               else
-                               if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
-                                       onRightEdge = true;
-                                       fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-                               }
-                               else
-                                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-                       }
-               }
-       }
-
-       return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::addAction()
-{
-       /* always check frame parity */
-
-       if (frame_a % 2 != 0)
-               frame_a++;
-
-       /* anatomy of an action
-        * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is
-        *     a       b      the right margin, the ACTION_KEYREL. This is the
-        * theory behind the singleshot.press actions; for any other kind the
-        * (b) is just a graphical and meaningless point. */
-
-       if (ch->mode == SINGLE_PRESS) {
-               G_Recorder.rec(parent->chan->index, ACTION_KEYPRESS, frame_a);
-               G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_a+4096);
-               //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
-       }
-       else {
-               G_Recorder.rec(parent->chan->index, parent->getActionType(), frame_a);
-               //gu_log("action added, [%d]\n", frame_a);
-       }
-
-  parent->chan->hasActions = true;
-
-       G_Recorder.sortActions();
-
-       index++; // important!
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::delAction()
-{
-       /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
-        * actions. */
-
-       if (ch->mode == SINGLE_PRESS) {
-               G_Recorder.deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS,
-      false, &G_Mixer.mutex_recs);
-               G_Recorder.deleteAction(parent->chan->index, frame_b, ACTION_KEYREL,
-      false, &G_Mixer.mutex_recs);
-       }
-       else
-               G_Recorder.deleteAction(parent->chan->index, frame_a, type, false,
-      &G_Mixer.mutex_recs);
-
-  parent->chan->hasActions = G_Recorder.hasActions(parent->chan->index);
-
-
-       /* restore the initial cursor shape, in case you delete an action and
-        * the double arrow (for resizing) is displayed */
-
-       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::moveAction(int frame_a)
-{
-       /* easy one: delete previous action and record the new ones. As usual,
-        * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
-        * value. */
-
-       delAction();
-
-       if (frame_a != -1)
-               this->frame_a = frame_a;
-       else
-               this->frame_a = xToFrame_a();
-
-
-       /* always check frame parity */
-
-       if (this->frame_a % 2 != 0)
-               this->frame_a++;
-
-       G_Recorder.rec(parent->chan->index, type, this->frame_a);
-
-       if (ch->mode == SINGLE_PRESS) {
-               frame_b = xToFrame_b();
-               G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_b);
-       }
-
-  parent->chan->hasActions = true;
-
-       G_Recorder.sortActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::absx()
-{
-       return x() - parent->ac->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_a()
-{
-       return (absx()) * parent->zoom;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_b()
-{
-       return (absx() + w()) * parent->zoom;
-}
diff --git a/src/gui/elems/actionEditor.h b/src/gui/elems/actionEditor.h
deleted file mode 100644 (file)
index 56f0ce8..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_ACTIONCHANNEL_H
-#define GE_ACTIONCHANNEL_H
-
-
-#include <FL/Fl.H>
-#include <FL/Fl_Box.H>
-#include "../../utils/gui.h"
-#include "../../core/mixer.h"
-#include "../../core/recorder.h"
-#include "baseActionEditor.h"
-
-
-class gAction : public Fl_Box
-{
-private:
-
-       bool                  selected;
-       unsigned              index;
-  class gdActionEditor *parent;   // pointer to parent (gActionEditor)
-       class SampleChannel  *ch;
-  char                  type;     // type of action
-
-public:
-
-       gAction(int x, int y, int h, int frame_a, unsigned index,
-                                 gdActionEditor *parent, class SampleChannel *ch, bool record,
-                           char type);
-       void draw();
-       int  handle(int e);
-       void addAction();
-       void delAction();
-
-       /* moveAction
-        * shift the action on the x-axis and update Recorder. If frame_a != -1
-        * use the new frame in input (used while snapping) */
-
-       void moveAction(int frame_a=-1);
-
-       /* absx
-        * x() is relative to scrolling position. absx() returns the absolute
-        * x value of the action, from the leftmost edge. */
-
-       int  absx();
-
-       /* xToFrame_a,b
-        * return the real frames of x() position */
-
-       int xToFrame_a();
-       int xToFrame_b();
-
-       int frame_a;  // initial frame (KEYPRESS for singlemode.press)
-       int frame_b;  // terminal frame (KEYREL for singlemode.press, null for others)
-
-       bool onRightEdge;
-       bool onLeftEdge;
-
-       static const int MIN_WIDTH = 8;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class geActionEditor : public geBaseActionEditor
-{
-
-private:
-
-       class SampleChannel *ch;
-
-       /* getSelectedAction
-        * get the action under the mouse. NULL if nothing found. */
-
-       gAction *getSelectedAction();
-
-       /* selected
-        * pointer to the selected action. Useful when dragging around. */
-
-       gAction *selected;
-
-       /* actionOriginalX, actionOriginalW
-        * x and w of the action, when moved. Useful for checking if the action
-        * overlaps another one: in that case the moved action returns to
-        * actionOriginalX (and to actionOriginalW if resized). */
-
-       int actionOriginalX;
-       int actionOriginalW;
-
-       /* actionPickPoint
-        * the precise x point in which the action has been picked with the mouse,
-        * before a dragging action. */
-
-       int actionPickPoint;
-
-
-       /* actionCollides
-        * true if an action collides with another. Used while adding new points
-        * with snap active.*/
-
-       bool actionCollides(int frame);
-
-public:
-
-       geActionEditor(int x, int y, gdActionEditor *pParent, class SampleChannel *ch);
-       void draw();
-       int  handle(int e);
-       void updateActions();
-};
-
-
-#endif
diff --git a/src/gui/elems/actionEditor/action.cpp b/src/gui/elems/actionEditor/action.cpp
new file mode 100644 (file)
index 0000000..d13d6d6
--- /dev/null
@@ -0,0 +1,292 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "actionEditor.h"
+#include "action.h"
+
+
+using namespace giada::m;
+
+
+/** TODO - index is useless?
+ *  TODO - pass a record::action pointer and let geAction compute values */
+
+geAction::geAction(int X, int Y, int H, int frame_a, unsigned index,
+  gdActionEditor *parent, SampleChannel *ch, bool record, char type)
+: Fl_Box     (X, Y, MIN_WIDTH, H),
+  selected   (false),
+  index      (index),
+  parent     (parent),
+  ch         (ch),
+  type       (type),
+  frame_a    (frame_a),
+  onRightEdge(false),
+  onLeftEdge (false)
+{
+       /* bool 'record' defines how to understand the action.
+        * record = false: don't record it, just show it. It happens when you
+        * open the editor with some actions to be shown.
+        *
+        * record = true: record it AND show it. It happens when you click on
+        * an empty area in order to add a new actions. First you record it
+        * (addAction()) then you show it (FLTK::Draw()) */
+
+       if (record)
+               addAction();
+
+       /* in order to show a singlepress action we must compute the frame_b. We
+        * do that after the possible recording, otherwise we don't know which
+        * key_release is associated. */
+
+       if (ch->mode == SINGLE_PRESS && type == G_ACTION_KEYPRESS) {
+               recorder::action *a2 = nullptr;
+               recorder::getNextAction(ch->index, G_ACTION_KEYREL, frame_a, &a2);
+               if (a2) {
+                       frame_b = a2->frame;
+                       w((frame_b - frame_a)/parent->zoom);
+               }
+               else
+                       gu_log("[geActionEditor] frame_b not found! [%d:???]\n", frame_a);
+
+       /* a singlepress action narrower than 8 pixel is useless. So check it.
+        * Warning: if an action is 8 px narrow, it has no body space to drag
+        * it. It's up to the user to zoom in and drag it. */
+
+               if (w() < MIN_WIDTH)
+                       size(MIN_WIDTH, h());
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::draw()
+{
+       int color;
+       if (selected)  /// && geActionEditor !disabled
+               color = COLOR_BD_1;
+       else
+               color = COLOR_BG_2;
+
+       if (ch->mode == SINGLE_PRESS) {
+               fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
+       }
+       else {
+               if (type == G_ACTION_KILL)
+                       fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+               else {
+                       fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+                       if (type == G_ACTION_KEYPRESS)
+                               fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
+                       else
+                       if  (type == G_ACTION_KEYREL)
+                               fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
+               }
+       }
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::handle(int e)
+{
+       /* ret = 0 sends the event to the parent window. */
+
+       int ret = 0;
+
+       switch (e) {
+
+               case FL_ENTER: {
+                       selected = true;
+                       ret = 1;
+                       redraw();
+                       break;
+               }
+               case FL_LEAVE: {
+                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+                       selected = false;
+                       ret = 1;
+                       redraw();
+                       break;
+               }
+               case FL_MOVE: {
+
+                       /* handling of the two margins, left & right. 4 pixels are good enough */
+
+                       if (ch->mode == SINGLE_PRESS) {
+                               onLeftEdge  = false;
+                               onRightEdge = false;
+                               if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
+                                       onLeftEdge = true;
+                                       fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+                               }
+                               else
+                               if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
+                                       onRightEdge = true;
+                                       fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+                               }
+                               else
+                                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::addAction()
+{
+       /* always check frame parity */
+
+       if (frame_a % 2 != 0)
+               frame_a++;
+
+       /* anatomy of an action
+        * ____[#######]_____ (a) is the left margin, G_ACTION_KEYPRESS. (b) is
+        *     a       b      the right margin, the G_ACTION_KEYREL. This is the
+        * theory behind the singleshot.press actions; for any other kind the
+        * (b) is just a graphical and meaningless point. */
+
+       if (ch->mode == SINGLE_PRESS) {
+               recorder::rec(parent->chan->index, G_ACTION_KEYPRESS, frame_a);
+               recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_a+4096);
+               //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
+       }
+       else {
+               recorder::rec(parent->chan->index, parent->getActionType(), frame_a);
+               //gu_log("action added, [%d]\n", frame_a);
+       }
+
+  parent->chan->hasActions = true;
+
+       recorder::sortActions();
+
+       index++; // important!
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::delAction()
+{
+       /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
+        * actions. */
+
+       if (ch->mode == SINGLE_PRESS) {
+               recorder::deleteAction(parent->chan->index, frame_a, G_ACTION_KEYPRESS,
+      false, &mixer::mutex_recs);
+               recorder::deleteAction(parent->chan->index, frame_b, G_ACTION_KEYREL,
+      false, &mixer::mutex_recs);
+       }
+       else
+               recorder::deleteAction(parent->chan->index, frame_a, type, false,
+      &mixer::mutex_recs);
+
+  parent->chan->hasActions = recorder::hasActions(parent->chan->index);
+
+
+       /* restore the initial cursor shape, in case you delete an action and
+        * the double arrow (for resizing) is displayed */
+
+       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::moveAction(int frame_a)
+{
+       /* easy one: delete previous action and record the new ones. As usual,
+        * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
+        * value. */
+
+       delAction();
+
+       if (frame_a != -1)
+               this->frame_a = frame_a;
+       else
+               this->frame_a = xToFrame_a();
+
+
+       /* always check frame parity */
+
+       if (this->frame_a % 2 != 0)
+               this->frame_a++;
+
+       recorder::rec(parent->chan->index, type, this->frame_a);
+
+       if (ch->mode == SINGLE_PRESS) {
+               frame_b = xToFrame_b();
+               recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_b);
+       }
+
+  parent->chan->hasActions = true;
+
+       recorder::sortActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::absx()
+{
+       return x() - parent->ac->x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::xToFrame_a()
+{
+       return (absx()) * parent->zoom;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::xToFrame_b()
+{
+       return (absx() + w()) * parent->zoom;
+}
diff --git a/src/gui/elems/actionEditor/action.h b/src/gui/elems/actionEditor/action.h
new file mode 100644 (file)
index 0000000..cd9823f
--- /dev/null
@@ -0,0 +1,86 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_ACTION_H
+#define GE_ACTION_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class gdActionEditor;
+class SampleChannel;
+
+
+class geAction : public Fl_Box
+{
+private:
+
+       bool            selected;
+       unsigned        index;
+  gdActionEditor *parent;   // pointer to parent (geActionEditor)
+       SampleChannel  *ch;
+  char            type;     // type of action
+
+public:
+
+       geAction(int x, int y, int h, int frame_a, unsigned index,
+               gdActionEditor *parent, SampleChannel *ch, bool record, char type);
+       void draw();
+       int  handle(int e);
+       void addAction();
+       void delAction();
+
+       /* moveAction
+        * shift the action on the x-axis and update Recorder. If frame_a != -1
+        * use the new frame in input (used while snapping) */
+
+       void moveAction(int frame_a=-1);
+
+       /* absx
+        * x() is relative to scrolling position. absx() returns the absolute
+        * x value of the action, from the leftmost edge. */
+
+       int absx();
+
+       /* xToFrame_a,b
+        * return the real frames of x() position */
+
+       int xToFrame_a();
+       int xToFrame_b();
+
+       int frame_a;  // initial frame (KEYPRESS for singlemode.press)
+       int frame_b;  // terminal frame (KEYREL for singlemode.press, null for others)
+
+       bool onRightEdge;
+       bool onLeftEdge;
+
+       static const int MIN_WIDTH = 8;
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/actionEditor.cpp b/src/gui/elems/actionEditor/actionEditor.cpp
new file mode 100644 (file)
index 0000000..f08f66d
--- /dev/null
@@ -0,0 +1,425 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/clock.h"
+#include "../../../core/sampleChannel.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "action.h"
+#include "gridTool.h"
+#include "actionEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
+  : geBaseActionEditor(x, y, 200, 40, pParent),
+    ch                (ch),
+    selected          (nullptr)
+{
+       size(pParent->totalWidth, h());
+
+       /* add actions when the window opens. Their position is zoom-based;
+        * each frame is / 2 because we don't care about stereo infos. */
+
+       for (unsigned i=0; i<recorder::frames.size(); i++) {
+               for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+
+                 recorder::action *action = recorder::global.at(i).at(j);
+
+      /* Don't show actions:
+      - that don't belong to the displayed channel (!= pParent->chan->index);
+      - that are covered by the grey area (> G_Mixer.totalFrames);
+      - of type G_ACTION_KILL in a SINGLE_PRESS channel. They cannot be
+        recorded in such mode, but they can exist if you change from another
+        mode to singlepress;
+      - of type G_ACTION_KEYREL in a SINGLE_PRESS channel. It's up to geAction to
+        find the other piece (namely frame_b)
+      - not of types G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL */
+
+      if ((action->chan != pParent->chan->index)                            ||
+          (recorder::frames.at(i) > clock::getTotalFrames())              ||
+          (action->type == G_ACTION_KILL && ch->mode == SINGLE_PRESS)     ||
+          (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS)       ||
+          (action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL))
+      )
+        continue;
+
+                       int ax = x + (action->frame / pParent->zoom);
+                       geAction *a = new geAction(
+                                       ax,               // x
+                                       y + 4,            // y
+                                       h() - 8,          // h
+                                       action->frame,    // frame_a
+                                       i,                // n. of recordings
+                                       pParent,          // pointer to the pParent window
+                                       ch,               // pointer to SampleChannel
+                                       false,            // record = false: don't record it, we are just displaying it!
+                                       action->type);    // type of action
+                       add(a);
+               }
+       }
+       end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geAction *geActionEditor::getSelectedAction()
+{
+       for (int i=0; i<children(); i++) {
+               int action_x  = ((geAction*)child(i))->x();
+               int action_w  = ((geAction*)child(i))->w();
+               if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
+                       return (geAction*)child(i);
+       }
+       return nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geActionEditor::updateActions()
+{
+       /* when zooming, don't delete and re-add actions, just MOVE them. This
+        * function shifts the action by a zoom factor. Those singlepress are
+        * stretched, as well */
+
+       geAction *a;
+       for (int i=0; i<children(); i++) {
+
+               a = (geAction*)child(i);
+               int newX = x() + (a->frame_a / pParent->zoom);
+
+               if (ch->mode == SINGLE_PRESS) {
+                       int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
+                       if (newW < geAction::MIN_WIDTH)
+                               newW = geAction::MIN_WIDTH;
+                       a->resize(newX, a->y(), newW, a->h());
+               }
+               else
+                       a->resize(newX, a->y(), geAction::MIN_WIDTH, a->h());
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geActionEditor::draw()
+{
+       /* draw basic boundaries (+ beat bars) and hide the unused area. Then
+        * draw the children (the actions) */
+
+       baseDraw();
+
+       /* print label */
+
+       fl_color(COLOR_BG_1);
+       fl_font(FL_HELVETICA, 12);
+       if (active())
+               fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));  /// FIXME h() is too much!
+       else
+               fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));  /// FIXME h() is too much!
+
+       draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geActionEditor::handle(int e)
+{
+       int ret = Fl_Group::handle(e);
+
+       /* do nothing if the widget is deactivated. It could happen for loopmode
+        * channels */
+
+       if (!active())
+               return 1;
+
+       switch (e) {
+
+               case FL_DRAG: {
+
+      if (selected == nullptr) {  // if you drag an empty area
+        ret = 1;
+        break;
+      }
+
+                       /* if onLeftEdge o onRightEdge are true it means that you're resizing
+                        * an action. Otherwise move the widget. */
+
+                       if (selected->onLeftEdge || selected->onRightEdge) {
+
+                               /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
+                                * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
+
+                               if (selected->onRightEdge) {
+
+                                       int aw = Fl::event_x()-selected->x();
+                                       int ah = selected->h();
+
+                                       if (Fl::event_x() < selected->x()+geAction::MIN_WIDTH)
+                                               aw = geAction::MIN_WIDTH;
+                                       else
+                                       if (Fl::event_x() > pParent->coverX)
+                                               aw = pParent->coverX-selected->x();
+
+                                       selected->size(aw, ah);
+                               }
+                               else {
+
+                                       int ax = Fl::event_x();
+                                       int ay = selected->y();
+                                       int aw = selected->x()-Fl::event_x()+selected->w();
+                                       int ah = selected->h();
+
+                                       if (Fl::event_x() < x()) {
+                                               ax = x();
+                                               aw = selected->w()+selected->x()-x();
+                                       }
+                                       else
+                                       if (Fl::event_x() > selected->x()+selected->w()-geAction::MIN_WIDTH) {
+                                               ax = selected->x()+selected->w()-geAction::MIN_WIDTH;
+                                               aw = geAction::MIN_WIDTH;
+                                       }
+                                       selected->resize(ax, ay, aw, ah);
+                               }
+                       }
+
+      /* move the widget around */
+
+                       else {
+                               int real_x = Fl::event_x() - actionPickPoint;
+                               if (real_x < x())                                  // don't go beyond the left border
+                                       selected->position(x(), selected->y());
+                               else
+                               if (real_x+selected->w() > pParent->coverX+x())         // don't go beyond the right border
+                                       selected->position(pParent->coverX+x()-selected->w(), selected->y());
+                               else {
+                                       if (pParent->gridTool->isOn()) {
+                                               int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
+                                               selected->position(snpx, selected->y());
+                                       }
+                                       else
+                                               selected->position(real_x, selected->y());
+                               }
+                       }
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_PUSH:   {
+
+                       if (Fl::event_button1()) {
+
+                               /* avoid at all costs two overlapping actions. We use 'selected' because
+                                * the selected action can be reused in FL_DRAG, in case you want to move
+                                * it. */
+
+                               selected = getSelectedAction();
+
+                               if (selected == nullptr) {
+
+                                       /* avoid click on grey area */
+
+                                       if (Fl::event_x() >= pParent->coverX) {
+                                               ret = 1;
+                                               break;
+                                       }
+
+                                       /* snap function, if enabled */
+
+                                       int ax = Fl::event_x();
+                                       int fx = (ax - x()) * pParent->zoom;
+                                       if (pParent->gridTool->isOn()) {
+                                               ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
+                                               fx = pParent->gridTool->getSnapFrame(ax-x());
+
+                                               /* with snap=on an action can fall onto another */
+
+                                               if (actionCollides(fx)) {
+                                                       ret = 1;
+                                                       break;
+                                               }
+                                       }
+
+                                       geAction *a = new geAction(
+                                                       ax,                                   // x
+                                                       y()+4,                                // y
+                                                       h()-8,                                // h
+                                                       fx,                                                                                                                                             // frame_a
+                                                       recorder::frames.size()-1,            // n. of actions recorded
+                                                       pParent,                              // pParent window pointer
+                                                       ch,                                   // pointer to SampleChannel
+                                                       true,                                 // record = true: record it!
+                                                       pParent->getActionType());            // type of action
+                                       add(a);
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); // mainWindow update
+                                       redraw();
+                                       ret = 1;
+                               }
+                               else {
+                                       actionOriginalX = selected->x();
+                                       actionOriginalW = selected->w();
+                                       actionPickPoint = Fl::event_x() - selected->x();
+                                       ret = 1;   // for dragging
+                               }
+                       }
+                       else
+                       if (Fl::event_button3()) {
+                               geAction *a = getSelectedAction();
+                               if (a != nullptr) {
+                                       a->delAction();
+                                       remove(a);
+                                       delete a;
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel);
+                                       redraw();
+                                       ret = 1;
+                               }
+                       }
+                       break;
+               }
+               case FL_RELEASE: {
+
+                       if (selected == nullptr) {
+                               ret = 1;
+                               break;
+                       }
+
+                       /* noChanges = true when you click on an action without doing anything
+                        * (dragging or moving it). */
+
+                       bool noChanges = false;
+                       if (actionOriginalX == selected->x())
+                               noChanges = true;
+                       if (ch->mode == SINGLE_PRESS &&
+                                       actionOriginalX+actionOriginalW != selected->x()+selected->w())
+                               noChanges = false;
+
+                       if (noChanges) {
+                               ret = 1;
+                               selected = nullptr;
+                               break;
+                       }
+
+                       /* step 1: check if the action doesn't overlap with another one.
+                        * In case of overlap the moved action returns to the original X
+                        * value ("actionOriginalX"). */
+
+                       bool overlap = false;
+                       for (int i=0; i<children() && !overlap; i++) {
+
+                               /* never check against itself. */
+
+                               if ((geAction*)child(i) == selected)
+                                       continue;
+
+                               int action_x  = ((geAction*)child(i))->x();
+                               int action_w  = ((geAction*)child(i))->w();
+                               if (ch->mode == SINGLE_PRESS) {
+
+                                       /* when 2 segments overlap?
+                                        * start = the highest value between the two starting points
+                                        * end   = the lowest value between the two ending points
+                                        * if start < end then there's an overlap of end-start pixels. */
+
+                                        int start = action_x >= selected->x() ? action_x : selected->x();
+                                        int end   = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
+                                        if (start < end) {
+                                               selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
+                                               redraw();
+                                               overlap = true;   // one overlap: stop checking
+                                       }
+                               }
+                               else {
+                                       if (selected->x() == action_x) {
+                                               selected->position(actionOriginalX, selected->y());
+                                               redraw();
+                                               overlap = true;   // one overlap: stop checking
+                                       }
+                               }
+                       }
+
+                       /* step 2: no overlap? then update the coordinates of the action, ie
+                        * delete the previous rec and create a new one.
+                        * Anyway the selected action becomes nullptr, because when you release
+                        * the mouse button the dragging process ends. */
+
+                       if (!overlap) {
+                               if (pParent->gridTool->isOn()) {
+                                       int f = pParent->gridTool->getSnapFrame(selected->absx());
+                                       selected->moveAction(f);
+                               }
+                               else
+                                       selected->moveAction();
+                       }
+                       selected = nullptr;
+                       ret = 1;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geActionEditor::actionCollides(int frame)
+{
+       /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
+        * overlap the head (frame) of the new one. First the general case, yet. */
+
+       bool collision = false;
+
+       for (int i=0; i<children() && !collision; i++)
+               if (((geAction*) child(i))->frame_a == frame)
+                       collision = true;
+
+       if (ch->mode == SINGLE_PRESS) {
+               for (int i=0; i<children() && !collision; i++) {
+                       geAction *c = ((geAction*) child(i));
+                       if (frame <= c->frame_b && frame >= c->frame_a)
+                               collision = true;
+               }
+       }
+
+       return collision;
+}
diff --git a/src/gui/elems/actionEditor/actionEditor.h b/src/gui/elems/actionEditor/actionEditor.h
new file mode 100644 (file)
index 0000000..deaf7c4
--- /dev/null
@@ -0,0 +1,86 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_ACTION_EDITOR_H
+#define GE_ACTION_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class geAction;
+class SampleChannel;
+
+
+class geActionEditor : public geBaseActionEditor
+{
+
+private:
+
+       SampleChannel *ch;
+
+       /* getSelectedAction
+        * get the action under the mouse. nullptr if nothing found. */
+
+       geAction *getSelectedAction();
+
+       /* selected
+        * pointer to the selected action. Useful when dragging around. */
+
+       geAction *selected;
+
+       /* actionOriginalX, actionOriginalW
+        * x and w of the action, when moved. Useful for checking if the action
+        * overlaps another one: in that case the moved action returns to
+        * actionOriginalX (and to actionOriginalW if resized). */
+
+       int actionOriginalX;
+       int actionOriginalW;
+
+       /* actionPickPoint
+        * the precise x point in which the action has been picked with the mouse,
+        * before a dragging action. */
+
+       int actionPickPoint;
+
+
+       /* actionCollides
+        * true if an action collides with another. Used while adding new points
+        * with snap active.*/
+
+       bool actionCollides(int frame);
+
+public:
+
+       geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch);
+       void draw();
+       int  handle(int e);
+       void updateActions();
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/baseActionEditor.cpp b/src/gui/elems/actionEditor/baseActionEditor.cpp
new file mode 100644 (file)
index 0000000..a740b64
--- /dev/null
@@ -0,0 +1,99 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseActionEditor
+ * Parent class of any widget inside the action editor.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/mixer.h"
+#include "../../../core/const.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "gridTool.h"
+#include "baseActionEditor.h"
+
+
+geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h,
+       gdActionEditor *pParent)
+       :       Fl_Group(x, y, w, h), pParent(pParent) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geBaseActionEditor::~geBaseActionEditor() {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseActionEditor::baseDraw(bool clear) {
+
+       /* clear the screen */
+
+       if (clear)
+               fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+       /* draw the container */
+
+       fl_color(COLOR_BD_0);
+       fl_rect(x(), y(), w(), h());
+
+       /* grid drawing, if > 1 */
+
+       if (pParent->gridTool->getValue() > 1) {
+
+               fl_color(fl_rgb_color(54, 54, 54));
+               fl_line_style(FL_DASH, 0, nullptr);
+
+               for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
+                       int px = pParent->gridTool->points.at(i)+x()-1;
+                       fl_line(px, y()+1, px, y()+h()-2);
+               }
+               fl_line_style(0);
+       }
+
+       /* bars and beats drawing */
+
+       fl_color(COLOR_BD_0);
+       for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
+               int px = pParent->gridTool->beats.at(i)+x()-1;
+               fl_line(px, y()+1, px, y()+h()-2);
+       }
+
+       fl_color(COLOR_BG_2);
+       for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
+               int px = pParent->gridTool->bars.at(i)+x()-1;
+               fl_line(px, y()+1, px, y()+h()-2);
+       }
+
+       /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
+        * are 32) */
+
+       int coverWidth = pParent->totalWidth-pParent->coverX;
+       if (coverWidth != 0)
+               fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
+}
diff --git a/src/gui/elems/actionEditor/baseActionEditor.h b/src/gui/elems/actionEditor/baseActionEditor.h
new file mode 100644 (file)
index 0000000..f907b57
--- /dev/null
@@ -0,0 +1,58 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionWidget
+ *
+ * parent class of any tool inside the action editor.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BASE_ACTION_EDITOR_H
+#define GE_BASE_ACTION_EDITOR_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class gdActionEditor;
+
+
+class geBaseActionEditor : public Fl_Group
+{
+protected:
+
+       gdActionEditor *pParent;
+
+  void baseDraw(bool clear=true);
+
+public:
+
+       virtual void updateActions() = 0;
+
+       geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent);
+       ~geBaseActionEditor();
+};
+
+#endif
diff --git a/src/gui/elems/actionEditor/basePianoItem.cpp b/src/gui/elems/actionEditor/basePianoItem.cpp
new file mode 100644 (file)
index 0000000..11c3806
--- /dev/null
@@ -0,0 +1,68 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "pianoRoll.h"
+#include "basePianoItem.h"
+
+
+geBasePianoItem::geBasePianoItem(int x, int y, int w, gdActionEditor *pParent)
+  : Fl_Box  (x, y, w, gePianoRoll::CELL_H),
+    pParent (pParent),
+    selected(false)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBasePianoItem::handle(int e)
+{
+  int ret = 0;
+       switch (e) {
+               case FL_ENTER:
+      selected = true;
+      redraw();
+      ret = 1;
+      break;
+    case FL_LEAVE:
+      selected = false;
+      redraw();
+      ret = 1;
+      break;
+  }
+  return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBasePianoItem::getY(int note)
+{
+  return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
+}
diff --git a/src/gui/elems/actionEditor/basePianoItem.h b/src/gui/elems/actionEditor/basePianoItem.h
new file mode 100644 (file)
index 0000000..ade2fb2
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BASE_PIANO_ITEM_H
+#define GE_BASE_PIANO_ITEM_H
+
+
+#include <FL/Fl_Box.H>
+#include "../../../core/recorder.h"
+
+
+class gdActionEditor;
+
+
+class geBasePianoItem : public Fl_Box
+{
+protected:
+
+  geBasePianoItem(int x, int y, int w, gdActionEditor *pParent);
+
+  /* getY
+        * from a note, return the y position on piano roll */
+
+       int getY(int note);
+
+  gdActionEditor *pParent;
+
+  bool selected;
+
+public:
+
+  virtual void reposition(int pianoRollX) = 0;
+
+  int handle(int e) override;
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/envelopeEditor.cpp b/src/gui/elems/actionEditor/envelopeEditor.cpp
new file mode 100644 (file)
index 0000000..19e84c8
--- /dev/null
@@ -0,0 +1,416 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * envelopeEditor
+ *
+ * Parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/channel.h"
+#include "../../../core/recorder.h"
+#include "../../../core/mixer.h"
+#include "../../../core/clock.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "gridTool.h"
+#include "envelopeEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
+       int type, int range, const char *l)
+       :       geBaseActionEditor(x, y, 200, 80, pParent),
+               l                 (l),
+               type              (type),
+               range             (range),
+               selectedPoint     (-1),
+               draggedPoint      (-1)
+{
+       size(pParent->totalWidth, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+geEnvelopeEditor::~geEnvelopeEditor() {
+       clearPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
+       point p;
+       p.frame  = frame;
+       p.iValue = iValue;
+       p.fValue = fValue;
+       p.x = px;
+       p.y = py;
+       points.push_back(p);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::updateActions() {
+       for (unsigned i=0; i<points.size(); i++)
+               points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::draw() {
+
+       baseDraw();
+
+       /* print label */
+
+       fl_color(COLOR_BG_1);
+       fl_font(FL_HELVETICA, 12);
+       fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
+
+       int pxOld = x()-3;
+       int pyOld = y()+1;
+       int pxNew = 0;
+       int pyNew = 0;
+
+       fl_color(COLOR_BG_2);
+
+       for (unsigned i=0; i<points.size(); i++) {
+
+               pxNew = points.at(i).x+x()-3;
+               pyNew = points.at(i).y+y();
+
+               if (selectedPoint == (int) i) {
+                       fl_color(COLOR_BD_1);
+                       fl_rectf(pxNew, pyNew, 7, 7);
+                       fl_color(COLOR_BG_2);
+               }
+               else
+                       fl_rectf(pxNew, pyNew, 7, 7);
+
+               if (i > 0)
+                       fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
+
+               pxOld = pxNew;
+               pyOld = pyNew;
+       }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::handle(int e) {
+
+       /* Adding an action: no further checks required, just record it on frame
+        * mx*pParent->zoom. Deleting action is trickier: find the active
+        * point and derive from it the corresponding frame. */
+
+       int ret = 0;
+       int mx  = Fl::event_x()-x();  // mouse x
+       int my  = Fl::event_y()-y();  // mouse y
+
+       switch (e) {
+
+               case FL_ENTER: {
+                       ret = 1;
+                       break;
+               }
+
+               case FL_MOVE: {
+                       selectedPoint = getSelectedPoint();
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_LEAVE: {
+                       draggedPoint  = -1;
+                       selectedPoint = -1;
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_PUSH: {
+
+                       /* left click on point: drag
+                        * right click on point: delete
+                        * left click on void: add */
+
+                       if (Fl::event_button1()) {
+
+                               if (selectedPoint != -1) {
+                                       draggedPoint = selectedPoint;
+                               }
+                               else {
+
+                                       /* top & border fix */
+
+                                       if (my > h()-8) my = h()-8;
+                                       if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
+
+                                       if (range == G_RANGE_FLOAT) {
+
+                                               /* if this is the first point ever, add other two points at the beginning
+                                                * and the end of the range */
+
+                                               if (points.size() == 0) {
+                                                       addPoint(0, 0, 1.0f, 0, 1);
+                                                       recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
+                                                       addPoint(clock::getTotalFrames(), 0, 1.0f, pParent->coverX, 1);
+                                                       recorder::rec(pParent->chan->index, type, clock::getTotalFrames(), 0, 1.0f);
+              pParent->chan->hasActions = true;
+                                               }
+
+                                               /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
+
+                                               int frame   = mx * pParent->zoom;
+                                               float value = (my - h() + 8) / (float) (1 - h() + 8);
+                                               addPoint(frame, 0, value, mx, my);
+                                               recorder::rec(pParent->chan->index, type, frame, 0, value);
+            pParent->chan->hasActions = true;
+                                               recorder::sortActions();
+                                               sortPoints();
+                                       }
+                                       else {
+                                               /// TODO
+                                       }
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+                                       redraw();
+                               }
+                       }
+                       else {
+
+                               /* right click on point 0 or point size-1 deletes the entire envelope */
+
+                               if (selectedPoint != -1) {
+                                       if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
+                                               recorder::clearAction(pParent->chan->index, type);
+            pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+                                               points.clear();
+                                       }
+                                       else {
+                                               recorder::deleteAction(pParent->chan->index,
+              points.at(selectedPoint).frame, type, false, &mixer::mutex_recs);
+            pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+            recorder::sortActions();
+                                               points.erase(points.begin() + selectedPoint);
+                                       }
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+                                       redraw();
+                               }
+                       }
+
+                       ret = 1;
+                       break;
+               }
+
+               case FL_RELEASE: {
+                       if (draggedPoint != -1) {
+
+                               if (points.at(draggedPoint).x == previousXPoint) {
+                                       //gu_log("nothing to do\n");
+                               }
+                               else {
+                                       int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+                                       /* x edge correction */
+
+                                       if (newFrame < 0)
+                                               newFrame = 0;
+                                       else if (newFrame > clock::getTotalFrames())
+                                               newFrame = clock::getTotalFrames();
+
+                                       /* vertical line check */
+
+                                       int vp = verticalPoint(points.at(draggedPoint));
+                                       if (vp == 1)                     newFrame -= 256;
+                                       else if (vp == -1) newFrame += 256;
+
+                                       /*  delete previous point and record a new one */
+
+                                       recorder::deleteAction(pParent->chan->index,
+            points.at(draggedPoint).frame, type, false, &mixer::mutex_recs);
+          pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+                                       if (range == G_RANGE_FLOAT) {
+                                               float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
+                                               recorder::rec(pParent->chan->index, type, newFrame, 0, value);
+            pParent->chan->hasActions = true;
+                                       }
+                                       else {
+                                               /// TODO
+                                       }
+
+                                       recorder::sortActions();
+                                       points.at(draggedPoint).frame = newFrame;
+                                       draggedPoint  = -1;
+                                       selectedPoint = -1;
+                               }
+                       }
+                       ret = 1;
+                       break;
+               }
+
+               case FL_DRAG: {
+
+                       if (draggedPoint != -1) {
+
+                               /* y constraint */
+
+                               if (my > h()-8)
+                                       points.at(draggedPoint).y = h()-8;
+                               else
+                               if (my < 1)
+                                       points.at(draggedPoint).y = 1;
+                               else
+                                       points.at(draggedPoint).y = my;
+
+                               /* x constraint
+                                * constrain the point between two ends (leftBorder-point, point-point,
+                                * point-rightBorder). First & last points cannot be shifted on x */
+
+                               if (draggedPoint == 0)
+                                       points.at(draggedPoint).x = x()-8;
+                               else
+                               if ((unsigned) draggedPoint == points.size()-1)
+                                       points.at(draggedPoint).x = pParent->coverX;
+                               else {
+                                       int prevPoint = points.at(draggedPoint-1).x;
+                                       int nextPoint = points.at(draggedPoint+1).x;
+                                       if (mx <= prevPoint)
+                                               points.at(draggedPoint).x = prevPoint;
+                                       else
+                                       if (mx >= nextPoint)
+                                               points.at(draggedPoint).x = nextPoint;
+                                       //else
+                                       //      points.at(draggedPoint).x = mx;
+                                       else {
+                                               if (pParent->gridTool->isOn())
+                                                       points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
+                                               else
+                                                       points.at(draggedPoint).x = mx;
+                                       }
+                               }
+                               redraw();
+                       }
+
+                       ret = 1;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::verticalPoint(const point &p) {
+       for (unsigned i=0; i<points.size(); i++) {
+               if (&p == &points.at(i)) {
+                       if (i == 0 || i == points.size()-1)  // first or last point
+                               return 0;
+                       else {
+                               if (points.at(i-1).x == p.x)    // vertical with point[i-1]
+                                       return -1;
+                               else
+                               if (points.at(i+1).x == p.x)    // vertical with point[i+1]
+                                       return 1;
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::sortPoints() {
+       for (unsigned i=0; i<points.size(); i++)
+               for (unsigned j=0; j<points.size(); j++)
+                       if (points.at(j).x > points.at(i).x)
+                               std::swap(points.at(j), points.at(i));
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::getSelectedPoint() {
+
+       /* point is a 7x7 dot */
+
+       for (unsigned i=0; i<points.size(); i++) {
+               if (Fl::event_x() >= points.at(i).x+x()-4  &&
+                               Fl::event_x() <= points.at(i).x+x()+4  &&
+                               Fl::event_y() >= points.at(i).y+y()    &&
+                               Fl::event_y() <= points.at(i).y+y()+7)
+               return i;
+       }
+       return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::fill() {
+       points.clear();
+       for (unsigned i=0; i<recorder::global.size(); i++)
+               for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+                       recorder::action *a = recorder::global.at(i).at(j);
+                       if (a->type == type && a->chan == pParent->chan->index) {
+                               if (range == G_RANGE_FLOAT)
+                                       addPoint(
+                                               a->frame,                      // frame
+                                               0,                             // int value (unused)
+                                               a->fValue,                     // float value
+                                               a->frame / pParent->zoom,       // x
+                                               ((1-h()+8)*a->fValue)+h()-8);  // y = (b-a)x + a (line between two points)
+                               // else: TODO
+                       }
+               }
+
+}
diff --git a/src/gui/elems/actionEditor/envelopeEditor.h b/src/gui/elems/actionEditor/envelopeEditor.h
new file mode 100644 (file)
index 0000000..c9675ea
--- /dev/null
@@ -0,0 +1,115 @@
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_ENVELOPE_EDITOR_H
+#define GE_ENVELOPE_EDITOR_H
+
+
+#include <vector>
+#include "baseActionEditor.h"
+
+
+class geEnvelopeEditor : public geBaseActionEditor
+{
+       const char *l;      // internal label
+       int         type;   // type of action
+       int         range;
+
+       /* point
+        * a single dot in the graph. x = relative frame, y = relative value */
+
+       struct point
+       {
+               int   frame;
+               int   iValue;
+               float fValue;
+               int   x;
+               int   y;
+       };
+
+       /* points
+        * array of points, filled by fillPoints() */
+
+       std::vector<point> points;
+
+       /* selectedPoint
+        * which point we are selecting? */
+
+       int selectedPoint;
+
+       /* draggedPoint
+        * which point we are dragging? */
+
+       int draggedPoint;
+
+       /* previousXPoint
+        * x coordinate of point at time t-1. Used to check effective shifts */
+
+       int previousXPoint;
+
+       void draw();
+
+       int handle(int e);
+
+       int getSelectedPoint();
+
+       void sortPoints();
+
+       /* verticalPoint
+        * check if two points form a vertical line. In that case the frame value
+        * would be the same and recorder would go crazy, so shift by a small value
+        * of frames to create a minimal fadein/fadeout level. Return 0: no
+        * vertical points; return 1: vertical with the next one, return -1: vertical
+        * with the previous one. */
+
+       int verticalPoint(const point &p);
+
+public:
+
+       geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range,
+    const char *l);
+       ~geEnvelopeEditor();
+
+       /* addPoint
+        * add a point made of frame+value to internal points[]. */
+
+       void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
+
+       void updateActions();
+
+       /* fill
+        * parse recorder's stack and fill the widget with points. It's up to
+        * the caller to call this method as initialization. */
+
+       void fill();
+
+       inline void clearPoints() { points.clear(); }
+};
+
+#endif
diff --git a/src/gui/elems/actionEditor/gridTool.cpp b/src/gui/elems/actionEditor/gridTool.cpp
new file mode 100644 (file)
index 0000000..c697cd8
--- /dev/null
@@ -0,0 +1,220 @@
+/* -----------------------------------------------------------------------------
+*
+* Giada - Your Hardcore Loopmachine
+*
+* ------------------------------------------------------------------------------
+*
+* Copyright (C) 2010-2017 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 <cmath>
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/clock.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "actionEditor.h"
+#include "gridTool.h"
+
+
+using namespace giada::m;
+
+
+geGridTool::geGridTool(int x, int y, gdActionEditor *parent)
+       :       Fl_Group(x, y, 80, 20), parent(parent)
+{
+       gridType = new geChoice(x, y, 40, 20);
+       gridType->add("1");
+       gridType->add("2");
+       gridType->add("3");
+       gridType->add("4");
+       gridType->add("6");
+       gridType->add("8");
+       gridType->add("16");
+       gridType->add("32");
+       gridType->value(0);
+       gridType->callback(cb_changeType, (void*)this);
+
+       active = new geCheck (x+44, y+4, 12, 12);
+
+       gridType->value(conf::actionEditorGridVal);
+       active->value(conf::actionEditorGridOn);
+
+       end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geGridTool::~geGridTool()
+{
+       conf::actionEditorGridVal = gridType->value();
+       conf::actionEditorGridOn  = active->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::cb_changeType(Fl_Widget *w, void *p)  { ((geGridTool*)p)->__cb_changeType(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::__cb_changeType()
+{
+       calc();
+       parent->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geGridTool::isOn()
+{
+       return active->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getValue()
+{
+       switch (gridType->value()) {
+               case 0: return 1;
+               case 1: return 2;
+               case 2: return 3;
+               case 3: return 4;
+               case 4: return 6;
+               case 5: return 8;
+               case 6: return 16;
+               case 7: return 32;
+       }
+       return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::calc()
+{
+       points.clear();
+       frames.clear();
+       bars.clear();
+       beats.clear();
+
+       /* find beats, bars and grid. The method is the same of the waveform in sample
+        * editor. Take totalwidth (the width in pixel of the area to draw), knowing
+        * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
+        * put a concentrate of each block (which is totalFrames / zoom) */
+
+       int  j   = 0;
+       int fpgc = floor(clock::getFramesPerBeat() / getValue());  // frames per grid cell
+
+       for (int i=1; i<parent->totalWidth; i++) {   // if i=0, step=0 -> useless cycle
+               int step = parent->zoom*i;
+               while (j < step && j < clock::getTotalFrames()) {
+                       if (j % fpgc == 0) {
+                               points.push_back(i);
+                               frames.push_back(j);
+                       }
+                       if (j % clock::getFramesPerBeat() == 0)
+                               beats.push_back(i);
+                       if (j % clock::getFramesPerBar() == 0 && i != 1)
+                               bars.push_back(i);
+                       if (j == clock::getTotalFrames() - 1)
+                               parent->coverX = i;
+                       j++;
+               }
+               j = step;
+       }
+
+       /* fix coverX if == 0, which means G_Mixer.beats == G_MAX_BEATS */
+
+       if (clock::getBeats() == G_MAX_BEATS)
+               parent->coverX = parent->totalWidth;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getSnapPoint(int v)
+{
+       if (v == 0) return 0;
+
+       for (int i=0; i<(int)points.size(); i++) {
+
+               if (i == (int) points.size()-1)
+                       return points.at(i);
+
+               int gp  = points.at(i);
+               int gpn = points.at(i+1);
+
+               if (v >= gp && v < gpn)
+                       return gp;
+       }
+       return v;  // default value
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getSnapFrame(int v)
+{
+       v *= parent->zoom;  // transformation pixel -> frame
+
+       for (int i=0; i<(int)frames.size(); i++) {
+
+               if (i == (int) frames.size()-1)
+                       return frames.at(i);
+
+               int gf  = frames.at(i);     // grid frame
+               int gfn = frames.at(i+1);   // grid frame next
+
+               if (v >= gf && v < gfn) {
+
+                       /* which one is the closest? gf < v < gfn */
+
+                       if ((gfn - v) < (v - gf))
+                               return gfn;
+                       else
+                               return gf;
+               }
+       }
+       return v;  // default value
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getCellSize()
+{
+       return (parent->coverX - parent->ac->x()) / clock::getBeats() / getValue();
+}
diff --git a/src/gui/elems/actionEditor/gridTool.h b/src/gui/elems/actionEditor/gridTool.h
new file mode 100644 (file)
index 0000000..8c56be3
--- /dev/null
@@ -0,0 +1,82 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_GRID_TOOL_H
+#define GE_GRID_TOOL_H
+
+
+#include <vector>
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+class gdActionEditor;
+
+
+class geGridTool : public Fl_Group
+{
+private:
+
+  geChoice *gridType;
+       geCheck  *active;
+
+       gdActionEditor *parent;
+
+       static void cb_changeType(Fl_Widget *w, void *p);
+       inline void __cb_changeType();
+
+public:
+
+       geGridTool(int x, int y, gdActionEditor *parent);
+       ~geGridTool();
+
+       int  getValue();
+       bool isOn();
+       void calc();
+
+       /* getSnapPoint
+        * given a cursor position in input, return the x coordinates of the
+        * nearest snap point (in pixel, clean, ie. not x()-shifted) */
+
+       int getSnapPoint(int v);
+       int getSnapFrame(int v);
+
+       /* getCellSize
+        * return the size in pixel of a single cell of the grid. */
+
+       int getCellSize();
+
+       std::vector<int> points;   // points of the grid
+       std::vector<int> frames;   // frames of the grid
+
+       std::vector<int> bars;
+       std::vector<int> beats;
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/muteEditor.cpp b/src/gui/elems/actionEditor/muteEditor.cpp
new file mode 100644 (file)
index 0000000..82f3d1c
--- /dev/null
@@ -0,0 +1,419 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/recorder.h"
+#include "../../../core/mixer.h"
+#include "../../../core/channel.h"
+#include "../../../core/clock.h"
+#include "../../../glue/main.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "gridTool.h"
+#include "muteEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
+ : geBaseActionEditor(x, y, 200, 80, pParent),
+   draggedPoint      (-1),
+   selectedPoint     (-1)
+{
+       size(pParent->totalWidth, h());
+       extractPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::draw()
+{
+       baseDraw();
+
+       /* print label */
+
+       fl_color(COLOR_BG_1);
+       fl_font(FL_HELVETICA, 12);
+       fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+       /* draw "on" and "off" labels. Must stay in background */
+
+       fl_color(COLOR_BG_1);
+       fl_font(FL_HELVETICA, 9);
+       fl_draw("on",  x()+4, y(),        w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+       fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+
+       /* draw on-off points. On = higher rect, off = lower rect. It always
+        * starts with a note_off */
+
+       fl_color(COLOR_BG_2);
+
+       int pxOld = x()+1;
+       int pxNew = 0;
+       int py    = y()+h()-5;
+       int pyDot = py-6;
+
+       for (unsigned i=0; i<points.size(); i++) {
+
+               /* next px */
+
+               pxNew = points.at(i).x+x();
+
+               /* draw line from pxOld to pxNew.
+                * i % 2 == 0: first point, mute_on
+                * i % 2 != 0: second point, mute_off */
+
+               fl_line(pxOld, py, pxNew, py);
+               pxOld = pxNew;
+
+               py = i % 2 == 0 ? y()+4 : y()+h()-5;
+
+               /* draw dots (handles) */
+
+               fl_line(pxNew, y()+h()-5, pxNew, y()+4);
+
+               if (selectedPoint == (int) i) {
+                       fl_color(COLOR_BD_1);
+                       fl_rectf(pxNew-3, pyDot, 7, 7);
+                       fl_color(COLOR_BG_2);
+               }
+               else
+                       fl_rectf(pxNew-3, pyDot, 7, 7);
+       }
+
+       /* last section */
+
+       py = y()+h()-5;
+       fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::extractPoints()
+{
+       points.clear();
+
+       /* actions are already sorted by recorder::sortActions() */
+
+       for (unsigned i=0; i<recorder::frames.size(); i++) {
+               for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+                       if (recorder::global.at(i).at(j)->chan == pParent->chan->index) {
+                               if (recorder::global.at(i).at(j)->type & (G_ACTION_MUTEON | G_ACTION_MUTEOFF)) {
+                                       point p;
+                                       p.frame = recorder::frames.at(i);
+                                       p.type  = recorder::global.at(i).at(j)->type;
+                                       p.x     = p.frame / pParent->zoom;
+                                       points.push_back(p);
+                                       //gu_log("[geMuteEditor::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
+                               }
+                       }
+               }
+       }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::updateActions() {
+       for (unsigned i=0; i<points.size(); i++)
+               points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geMuteEditor::handle(int e) {
+
+       int ret = 0;
+       int mouseX = Fl::event_x()-x();
+
+       switch (e) {
+
+               case FL_ENTER: {
+                       ret = 1;
+                       break;
+               }
+
+               case FL_MOVE: {
+                       selectedPoint = getSelectedPoint();
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_LEAVE: {
+                       draggedPoint  = -1;
+                       selectedPoint = -1;
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_PUSH: {
+
+                       /* left click on point: drag
+                        * right click on point: delete
+                        * left click on void: add */
+
+                       if (Fl::event_button1())  {
+
+                               if (selectedPoint != -1) {
+                                       draggedPoint   = selectedPoint;
+                                       previousXPoint = points.at(selectedPoint).x;
+                               }
+                               else {
+
+                                       /* click on the grey area leads to nowhere */
+
+                                       if (mouseX > pParent->coverX) {
+                                               ret = 1;
+                                               break;
+                                       }
+
+                                       /* click in the middle of a long mute_on (between two points): new actions
+                                        * must be added in reverse: first mute_off then mute_on. Let's find the
+                                        * next point from here. */
+
+                                       unsigned nextPoint = points.size();
+                                       for (unsigned i=0; i<points.size(); i++) {
+                                               if (mouseX < points.at(i).x) {
+                                                       nextPoint = i;
+                                                       break;
+                                               }
+                                       }
+
+                                       /* next point odd = mute_on [click here] mute_off
+                                        * next point even = mute_off [click here] mute_on */
+
+                                       int frame_a = mouseX * pParent->zoom;
+                                       int frame_b = frame_a+2048;
+
+                                       if (pParent->gridTool->isOn()) {
+                                               frame_a = pParent->gridTool->getSnapFrame(mouseX);
+                                               frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
+
+                                               /* with snap=on a point can fall onto another */
+
+                                               if (pointCollides(frame_a) || pointCollides(frame_b)) {
+                                                       ret = 1;
+                                                       break;
+                                               }
+                                       }
+
+                                       /* ensure frame parity */
+
+                                       if (frame_a % 2 != 0) frame_a++;
+                                       if (frame_b % 2 != 0) frame_b++;
+
+                                       /* avoid overflow: frame_b must be within the sequencer range. In that
+                                        * case shift the ON-OFF block */
+
+                                       if (frame_b >= clock::getTotalFrames()) {
+                                               frame_b = clock::getTotalFrames();
+                                               frame_a = frame_b-2048;
+                                       }
+
+                                       if (nextPoint % 2 != 0) {
+                                               recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_a);
+                                               recorder::rec(pParent->chan->index, G_ACTION_MUTEON,  frame_b);
+                                       }
+                                       else {
+                                               recorder::rec(pParent->chan->index, G_ACTION_MUTEON,  frame_a);
+                                               recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_b);
+                                       }
+          pParent->chan->hasActions = true;
+                                       recorder::sortActions();
+
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+                                       extractPoints();
+                                       redraw();
+                               }
+                       }
+                       else {
+
+                               /* delete points pair */
+
+                               if (selectedPoint != -1) {
+
+                                       unsigned a;
+                                       unsigned b;
+
+                                       if (points.at(selectedPoint).type == G_ACTION_MUTEOFF) {
+                                               a = selectedPoint-1;
+                                               b = selectedPoint;
+                                       }
+                                       else {
+                                               a = selectedPoint;
+                                               b = selectedPoint+1;
+                                       }
+
+                                       //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
+                                       //              a, b, points.at(a).frame, points.at(b).frame);
+
+                                       recorder::deleteAction(pParent->chan->index, points.at(a).frame,
+          points.at(a).type, false, &mixer::mutex_recs); // false = don't check vals
+                                       recorder::deleteAction(pParent->chan->index,    points.at(b).frame,
+          points.at(b).type, false, &mixer::mutex_recs); // false = don't check vals
+          pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+          recorder::sortActions();
+
+                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+                                       extractPoints();
+                                       redraw();
+                               }
+                       }
+                       ret = 1;
+                       break;
+               }
+
+               case FL_RELEASE: {
+
+                       if (draggedPoint != -1) {
+
+                               if (points.at(draggedPoint).x == previousXPoint) {
+                                       //gu_log("nothing to do\n");
+                               }
+                               else {
+
+                                       int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+                                       recorder::deleteAction(pParent->chan->index,
+            points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
+            &mixer::mutex_recs);  // don't check values
+          pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+                                       recorder::rec(
+                                                       pParent->chan->index,
+                                                       points.at(draggedPoint).type,
+                                                       newFrame);
+
+          pParent->chan->hasActions = true;
+                                       recorder::sortActions();
+
+                                       points.at(draggedPoint).frame = newFrame;
+                               }
+                       }
+                       draggedPoint  = -1;
+                       selectedPoint = -1;
+
+                       ret = 1;
+                       break;
+               }
+
+               case FL_DRAG: {
+
+                       if (draggedPoint != -1) {
+
+                               /* constrain the point between two ends (leftBorder-point,
+                                * point-point, point-rightBorder) */
+
+                               int prevPoint;
+                               int nextPoint;
+
+                               if (draggedPoint == 0) {
+                                       prevPoint = 0;
+                                       nextPoint = points.at(draggedPoint+1).x - 1;
+                                       if (pParent->gridTool->isOn())
+                                               nextPoint -= pParent->gridTool->getCellSize();
+                               }
+                               else
+                               if ((unsigned) draggedPoint == points.size()-1) {
+                                       prevPoint = points.at(draggedPoint-1).x + 1;
+                                       nextPoint = pParent->coverX-x();
+                                       if (pParent->gridTool->isOn())
+                                               prevPoint += pParent->gridTool->getCellSize();
+                               }
+                               else {
+                                       prevPoint = points.at(draggedPoint-1).x + 1;
+                                       nextPoint = points.at(draggedPoint+1).x - 1;
+                                       if (pParent->gridTool->isOn()) {
+                                               prevPoint += pParent->gridTool->getCellSize();
+                                               nextPoint -= pParent->gridTool->getCellSize();
+                                       }
+                               }
+
+                               if (mouseX <= prevPoint)
+                                       points.at(draggedPoint).x = prevPoint;
+                               else
+                               if (mouseX >= nextPoint)
+                                       points.at(draggedPoint).x = nextPoint;
+                               else
+                               if (pParent->gridTool->isOn())
+                                       points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
+                               else
+                                       points.at(draggedPoint).x = mouseX;
+
+                               redraw();
+                       }
+                       ret = 1;
+                       break;
+               }
+       }
+
+
+       return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool geMuteEditor::pointCollides(int frame) {
+       for (unsigned i=0; i<points.size(); i++)
+               if (frame == points.at(i).frame)
+                       return true;
+       return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geMuteEditor::getSelectedPoint() {
+
+       /* point is a 7x7 dot */
+
+       for (unsigned i=0; i<points.size(); i++) {
+               if (Fl::event_x() >= points.at(i).x+x()-3 &&
+                               Fl::event_x() <= points.at(i).x+x()+3)
+               return i;
+       }
+       return -1;
+}
diff --git a/src/gui/elems/actionEditor/muteEditor.h b/src/gui/elems/actionEditor/muteEditor.h
new file mode 100644 (file)
index 0000000..90ad87b
--- /dev/null
@@ -0,0 +1,102 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_MUTE_TOOL_H
+#define GE_MUTE_TOOL_H
+
+
+#include <vector>
+#include "baseActionEditor.h"
+
+
+class gdActionEditor;
+
+
+class geMuteEditor : public geBaseActionEditor
+{
+private:
+
+       /* point
+        * a single dot in the graph. */
+
+       struct point
+       {
+               int  frame;
+               char type;
+               int  x;
+       };
+
+       /* points
+        * array of on/off points, in frames */
+
+       std::vector<point> points;
+
+       /* draggedPoint
+        * which point we are dragging? */
+
+       int draggedPoint;
+
+       /* selectedPoint
+        * which point we are selecting? */
+
+       int selectedPoint;
+
+       /* previousXPoint
+        * x coordinate of point at time t-1. Used to check effective shifts */
+
+       int previousXPoint;
+
+       /* extractPoints
+        * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
+        * interessanti mute_on o mute_off. Li mette poi nel vector points. */
+       void extractPoints();
+
+       /* getSelectedPoint
+        * ritorna l'indice di points[] in base al punto selezionato (quello
+        * con il mouse hover). Ritorna -1 se non trova niente. */
+       int getSelectedPoint();
+
+       /* pointCollides
+        * true if a point collides with another. Used while adding new points
+        * with snap active.*/
+
+       bool pointCollides(int frame);
+
+public:
+
+       geMuteEditor(int x, int y, gdActionEditor *pParent);
+       void draw();
+       int  handle(int e);
+
+       /* updateActions
+        * calculates new points affected by the zoom. Call this one after
+        * each zoom update. */
+
+       void updateActions();
+};
+
+#endif
diff --git a/src/gui/elems/actionEditor/noteEditor.cpp b/src/gui/elems/actionEditor/noteEditor.cpp
new file mode 100644 (file)
index 0000000..2a2531f
--- /dev/null
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoItem.h"
+#include "pianoRoll.h"
+#include "noteEditor.h"
+
+
+using namespace giada::m;
+
+
+geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent)
+  : Fl_Scroll(x, y, 200, 422),
+    pParent  (pParent)
+{
+       size(pParent->totalWidth, conf::pianoRollH);
+       pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geNoteEditor::~geNoteEditor()
+{
+       clear();
+       conf::pianoRollH = h();
+       conf::pianoRollY = pianoRoll->y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geNoteEditor::updateActions()
+{
+       pianoRoll->updateActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geNoteEditor::draw()
+{
+       pianoRoll->size(this->w(), pianoRoll->h());  /// <--- not optimal
+
+       /* clear background */
+
+       fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+       /* clip pianoRoll to pianoRollContainer size */
+
+       fl_push_clip(x(), y(), w(), h());
+       draw_child(*pianoRoll);
+       fl_pop_clip();
+
+       fl_color(COLOR_BD_0);
+       fl_line_style(0);
+       fl_rect(x(), y(), pParent->totalWidth, h());
+}
diff --git a/src/gui/elems/actionEditor/noteEditor.h b/src/gui/elems/actionEditor/noteEditor.h
new file mode 100644 (file)
index 0000000..2a63c0d
--- /dev/null
@@ -0,0 +1,55 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_NOTE_EDITOR_H
+#define GE_NOTE_EDITOR_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class gdActionEditor;
+class gePianoRoll;
+
+
+class geNoteEditor : public Fl_Scroll
+{
+private:
+
+       gdActionEditor *pParent;
+       gePianoRoll    *pianoRoll;
+
+public:
+
+       geNoteEditor(int x, int y, gdActionEditor *parent);
+       ~geNoteEditor();
+       void draw();
+       void updateActions();
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/pianoItem.cpp b/src/gui/elems/actionEditor/pianoItem.cpp
new file mode 100644 (file)
index 0000000..612b467
--- /dev/null
@@ -0,0 +1,356 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/kernelMidi.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/channel.h"
+#include "../../../core/clock.h"
+#include "../../../core/midiChannel.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "noteEditor.h"
+#include "pianoRoll.h"
+#include "gridTool.h"
+#include "pianoItem.h"
+
+
+using namespace giada::m;
+
+
+gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *_a,
+  recorder::action *_b, gdActionEditor *pParent)
+       : geBasePianoItem(X, Y, MIN_WIDTH, pParent),
+         a              (_a),
+         b              (_b),
+               event_a        (0x00),
+               event_b        (0x00),
+               changed        (false)
+{
+       /* a is a pointer: action exists, needs to be displayed */
+
+       if (a) {
+               note    = kernelMidi::getB2(a->iValue);
+               frame_a = a->frame;
+               frame_b = b->frame;
+               event_a = a->iValue;
+               event_b = b->iValue;
+               int newX = rel_x + (frame_a / pParent->zoom);
+               int newY = rel_y + getY(note);
+               int newW = (frame_b - frame_a) / pParent->zoom;
+               resize(newX, newY, newW, h());
+       }
+
+       /* a is null: action needs to be recorded from scratch */
+
+       else {
+               note    = getNote(rel_y);
+               frame_a = rel_x * pParent->zoom;
+               frame_b = (rel_x + 20) * pParent->zoom;
+               record();
+               size((frame_b - frame_a) / pParent->zoom, h());
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::reposition(int pianoRollX)
+{
+  int newX = pianoRollX + (frame_a / pParent->zoom);
+  int newW = ((frame_b - frame_a) / pParent->zoom);
+  if (newW < MIN_WIDTH)
+    newW = MIN_WIDTH;
+  resize(newX, y(), newW, h());
+  redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gePianoItem::overlap()
+{
+       /* when 2 segments overlap?
+        * start = the highest value between the two starting points
+        * end   = the lowest value between the two ending points
+        * if start < end then there's an overlap of end-start pixels. */
+
+       geNoteEditor *pPiano = static_cast<geNoteEditor*>(parent());
+
+       for (int i=0; i<pPiano->children(); i++) {
+
+               gePianoItem *pItem = static_cast<gePianoItem*>(pPiano->child(i));
+
+               /* don't check against itself and with different y positions */
+
+               if (pItem == this || pItem->y() != y())
+                       continue;
+
+               int start = pItem->x() >= x() ? pItem->x() : x();
+               int end   = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
+               if (start < end)
+                       return true;
+       }
+
+       return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::draw()
+{
+       int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH;
+       fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::record()
+{
+       /* avoid frame overflow */
+
+       int overflow = frame_b - clock::getTotalFrames();
+       if (overflow > 0) {
+               frame_b -= overflow;
+               frame_a -= overflow;
+       }
+
+       event_a |= (MIDI_NOTE_ON);
+       event_a |= (note << 16);   // note value
+       event_a |= (MIDI_VELOCITY);
+       event_a |= (0x00);
+
+       event_b |= (MIDI_NOTE_OFF);
+       event_b |= (note << 16);   // note value
+       event_b |= (MIDI_VELOCITY);
+       event_b |= (0x00);
+
+       recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_a, event_a);
+       recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_b, event_b);
+  pParent->chan->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::remove()
+{
+  MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
+       recorder::deleteAction(ch->index, frame_a, G_ACTION_MIDI, true,
+    &mixer::mutex_recs, event_a, 0.0);
+       recorder::deleteAction(ch->index, frame_b, G_ACTION_MIDI, true,
+    &mixer::mutex_recs, event_b, 0.0);
+
+       /* Send a note-off in case we are deleting it in a middle of a key_on/key_off
+  sequence. */
+
+       ch->sendMidi(event_b);
+  ch->hasActions = recorder::hasActions(ch->index);
+  static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::handle(int e)
+{
+       int ret = 0;
+
+       switch (e) {
+
+               case FL_ENTER: {
+      static_cast<gePianoRoll*>(parent())->cursorOnItem = true;
+                       selected = true;
+                       ret = 1;
+                       redraw();
+                       break;
+               }
+
+               case FL_LEAVE: {
+                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+      static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
+                       selected = false;
+                       ret = 1;
+                       redraw();
+                       break;
+               }
+
+               case FL_MOVE: {
+                       onLeftEdge  = false;
+                       onRightEdge = false;
+
+                       if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
+                               onLeftEdge = true;
+                               fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+                       }
+                       else
+                       if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
+                               onRightEdge = true;
+                               fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+                       }
+                       else
+                               fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+                       ret = 1;
+                       break;
+               }
+
+               case FL_PUSH: {
+
+                       push_x = Fl::event_x() - x();
+                       old_x  = x();
+                       old_w  = w();
+
+                       if (Fl::event_button3()) {
+                               fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+                               remove();
+                               hide();   // for Windows
+                               Fl::delete_widget(this);
+                               static_cast<geNoteEditor*>(parent())->redraw();
+                       }
+                       ret = 1;
+                       break;
+               }
+
+               case FL_DRAG: {
+
+                       changed = true;
+
+                       geNoteEditor *pr = static_cast<geNoteEditor*>(parent());
+                       int coverX     = pParent->coverX + pr->x(); // relative coverX
+                       int nx, ny, nw;
+
+                       if (onLeftEdge) {
+                               nx = Fl::event_x();
+                               ny = y();
+                               nw = x()-Fl::event_x()+w();
+                               if (nx < pr->x()) {
+                                       nx = pr->x();
+                                       nw = w()+x()-pr->x();
+                               }
+                               else
+                               if (nx > x()+w()-MIN_WIDTH) {
+                                       nx = x()+w()-MIN_WIDTH;
+                                       nw = MIN_WIDTH;
+                               }
+                               resize(nx, ny, nw, h());
+                       }
+                       else
+                       if (onRightEdge) {
+                               nw = Fl::event_x()-x();
+                               if (Fl::event_x() < x()+MIN_WIDTH)
+                                       nw = MIN_WIDTH;
+                               else
+                               if (Fl::event_x() > coverX)
+                                       nw = coverX-x();
+                               size(nw, h());
+                       }
+                       else {
+                               nx = Fl::event_x() - push_x;
+                               if (nx < pr->x()+1)
+                                       nx = pr->x()+1;
+                               else
+                               if (nx+w() > coverX)
+                                       nx = coverX-w();
+
+                               /* snapping */
+
+                               if (pParent->gridTool->isOn())
+                                       nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
+
+                               position(nx, y());
+                       }
+
+                       /* update screen */
+
+                       redraw();
+                       static_cast<geNoteEditor*>(parent())->redraw();
+                       ret = 1;
+                       break;
+               }
+
+               case FL_RELEASE: {
+
+                       /* delete & record the action, only if it doesn't overlap with
+                        * another one */
+
+                       if (overlap()) {
+                               resize(old_x, y(), old_w, h());
+                               redraw();
+                       }
+                       else
+                       if (changed) {
+                               remove();
+                               note    = getNote(getRelY());
+                               frame_a = getRelX() * pParent->zoom;
+                               frame_b = (getRelX()+w()) * pParent->zoom;
+                               record();
+                               changed = false;
+                       }
+
+                       static_cast<geNoteEditor*>(parent())->redraw();
+
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getNote(int rel_y)
+{
+  return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelY()
+{
+  return y() - parent()->y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelX()
+{
+  return x() - parent()->x();
+}
diff --git a/src/gui/elems/actionEditor/pianoItem.h b/src/gui/elems/actionEditor/pianoItem.h
new file mode 100644 (file)
index 0000000..9f3418c
--- /dev/null
@@ -0,0 +1,115 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PIANO_ITEM_H
+#define GE_PIANO_ITEM_H
+
+
+#include "../../../core/recorder.h"
+#include "basePianoItem.h"
+
+
+class gdActionEditor;
+
+
+class gePianoItem : public geBasePianoItem
+{
+private:
+
+       /* getRelX/Y
+        * return x/y point of this item, relative to piano roll (and not to
+        * entire screen) */
+
+       int getRelY();
+       int getRelX();
+
+       /* getNote
+        * from a relative_y return the real MIDI note, range 0-127. 15 is
+        * the hardcoded value for note height in pixels */
+
+       int getNote(int rel_y);
+
+       /* overlap
+        * check if this item don't overlap with another one. */
+
+       bool overlap();
+
+       struct giada::m::recorder::action *a;
+       struct giada::m::recorder::action *b;
+
+       int  push_x;
+
+       /* MIDI note, start frame, end frame - Used only if it's a newly added
+        * action */ /** FIXME - is it true? */
+
+       int  note;
+       int  frame_a;
+       int  frame_b;
+
+       /* event - bitmasked MIDI events, generated by record() or by ctor if
+        * not newly added action */
+
+       int event_a;
+       int event_b;
+
+       /* changed - if Item has been moved or resized: re-recording needed */
+
+       bool changed;
+
+       /* onLeft,RightEdge - if cursor is on a widget's edge */
+
+       bool onLeftEdge;
+       bool onRightEdge;
+
+       /* old_x, old_w - store previous width and position while dragging
+        * and moving, in order to restore it if overlap */
+
+       int old_x, old_w;
+
+public:
+
+       static const int MIN_WIDTH    = 10;
+       static const int HANDLE_WIDTH = 5;
+
+       /* pianoItem ctor
+        * if action *a == nullptr, record a new action */
+
+       gePianoItem(int x, int y, int rel_x, int rel_y, 
+               struct giada::m::recorder::action *a, struct giada::m::recorder::action *b, 
+               gdActionEditor *pParent);
+
+       void draw() override;
+       int handle(int e) override;
+
+  void reposition(int pianoRollX) override;
+
+       void record();
+       void remove();
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/pianoItemOrphaned.cpp b/src/gui/elems/actionEditor/pianoItemOrphaned.cpp
new file mode 100644 (file)
index 0000000..4642039
--- /dev/null
@@ -0,0 +1,103 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../core/recorder.h"
+#include "../../../core/channel.h"
+#include "../../../core/midiChannel.h"
+#include "../../../core/mixer.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoRoll.h"
+#include "noteEditor.h"
+#include "pianoItemOrphaned.h"
+
+
+using namespace giada::m;
+
+
+gePianoItemOrphaned::gePianoItemOrphaned(int x, int y, int xRel, int yRel,
+  recorder::action *action, gdActionEditor *pParent)
+  : geBasePianoItem(x, y, WIDTH, pParent),
+    action         (action)
+{
+  note  = kernelMidi::getB2(action->iValue);
+  frame = action->frame;
+  event = action->iValue;
+  int newX = xRel + (frame / pParent->zoom);
+  int newY = yRel + getY(note);
+  resize(newX, newY, w(), h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::reposition(int pianoRollX)
+{
+  int newX = pianoRollX + (frame / pParent->zoom);
+  resize(newX, y(), WIDTH, h());
+  redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItemOrphaned::handle(int e)
+{
+  int ret = geBasePianoItem::handle(e);
+  if (e == FL_PUSH) {
+    remove();
+    ret = 1;
+  }
+  return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::remove()
+{
+  MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
+  recorder::deleteAction(ch->index, frame, G_ACTION_MIDI, true,
+    &mixer::mutex_recs, event, 0.0);
+  hide();   // for Windows
+  Fl::delete_widget(this);
+  ch->hasActions = recorder::hasActions(ch->index);
+  static_cast<geNoteEditor*>(parent())->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::draw()
+{
+  fl_rect(x(), y()+2, WIDTH, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
+}
diff --git a/src/gui/elems/actionEditor/pianoItemOrphaned.h b/src/gui/elems/actionEditor/pianoItemOrphaned.h
new file mode 100644 (file)
index 0000000..a41671a
--- /dev/null
@@ -0,0 +1,65 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PIANO_ITEM_ORPHANED_H
+#define GE_PIANO_ITEM_ORPHANED_H
+
+
+#include "../../../core/recorder.h"
+#include "basePianoItem.h"
+
+
+class gdActionEditor;
+
+
+class gePianoItemOrphaned : public geBasePianoItem
+{
+private:
+
+  struct giada::m::recorder::action *action;
+
+       int note;
+       int frame;
+       int event;
+
+public:
+
+  static const int WIDTH = 12;
+
+  gePianoItemOrphaned(int x, int y, int xRel, int yRel,
+    struct giada::m::recorder::action *action, gdActionEditor *pParent);
+
+  void draw() override;
+  int handle(int e) override;
+
+  void reposition(int pianoRollX) override;
+
+  void remove();
+};
+
+
+#endif
diff --git a/src/gui/elems/actionEditor/pianoRoll.cpp b/src/gui/elems/actionEditor/pianoRoll.cpp
new file mode 100644 (file)
index 0000000..13c738d
--- /dev/null
@@ -0,0 +1,356 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/clock.h"
+#include "../../../core/channel.h"
+#include "../../../core/recorder.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoItem.h"
+#include "pianoItemOrphaned.h"
+#include "noteEditor.h"
+#include "pianoRoll.h"
+
+
+using namespace giada::m;
+
+
+gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent)
+  : geBaseActionEditor(X, Y, W, 40, pParent),
+    cursorOnItem      (false)
+{
+       resizable(nullptr);                   // don't resize children (i.e. pianoItem)
+       size(W, (MAX_KEYS+1) * CELL_H);      // 128 MIDI channels * CELL_H height
+
+       if (conf::pianoRollY == -1)
+               position(x(), y()-(h()/2));  // center
+       else
+               position(x(), conf::pianoRollY);
+
+       drawSurface1();
+       drawSurface2();
+
+       /* Add actions when the window is opened. Position is zoom-based. MIDI actions
+  come always in pair: noteOn + noteOff. */
+
+       recorder::sortActions();
+
+       recorder::action *a2   = nullptr;
+       recorder::action *prev = nullptr;
+
+       for (unsigned i=0; i<recorder::frames.size(); i++) {
+
+    if (recorder::frames.at(i) > clock::getTotalFrames()) // don't show actions > gray area
+      continue;
+
+    for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+
+                       recorder::action *a1 = recorder::global.at(i).at(j);
+
+      /* Skip action if:
+        - does not belong to this channel
+        - is not a MIDI action (we only want MIDI things here)
+        - is the previous one (we have already checked it)
+        - (later on) is not a MIDI Note On type. We don't want any other kind of
+          action here */
+
+      if (a1->chan != pParent->chan->index)
+                               continue;
+      if (a1->type != G_ACTION_MIDI)
+        continue;
+                       if (a1 == prev)
+                               continue;
+
+                       /* Extract MIDI infos from a1: if is note off skip it, we are looking for
+      noteOn only. */
+
+                       int a1_type = kernelMidi::getB1(a1->iValue);
+                       int a1_note = kernelMidi::getB2(a1->iValue);
+
+      /* If two same notes are found (noteOn-noteOn, noteOff-noteOff) or the
+      very first action is a noteOff, add orphaned item.*/
+
+      if ((prev && a1_type == prev->type) || a1_type == 0x80) {
+        gu_log("[geNoteEditor] invalid note pair! Add orphaned item\n");
+        new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
+        a2 = nullptr;
+        continue;
+      }
+
+      /* Now skip anything that is not a noteOn. */
+
+                       if (a1_type != 0x90)
+                               continue;
+
+                       /* Search for the next action. Must have: same channel, G_ACTION_MIDI,
+      greater than a1->frame and with MIDI properties of note_off (0x80), same
+      note of a1, any velocity value (0xFF) because we just don't care about the
+      velocity of a note_off. */
+
+                       recorder::getNextAction(a1->chan, G_ACTION_MIDI, a1->frame, &a2,
+                                       kernelMidi::getIValue(0x80, a1_note, 0xFF));
+
+                       /* Next action note_off found: add a new gePianoItem to piano roll. Add
+      an orphaned piano item otherwise.  */
+
+                       if (a2) {
+                               new gePianoItem(0, 0, x(), y(), a1, a2, pParent);
+                               prev = a2;
+                               a2 = nullptr;
+                       }
+                       else {
+        gu_log("[geNoteEditor] noteOff not found! Add orphaned item\n");
+        new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
+      }
+         }
+       }
+
+       end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::drawSurface1()
+{
+       surface1 = fl_create_offscreen(CELL_W, h());
+       fl_begin_offscreen(surface1);
+
+       /* warning: only w() and h() come from this widget, x and y coordinates
+        * are absolute, since we are writing in a memory chunk */
+
+       fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
+
+       fl_line_style(FL_DASH, 0, nullptr);
+       fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+
+       int octave = MAX_OCTAVES;
+
+       for (int i=1; i<=MAX_KEYS+1; i++) {
+
+               /* print key note label. C C# D D# E F F# G G# A A# B */
+
+               char note[6];
+               switch (i % KEYS) {
+                       case (int) Notes::G:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               sprintf(note, "%dG", octave);
+                               break;
+                       case (int) Notes::FS:
+                               sprintf(note, "%dF#", octave);
+                               break;
+                       case (int) Notes::F:
+                               sprintf(note, "%dF", octave);
+                               break;
+                       case (int) Notes::E:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               sprintf(note, "%dE", octave);
+                               break;
+                       case (int) Notes::DS:
+                               sprintf(note, "%dD#", octave);
+                               break;
+                       case (int) Notes::D:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               sprintf(note, "%dD", octave);
+                               break;
+                       case (int) Notes::CS:
+                               sprintf(note, "%dC#", octave);
+                               break;
+                       case (int) Notes::C:
+                               sprintf(note, "%dC", octave);
+                               break;
+                       case (int) Notes::B:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               sprintf(note, "%dB", octave);
+                               break;
+                       case (int) Notes::AS:
+                               sprintf(note, "%dA#", octave);
+                               break;
+                       case (int) Notes::A:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               sprintf(note, "%dA", octave);
+                               break;
+                       case (int) Notes::GS:
+                               sprintf(note, "%dG#", octave);
+                               octave--;
+                               break;
+               }
+
+    /* Print note name */
+
+               fl_color(COLOR_BG_LINE);
+               fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
+      (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+               /* Print horizontal line */
+
+               if (i < MAX_KEYS+1)
+                       fl_line(0, i*CELL_H, CELL_W, +i*CELL_H);
+       }
+
+       fl_line_style(0);
+       fl_end_offscreen();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::drawSurface2()
+{
+       surface2 = fl_create_offscreen(CELL_W, h());
+       fl_begin_offscreen(surface2);
+       fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
+       fl_color(COLOR_BG_LINE);
+       fl_line_style(FL_DASH, 0, nullptr);
+       for (int i=1; i<=MAX_KEYS+1; i++) {
+               switch (i % KEYS) {
+                       case (int) Notes::G:
+                       case (int) Notes::E:
+                       case (int) Notes::D:
+                       case (int) Notes::B:
+                       case (int) Notes::A:
+                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+                               break;
+               }
+               if (i < MAX_KEYS+1) {
+                       fl_color(COLOR_BG_LINE);
+                       fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
+               }
+       }
+       fl_line_style(0);
+       fl_end_offscreen();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::draw()
+{
+       fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
+
+#if defined(__APPLE__)
+       for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
+               fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
+#else
+       for (int i=CELL_W; i<pParent->totalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster
+               fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
+#endif
+
+       baseDraw(false);
+       draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoRoll::handle(int e)
+{
+       int ret = Fl_Group::handle(e);
+
+       switch (e) {
+               case FL_PUSH:   {
+
+                       /* avoid click on grey area */
+
+                       if (Fl::event_x() >= pParent->coverX) {
+                               ret = 1;
+                               break;
+                       }
+
+
+                       push_y = Fl::event_y() - y();
+
+                       if (Fl::event_button1()) {
+
+                               /* ax is driven by grid, ay by the height in px of each note */
+
+                               int ax = Fl::event_x();
+                               int ay = Fl::event_y();
+
+                               /* vertical snap */
+
+                               int edge = (ay-y()) % CELL_H;
+                               if (edge != 0) ay -= edge;
+
+                               /* if no overlap, add new piano item. Also check that it doesn't
+                                * overflow on the grey area, by shifting it to the left if
+                                * necessary. */
+
+        if (!cursorOnItem) {
+                                       int greyover = ax+20 - pParent->coverX-x();
+                                       if (greyover > 0)
+                                               ax -= greyover;
+                                       add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent));
+                                       redraw();
+                               }
+                       }
+                       ret = 1;
+                       break;
+               }
+               case FL_DRAG:   {
+
+                       if (Fl::event_button3()) {
+
+                               geNoteEditor *prc = static_cast<geNoteEditor*>(parent());
+                               position(x(), Fl::event_y() - push_y);
+
+                               if (y() > prc->y())
+                                       position(x(), prc->y());
+                               else
+                               if (y() < prc->y()+prc->h()-h())
+                                       position(x(), prc->y()+prc->h()-h());
+
+                               prc->redraw();
+                       }
+                       ret = 1;
+                       break;
+               }
+               case FL_MOUSEWHEEL: {   // nothing to do, just avoid small internal scroll
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::updateActions()
+{
+       for (int k=0; k<children(); k++)
+               static_cast<geBasePianoItem*>(child(k))->reposition(x());
+}
diff --git a/src/gui/elems/actionEditor/pianoRoll.h b/src/gui/elems/actionEditor/pianoRoll.h
new file mode 100644 (file)
index 0000000..0f78ef8
--- /dev/null
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PIANO_ROLL_H
+#define GE_PIANO_ROLL_H
+
+
+#include <FL/fl_draw.H>
+#include "baseActionEditor.h"
+
+
+class gdActionEditor;
+
+
+class gePianoRoll : public geBaseActionEditor
+{
+private:
+
+       enum class Notes
+       {
+               G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10,
+               A = 11, GS = 0
+       };
+
+       /* drawSurface*
+       Generates a complex drawing in memory first and copy it to the screen at a
+       later point in time. Fl_Offscreen surface holds the necessary data.     The first
+       call creates an offscreen surface of CELL_W pixel wide containing note values.
+       The second call creates another offscreen surface of CELL_W pixels wide
+       containing the rest of the piano roll. The latter will then be tiled during
+       the ::draw() call. */
+
+       void drawSurface1();
+       void drawSurface2();
+
+       int push_y;
+
+       Fl_Offscreen surface1;  // notes, no repeat
+       Fl_Offscreen surface2;  // lines, x-repeat
+
+public:
+
+       static const int MAX_KEYS    = 127;
+       static const int MAX_OCTAVES = 9;
+       static const int KEYS        = 12;
+       static const int CELL_H      = 18;
+       static const int CELL_W      = 40;
+
+       gePianoRoll(int x, int y, int w, gdActionEditor *pParent);
+
+       void draw();
+       int  handle(int e);
+
+  /* updateActions
+  Repositions existing actions after a zoom gesture. */
+  
+       void updateActions();
+
+       /* cursorOnItem
+       Defines wheter the cursor is over a piano item. This value is updated by each
+       gePianoItem sub-widget. */
+
+       bool cursorOnItem;
+};
+
+
+#endif
diff --git a/src/gui/elems/baseActionEditor.cpp b/src/gui/elems/baseActionEditor.cpp
deleted file mode 100644 (file)
index bc7a412..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionWidget
- *
- * pParent class of any widget inside the action editor.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <FL/fl_draw.H>
-#include "../../core/mixer.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "baseActionEditor.h"
-#include "ge_mixed.h"
-
-
-extern Mixer G_Mixer;
-
-
-geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h,
-       gdActionEditor *pParent)
-       :       Fl_Group(x, y, w, h), pParent(pParent) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geBaseActionEditor::~geBaseActionEditor() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBaseActionEditor::baseDraw(bool clear) {
-
-       /* clear the screen */
-
-       if (clear)
-               fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
-       /* draw the container */
-
-       fl_color(COLOR_BD_0);
-       fl_rect(x(), y(), w(), h());
-
-       /* grid drawing, if > 1 */
-
-       if (pParent->gridTool->getValue() > 1) {
-
-               fl_color(fl_rgb_color(54, 54, 54));
-               fl_line_style(FL_DASH, 0, NULL);
-
-               for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
-                       int px = pParent->gridTool->points.at(i)+x()-1;
-                       fl_line(px, y()+1, px, y()+h()-2);
-               }
-               fl_line_style(0);
-       }
-
-       /* bars and beats drawing */
-
-       fl_color(COLOR_BD_0);
-       for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
-               int px = pParent->gridTool->beats.at(i)+x()-1;
-               fl_line(px, y()+1, px, y()+h()-2);
-       }
-
-       fl_color(COLOR_BG_2);
-       for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
-               int px = pParent->gridTool->bars.at(i)+x()-1;
-               fl_line(px, y()+1, px, y()+h()-2);
-       }
-
-       /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
-        * are 32) */
-
-       int coverWidth = pParent->totalWidth-pParent->coverX;
-       if (coverWidth != 0)
-               fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
-}
diff --git a/src/gui/elems/baseActionEditor.h b/src/gui/elems/baseActionEditor.h
deleted file mode 100644 (file)
index 49c74b1..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionWidget
- *
- * parent class of any tool inside the action editor.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 __GE_ACTIONWIDGET_H__
-#define __GE_ACTIONWIDGET_H__
-
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include "../../core/const.h"
-
-
-class geBaseActionEditor : public Fl_Group
-{
-protected:
-
-       class gdActionEditor *pParent;
-       void  baseDraw(bool clear=true);
-
-public:
-
-       virtual void updateActions() = 0;
-
-       geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent);
-       ~geBaseActionEditor();
-};
-
-#endif
diff --git a/src/gui/elems/basics/baseButton.cpp b/src/gui/elems/basics/baseButton.cpp
new file mode 100644 (file)
index 0000000..6aa636e
--- /dev/null
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseButton
+ * Base class for every button widget.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "baseButton.h"
+
+
+geBaseButton::geBaseButton(int x, int y, int w, int h, const char *l)
+  : Fl_Button(x, y, w, h, l)
+{
+  initLabel = l ? l : "";
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::trimLabel()
+{
+  if (initLabel.empty())
+    return;
+
+  std::string out;
+  if (w() > 20) {
+    out = initLabel;
+    int len = initLabel.size();
+    while (fl_width(out.c_str(), out.size()) > w()) {
+      out = initLabel.substr(0, len) + "...";
+      len--;
+    }
+  }
+  else {
+    out = "";
+  }
+  copy_label(out.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::label(const char *l)
+{
+  Fl_Button::label(l);
+  initLabel = l;
+  trimLabel();
+}
+
+
+const char *geBaseButton::label()
+{
+  return Fl_Button::label();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::resize(int X, int Y, int W, int H)
+{
+  trimLabel();
+  Fl_Button::resize(X, Y, W, H);
+}
diff --git a/src/gui/elems/basics/baseButton.h b/src/gui/elems/basics/baseButton.h
new file mode 100644 (file)
index 0000000..e628cca
--- /dev/null
@@ -0,0 +1,57 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseButton
+ * Base class for every button widget.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BASE_BUTTON_H
+#define GE_BASE_BUTTON_H
+
+
+#include <string>
+#include <FL/Fl_Button.H>
+
+
+class geBaseButton : public Fl_Button
+{
+private:
+
+       std::string initLabel;
+
+       void trimLabel();
+
+public:
+
+  geBaseButton(int x, int y, int w, int h, const char *l=0);
+
+  void resize(int x, int y, int w, int h) override;
+  void label(const char *l);
+       const char *label();
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/box.cpp b/src/gui/elems/basics/box.cpp
new file mode 100644 (file)
index 0000000..0de1ec9
--- /dev/null
@@ -0,0 +1,41 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "box.h"
+
+
+geBox::geBox(int x, int y, int w, int h, const char *l, Fl_Align al)
+: Fl_Box(x, y, w, h)
+{
+  copy_label(l);
+  labelsize(GUI_FONT_SIZE_BASE);
+  box(FL_NO_BOX);
+  labelcolor(COLOR_TEXT_0);
+  if (al != 0)
+    align(al | FL_ALIGN_INSIDE);
+}
diff --git a/src/gui/elems/basics/box.h b/src/gui/elems/basics/box.h
new file mode 100644 (file)
index 0000000..8b96a72
--- /dev/null
@@ -0,0 +1,43 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BOX_H
+#define GE_BOX_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geBox : public Fl_Box
+{
+public:
+
+  geBox(int x, int y, int w, int h, const char *l=0, Fl_Align al=FL_ALIGN_CENTER);
+};
+
+
+#endif
index 547d1aa8459c2f80f4ec0b427a9ce691290e4ba4..87d9c14743571d2d6c0605287c2fce529e6a0452 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index d717c8643d2bc1b7be70b2b25f9278b60b686265..71d72c07c1185ae972a503dc501768721154ee5c 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -27,8 +27,8 @@
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __BOXTYPES_H__
-#define __BOXTYPES_H__
+#ifndef GE_BOXTYPES_H
+#define GE_BOXTYPES_H
 
 
 #include <FL/Fl.H>
diff --git a/src/gui/elems/basics/button.cpp b/src/gui/elems/basics/button.cpp
new file mode 100644 (file)
index 0000000..06230e8
--- /dev/null
@@ -0,0 +1,75 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geButton
+ * A regular button.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "button.h"
+
+
+geButton::geButton(int x, int y, int w, int h, const char *L,
+  const char **imgOff, const char **imgOn)
+: geBaseButton(x, y, w, h, L),
+  imgOff      (imgOff),
+  imgOn       (imgOn),
+  bgColor0    (COLOR_BG_0),
+  bgColor1    (COLOR_BG_1),
+  bdColor     (COLOR_BD_0),
+  txtColor    (COLOR_TEXT_0)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geButton::draw()
+{
+  if (!active()) txtColor = bdColor;
+  else           txtColor = COLOR_TEXT_0;
+
+  fl_rect(x(), y(), w(), h(), bdColor);             // borders
+  if (value()) {                                    // -- clicked
+    if (imgOn != nullptr)
+      fl_draw_pixmap(imgOn, x()+1, y()+1);
+    else
+      fl_rectf(x(), y(), w(), h(), bgColor1);       // covers the border
+  }
+  else {                                            // -- not clicked
+    fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border
+    if (imgOff != nullptr)
+      fl_draw_pixmap(imgOff, x()+1, y()+1);
+  }
+  if (!active())
+    fl_color(FL_INACTIVE_COLOR);
+
+  fl_color(txtColor);
+  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+  fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
+}
diff --git a/src/gui/elems/basics/button.h b/src/gui/elems/basics/button.h
new file mode 100644 (file)
index 0000000..4d9a085
--- /dev/null
@@ -0,0 +1,56 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geButton
+ * A regular button.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BUTTON_H
+#define GE_BUTTON_H
+
+
+#include "baseButton.h"
+
+
+class geButton : public geBaseButton
+{
+public:
+
+  geButton(int x, int y, int w, int h, const char *L=0,
+    const char **imgOff=nullptr, const char **imgOn=nullptr);
+
+  void draw() override;
+
+  const char **imgOff;
+       const char **imgOn;
+       Fl_Color bgColor0;   // background not clicked
+       Fl_Color bgColor1;   // background clicked
+       Fl_Color bdColor;    // border
+       Fl_Color txtColor;       // text
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/check.cpp b/src/gui/elems/basics/check.cpp
new file mode 100644 (file)
index 0000000..1297e95
--- /dev/null
@@ -0,0 +1,59 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "check.h"
+
+
+geCheck::geCheck(int x, int y, int w, int h, const char *l)
+: Fl_Check_Button(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geCheck::draw()
+{
+  int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+  if (value()) {
+    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+    fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+  }
+  else {
+    fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+  }
+
+  fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
+  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+  fl_color(!active() ? FL_INACTIVE_COLOR : COLOR_TEXT_0);
+  fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
diff --git a/src/gui/elems/basics/check.h b/src/gui/elems/basics/check.h
new file mode 100644 (file)
index 0000000..2d31446
--- /dev/null
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_CHECK_H
+#define GE_CHECK_H
+
+
+#include <FL/Fl_Check_Button.H>
+
+
+class geCheck : public Fl_Check_Button
+{
+public:
+
+  geCheck(int x, int y, int w, int h, const char *l=0);
+
+  void draw();
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/choice.cpp b/src/gui/elems/basics/choice.cpp
new file mode 100644 (file)
index 0000000..8c4faa5
--- /dev/null
@@ -0,0 +1,85 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "choice.h"
+
+
+geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang)
+  : Fl_Choice(x, y, w, h, l), angle(ang)
+{
+  labelsize(GUI_FONT_SIZE_BASE);
+  labelcolor(COLOR_TEXT_0);
+  box(FL_BORDER_BOX);
+  textsize(GUI_FONT_SIZE_BASE);
+  textcolor(COLOR_TEXT_0);
+  color(COLOR_BG_0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::draw()
+{
+  fl_rectf(x(), y(), w(), h(), COLOR_BG_0);              // bg
+  fl_rect(x(), y(), w(), h(), (Fl_Color) COLOR_BD_0);    // border
+  if (angle)
+    fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
+
+  /* pick up the text() from the selected item (value()) and print it in
+   * the box and avoid overflows */
+
+  fl_color(!active() ? COLOR_BD_0 : COLOR_TEXT_0);
+  if (value() != -1) {
+    if (fl_width(text(value())) < w()-8) {
+      fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
+    }
+    else {
+      std::string tmp = text(value());
+      int size        = tmp.size();
+      while (fl_width(tmp.c_str()) >= w()-16) {
+        tmp.resize(size);
+        size--;
+      }
+      tmp += "...";
+      fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+    }
+
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::showItem(const char *c)
+{
+  value(find_index(c));
+}
diff --git a/src/gui/elems/basics/choice.h b/src/gui/elems/basics/choice.h
new file mode 100644 (file)
index 0000000..31bd15c
--- /dev/null
@@ -0,0 +1,49 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_CHOICE_H
+#define GE_CHOICE_H
+
+
+#include <FL/Fl_Choice.H>
+
+
+class geChoice : public Fl_Choice
+{
+public:
+
+       geChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
+       void draw();
+
+       void showItem(const char *c);
+
+       bool angle;
+       int  id;
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/dial.cpp b/src/gui/elems/basics/dial.cpp
new file mode 100644 (file)
index 0000000..0d141e2
--- /dev/null
@@ -0,0 +1,59 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "dial.h"
+
+
+geDial::geDial(int x, int y, int w, int h, const char *l)
+: Fl_Dial(x, y, w, h, l)
+{
+  labelsize(GUI_FONT_SIZE_BASE);
+  labelcolor(COLOR_TEXT_0);
+  align(FL_ALIGN_LEFT);
+  type(FL_FILL_DIAL);
+  angles(0, 360);
+  color(COLOR_BG_0);            // background
+  selection_color(COLOR_BG_1);   // selection
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geDial::draw()
+{
+  double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
+
+  fl_color(COLOR_BG_0);
+  fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
+
+  fl_color(COLOR_BD_0);
+  fl_arc(x(), y(), w(), h(), 0, 360);
+  fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
+}
diff --git a/src/gui/elems/basics/dial.h b/src/gui/elems/basics/dial.h
new file mode 100644 (file)
index 0000000..3da6030
--- /dev/null
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_DIAL_H
+#define GE_DIAL_H
+
+
+#include <FL/Fl_Dial.H>
+
+
+class geDial : public Fl_Dial
+{
+public:
+
+       geDial(int x, int y, int w, int h, const char *l=0);
+
+       void draw();
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/idButton.cpp b/src/gui/elems/basics/idButton.cpp
new file mode 100644 (file)
index 0000000..0da67ca
--- /dev/null
@@ -0,0 +1,39 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geIdButton
+ * Exactly as geButton but with a unique id. Used for the buttons in channels
+ * and for FXs.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "idButton.h"
+
+
+geIdButton::geIdButton(int X, int Y, int W, int H, const char *L,
+  const char **imgOff, const char **imgOn)
+  : geButton(X, Y, W, H, L, imgOff, imgOn)
+{
+}
diff --git a/src/gui/elems/basics/idButton.h b/src/gui/elems/basics/idButton.h
new file mode 100644 (file)
index 0000000..4d37e3a
--- /dev/null
@@ -0,0 +1,51 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geIdButton
+ * Exactly as geButton but with a unique id. Used for the buttons in channels
+ * and for FXs.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_ID_BUTTON_H
+#define GE_ID_BUTTON_H
+
+
+#include "button.h"
+
+
+class geIdButton : public geButton
+{
+public:
+
+  geIdButton(int X,int Y,int W,int H,const char *L=0,
+    const char **imgOff=nullptr, const char **imgOn=nullptr);
+
+  int key;
+       int id;
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/input.cpp b/src/gui/elems/basics/input.cpp
new file mode 100644 (file)
index 0000000..0a2384c
--- /dev/null
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "boxtypes.h"
+#include "input.h"
+
+
+geInput::geInput(int x, int y, int w, int h, const char *l)
+  : Fl_Input(x, y, w, h, l)
+{
+  //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
+  box(G_CUSTOM_BORDER_BOX);
+  labelsize(GUI_FONT_SIZE_BASE);
+  labelcolor(COLOR_TEXT_0);
+  color(COLOR_BG_DARK);
+  textcolor(COLOR_TEXT_0);
+  cursor_color(COLOR_TEXT_0);
+  selection_color(COLOR_BD_0);
+  textsize(GUI_FONT_SIZE_BASE);
+}
diff --git a/src/gui/elems/basics/input.h b/src/gui/elems/basics/input.h
new file mode 100644 (file)
index 0000000..8072ae4
--- /dev/null
@@ -0,0 +1,42 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_INPUT_H
+#define GE_INPUT_H
+
+
+#include <FL/Fl_Input.H>
+
+
+class geInput : public Fl_Input
+{
+public:
+
+       geInput(int x, int y, int w, int h, const char *l=0);
+};
+
+#endif
diff --git a/src/gui/elems/basics/liquidScroll.cpp b/src/gui/elems/basics/liquidScroll.cpp
new file mode 100644 (file)
index 0000000..c651d14
--- /dev/null
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gLiquidScroll
+ * custom scroll that tells children to follow scroll's width when
+ * resized. Thanks to Greg Ercolano from FLTK dev team.
+ * http://seriss.com/people/erco/fltk/
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "boxtypes.h"
+#include "liquidScroll.h"
+
+
+geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, const char *l)
+  : Fl_Scroll(x, y, w, h, l)
+{
+  type(Fl_Scroll::VERTICAL);
+  scrollbar.color(COLOR_BG_0);
+  scrollbar.selection_color(COLOR_BG_1);
+  scrollbar.labelcolor(COLOR_BD_1);
+  scrollbar.slider(G_CUSTOM_BORDER_BOX);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geLiquidScroll::resize(int X, int Y, int W, int H)
+{
+  int nc = children()-2;                // skip hscrollbar and vscrollbar
+  for ( int t=0; t<nc; t++) {           // tell children to resize to our new width
+    Fl_Widget *c = child(t);
+    c->resize(c->x(), c->y(), W-24, c->h());    // W-24: leave room for scrollbar
+  }
+  init_sizes();   // tell scroll children changed in size
+  Fl_Scroll::resize(X,Y,W,H);
+}
diff --git a/src/gui/elems/basics/liquidScroll.h b/src/gui/elems/basics/liquidScroll.h
new file mode 100644 (file)
index 0000000..cdc52e1
--- /dev/null
@@ -0,0 +1,50 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gLiquidScroll
+ * custom scroll that tells children to follow scroll's width when
+ * resized. Thanks to Greg Ercolano from FLTK dev team.
+ * http://seriss.com/people/erco/fltk/
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_LIQUID_SCROLL_H
+#define GE_LIQUID_SCROLL_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class geLiquidScroll : public Fl_Scroll
+{
+public:
+
+       geLiquidScroll(int x, int y, int w, int h, const char *l=0);
+
+       void resize(int x, int y, int w, int h);
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/progress.cpp b/src/gui/elems/basics/progress.cpp
new file mode 100644 (file)
index 0000000..2db4201
--- /dev/null
@@ -0,0 +1,38 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "boxtypes.h"
+#include "progress.h"
+
+
+geProgress::geProgress(int x, int y, int w, int h, const char *l)
+: Fl_Progress(x, y, w, h, l)
+{
+  color(COLOR_BG_0, COLOR_BD_0);
+  box(G_CUSTOM_BORDER_BOX);
+}
diff --git a/src/gui/elems/basics/progress.h b/src/gui/elems/basics/progress.h
new file mode 100644 (file)
index 0000000..5a6a68c
--- /dev/null
@@ -0,0 +1,43 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PROGRESS_H
+#define GE_PROGRESS_H
+
+
+#include <FL/Fl_Progress.H>
+
+
+class geProgress : public Fl_Progress
+{
+public:
+
+       geProgress(int x, int y, int w, int h, const char *l=0);
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/radio.cpp b/src/gui/elems/basics/radio.cpp
new file mode 100644 (file)
index 0000000..cca9daf
--- /dev/null
@@ -0,0 +1,59 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "radio.h"
+
+
+geRadio::geRadio(int x, int y, int w, int h, const char *l)
+: Fl_Radio_Button(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRadio::draw()
+{
+ int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+ if (value()) {
+   fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+   fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+ }
+ else {
+   fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+   fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ }
+
+ fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
+ fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+ fl_color(COLOR_TEXT_0);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
diff --git a/src/gui/elems/basics/radio.h b/src/gui/elems/basics/radio.h
new file mode 100644 (file)
index 0000000..651b034
--- /dev/null
@@ -0,0 +1,44 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_RADIO_H
+#define GE_RADIO_H
+
+
+#include <FL/Fl_Radio_Button.H>
+
+
+class geRadio : public Fl_Radio_Button
+{
+public:
+
+  geRadio(int x, int y, int w, int h, const char *l=0);
+
+  void draw();
+};
+
+#endif
diff --git a/src/gui/elems/basics/resizerBar.cpp b/src/gui/elems/basics/resizerBar.cpp
new file mode 100644 (file)
index 0000000..da30c69
--- /dev/null
@@ -0,0 +1,182 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geResizerBar
+ * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
+ * FLTK dev team. http://seriss.com/people/erco/fltk/
+ *
+ * Shows a resize cursor when hovered over.
+ * Assumes:
+ *     - Parent is an Fl_Scroll
+ *     - All children of Fl_Scroll are vertically arranged
+ *     - The widget above us has a bottom edge touching our top edge
+ *       ie. (w->y()+w->h() == this->y())
+ *
+ * When this widget is dragged:
+ *     - The widget above us (with a common edge) will be /resized/
+ *       vertically
+ *     - All children below us will be /moved/ vertically
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include <FL/Fl_Scroll.H>
+#include <FL/fl_draw.H>
+#include "resizerBar.h"
+
+
+geResizerBar::geResizerBar(int X,int Y,int W,int H, bool vertical)
+  : Fl_Box(X,Y,W,H), vertical(vertical)
+{
+  last_y = 0;
+  min_h  = 30;
+  if (vertical) {
+    orig_h = H;
+    labelsize(H);
+  }
+  else {
+    orig_h = W;
+    labelsize(W);
+  }
+  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
+  labelfont(FL_COURIER);
+  visible_focus(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geResizerBar::HandleDrag(int diff)
+{
+  Fl_Scroll *grp = static_cast<Fl_Scroll*>(parent());
+  int top;
+  int bot;
+  if (vertical) {
+    top = y();
+    bot = y()+h();
+  }
+  else {
+    top = x();
+    bot = x()+w();
+  }
+
+  // First pass: find widget directly above us with common edge
+  //    Possibly clamp 'diff' if widget would get too small..
+
+  for (int t=0; t<grp->children(); t++) {
+    Fl_Widget *wd = grp->child(t);
+    if (vertical) {
+      if ((wd->y()+wd->h()) == top) {                           // found widget directly above?
+        if ((wd->h()+diff) < min_h)
+          diff = wd->h() - min_h;                              // clamp
+        wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff);       // change height
+        break;                                                // done with first pass
+      }
+    }
+    else {
+      if ((wd->x()+wd->w()) == top) {                           // found widget directly above?
+        if ((wd->w()+diff) < min_h)
+          diff = wd->w() - min_h;                              // clamp
+        wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h());       // change height
+        break;                                                // done with first pass
+      }
+    }
+  }
+
+  // Second pass: find widgets below us, move based on clamped diff
+
+  for (int t=0; t<grp->children(); t++) {
+    Fl_Widget *wd = grp->child(t);
+    if (vertical) {
+      if (wd->y() >= bot)                                     // found widget below us?
+        wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h());      // change position
+    }
+    else {
+      if (wd->x() >= bot)
+        wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h());
+    }
+  }
+
+  // Change our position last
+
+  if (vertical)
+    resize(x(), y()+diff, w(), h());
+  else
+    resize(x()+diff, y(), w(), h());
+
+  grp->init_sizes();
+  grp->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geResizerBar::handle(int e)
+{
+  int ret = 0;
+  int this_y;
+  if (vertical)
+    this_y = Fl::event_y_root();
+  else
+    this_y = Fl::event_x_root();
+  switch (e) {
+    case FL_FOCUS:
+      ret = 1;
+      break;
+    case FL_ENTER:
+      ret = 1;
+      fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE);
+      break;
+    case FL_LEAVE:
+      ret = 1;
+      fl_cursor(FL_CURSOR_DEFAULT);
+      break;
+    case FL_PUSH:
+      ret = 1;
+      last_y = this_y;
+      break;
+    case FL_DRAG:
+      HandleDrag(this_y-last_y);
+      last_y = this_y;
+      ret = 1;
+      break;
+    default: break;
+  }
+  return(Fl_Box::handle(e) | ret);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geResizerBar::resize(int X,int Y,int W,int H)
+{
+  if (vertical)
+    Fl_Box::resize(X,Y,W,orig_h);                                // height of resizer stays constant size
+  else
+    Fl_Box::resize(X,Y,orig_h,H);
+}
diff --git a/src/gui/elems/basics/resizerBar.h b/src/gui/elems/basics/resizerBar.h
new file mode 100644 (file)
index 0000000..74f5bbf
--- /dev/null
@@ -0,0 +1,80 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geResizerBar
+ * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
+ * FLTK dev team. http://seriss.com/people/erco/fltk/
+ *
+ * Shows a resize cursor when hovered over.
+ * Assumes:
+ *     - Parent is an Fl_Scroll
+ *     - All children of Fl_Scroll are vertically arranged
+ *     - The widget above us has a bottom edge touching our top edge
+ *       ie. (w->y()+w->h() == this->y())
+ *
+ * When this widget is dragged:
+ *     - The widget above us (with a common edge) will be /resized/
+ *       vertically
+ *     - All children below us will be /moved/ vertically
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_RESIZER_BAR_H
+#define GE_RESIZER_BAR_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geResizerBar : public Fl_Box
+{
+private:
+
+  /* TODO - use more general variable names
+   * (last_y -> last_?, min_h -> min_?, ...) */
+
+  bool vertical;
+       int  orig_h;
+       int  last_y;
+       int  min_h;   // min height for widget above us
+
+       void HandleDrag(int diff);
+
+public:
+
+ /* 'vertical' defines the bar movement. Vertical=true: the bar moves
+  * vertically (up and down). */
+
+       geResizerBar(int x, int y, int w, int h, bool vertical=true);
+
+  inline void setMinSize(int val) { min_h = val; }
+  inline int  getMinSize()        { return min_h; }
+
+  int  handle(int e);
+  void resize(int x, int y, int w, int h);
+};
+
+
+#endif
index 9cee240370499a4f31e0f70c26922d02754bf2c7..4d6477bf39c435ffdb88e26df96145dcda6bee35 100644 (file)
@@ -7,7 +7,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index b122d4412c69d277b2fe93cf70da91191e19ce97..5f384874b247ecd29429d36ede6e35a5ff4f98c8 100644 (file)
@@ -7,7 +7,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -28,8 +28,8 @@
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __GE_SCROLL_H__
-#define __GE_SCROLL_H__
+#ifndef GE_SCROLL_H
+#define GE_SCROLL_H
 
 
 #include <FL/Fl_Scroll.H>
diff --git a/src/gui/elems/basics/slider.cpp b/src/gui/elems/basics/slider.cpp
new file mode 100644 (file)
index 0000000..03ac86d
--- /dev/null
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "boxtypes.h"
+#include "slider.h"
+
+
+geSlider::geSlider(int x, int y, int w, int h, const char *l)
+ : Fl_Slider(x, y, w, h, l)
+{
+ type(FL_HOR_FILL_SLIDER);
+
+ labelsize(GUI_FONT_SIZE_BASE);
+ align(FL_ALIGN_LEFT);
+ labelcolor(COLOR_TEXT_0);
+
+ box(G_CUSTOM_BORDER_BOX);
+ color(COLOR_BG_0);
+ selection_color(COLOR_BD_0);
+}
diff --git a/src/gui/elems/basics/slider.h b/src/gui/elems/basics/slider.h
new file mode 100644 (file)
index 0000000..0103e93
--- /dev/null
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_SLIDER_H
+#define GE_SLIDER_H
+
+
+#include <FL/Fl_Slider.H>
+
+
+class geSlider : public Fl_Slider
+{
+public:
+
+  geSlider(int x, int y, int w, int h, const char *l=0);
+
+  int id;
+};
+
+
+#endif
diff --git a/src/gui/elems/basics/statusButton.cpp b/src/gui/elems/basics/statusButton.cpp
new file mode 100644 (file)
index 0000000..dd738f4
--- /dev/null
@@ -0,0 +1,52 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geStatusButton
+ * Simple geButton with a boolean 'status' parameter.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "statusButton.h"
+
+
+geStatusButton::geStatusButton(int x, int y, int w, int h, const char **imgOff,
+  const char **imgOn)
+  : geButton(x, y, w, h, nullptr, imgOff, imgOn),
+    status  (false)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geStatusButton::draw()
+{
+  geButton::draw();
+  if (status)
+    fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0);
+}
diff --git a/src/gui/elems/basics/statusButton.h b/src/gui/elems/basics/statusButton.h
new file mode 100644 (file)
index 0000000..b174a01
--- /dev/null
@@ -0,0 +1,51 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geStatusButton
+ * Simple geButton with a boolean 'status' parameter.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_STATUS_BUTTON_H
+#define GE_STATUS_BUTTON_H
+
+
+#include "button.h"
+
+
+class geStatusButton : public geButton
+{
+public:
+
+       geStatusButton(int x, int y, int w, int h, const char **imgOff=nullptr,
+    const char **imgOn=nullptr);
+
+       void draw() override;
+
+       bool status;
+};
+
+
+#endif
index 812aaba3065dedff44bef77c00ad85b8af580322..0f0290ced0bc6d710ce1552ce8aab80e9a4a82a8 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_browser
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include "../../core/const.h"
 #include "../../utils/string.h"
-#include "../dialogs/gd_browser.h"
-#include "../elems/ge_mixed.h"
+#include "../dialogs/browser/browserBase.h"
 #include "basics/boxtypes.h"
 #include "browser.h"
 
 
+using std::string;
+
+
 geBrowser::geBrowser(int x, int y, int w, int h)
  : Fl_File_Browser(x, y, w, h),
    showHiddenFiles(false)
@@ -108,12 +108,12 @@ int geBrowser::handle(int e)
         select(value() - 1);
       else
       if (Fl::event_key(FL_Enter))
-        ((gdBaseBrowser*) parent())->fireCallback();
+        ((gdBrowserBase*) parent())->fireCallback();
       ret = 1;
       break;
     case FL_PUSH:    // mouse
       if (Fl::event_clicks() > 0)  // double click
-        ((gdBaseBrowser*) parent())->fireCallback();
+        ((gdBrowserBase*) parent())->fireCallback();
       ret = 1;
       break;
     case FL_RELEASE: // mouse
index 4515dbf9eb4fb2b58a33c038a3e2abaeb8432157..03cb85f1f5220145b1c0172e1ff7e23b07b8cb9f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_File_Browser.H>
 
 
-using std::string;
-
-
 class geBrowser : public Fl_File_Browser
 {
 private:
 
-       string currentDir;
+       std::string currentDir;
   bool showHiddenFiles;
 
        /* normalize
-        * Make sure the string never ends with a trailing slash. */
+        * Make sure the std::string never ends with a trailing slash. */
 
-       string normalize(const string &s);
+       std::string normalize(const std::string &s);
 
 public:
 
@@ -59,15 +56,15 @@ public:
        /* init
         * Initialize browser and show 'dir' as initial directory. */
 
-       void loadDir(const string &dir);
+       void loadDir(const std::string &dir);
 
        /* getSelectedItem
         * Return the full path or just the displayed name of the i-th selected item.
         * Always with the trailing slash! */
 
-       string getSelectedItem(bool fullPath=true);
+       std::string getSelectedItem(bool fullPath=true);
 
-       string getCurrentDir();
+       std::string getCurrentDir();
 
        void preselect(int position, int line);
 
diff --git a/src/gui/elems/config/tabAudio.cpp b/src/gui/elems/config/tabAudio.cpp
new file mode 100644 (file)
index 0000000..c566b14
--- /dev/null
@@ -0,0 +1,512 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../deps/rtaudio-mod/RtAudio.h"
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/kernelAudio.h"
+#include "../../../utils/string.h"
+#include "../../../gui/dialogs/gd_devInfo.h"
+#include "../basics/box.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "../basics/input.h"
+#include "../basics/button.h"
+#include "tabAudio.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabAudio::geTabAudio(int X, int Y, int W, int H)
+       : Fl_Group(X, Y, W, H, "Sound System")
+{
+       begin();
+       soundsys    = new geChoice(x()+92,  y()+9,  253, 20, "System");
+       buffersize  = new geChoice(x()+92,  y()+37, 55,  20, "Buffer size");
+       samplerate  = new geChoice(x()+290, y()+37, 55,  20, "Sample rate");
+       sounddevOut = new geChoice(x()+92,  y()+65, 225, 20, "Output device");
+       devOutInfo  = new geButton (x()+325, y()+65, 20,  20, "?");
+       channelsOut = new geChoice(x()+92,  y()+93, 55,  20, "Output channels");
+       limitOutput = new geCheck (x()+155, y()+97, 55,  20, "Limit output");
+       sounddevIn  = new geChoice(x()+92,  y()+121, 225, 20, "Input device");
+       devInInfo   = new geButton (x()+325, y()+121, 20,  20, "?");
+       channelsIn  = new geChoice(x()+92,  y()+149, 55,  20, "Input channels");
+       delayComp   = new geInput (x()+290, y()+149, 55,  20, "Rec delay comp.");
+       rsmpQuality = new geChoice(x()+92, y()+177, 253, 20, "Resampling");
+                new geBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92,
+                                                                               "Restart Giada for the changes to take effect.");
+       end();
+       labelsize(GUI_FONT_SIZE_BASE);
+
+       soundsys->add("(none)");
+
+#if defined(__linux__)
+
+       if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+               soundsys->add("ALSA");
+       if (kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+               soundsys->add("Jack");
+       if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+               soundsys->add("PulseAudio");
+
+       switch (conf::soundSystem) {
+               case G_SYS_API_NONE:
+                       soundsys->showItem("(none)");
+                       break;
+               case G_SYS_API_ALSA:
+                       soundsys->showItem("ALSA");
+                       break;
+               case G_SYS_API_JACK:
+                       soundsys->showItem("Jack");
+                       buffersize->deactivate();
+                       samplerate->deactivate();
+                       break;
+               case G_SYS_API_PULSE:
+                       soundsys->showItem("PulseAudio");
+                       break;
+       }
+
+#elif defined(_WIN32)
+
+       if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
+               soundsys->add("DirectSound");
+       if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+               soundsys->add("ASIO");
+       if (kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
+               soundsys->add("WASAPI");
+
+       switch (conf::soundSystem) {
+               case G_SYS_API_NONE:
+                       soundsys->showItem("(none)");
+                       break;
+               case G_SYS_API_DS:
+                       soundsys->showItem("DirectSound");
+                       break;
+               case G_SYS_API_ASIO:
+                       soundsys->showItem("ASIO");
+                       break;
+               case G_SYS_API_WASAPI:
+                       soundsys->showItem("WASAPI");
+                       break;
+       }
+
+#elif defined (__APPLE__)
+
+       if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+               soundsys->add("CoreAudio");
+
+       switch (conf::soundSystem) {
+               case G_SYS_API_NONE:
+                       soundsys->showItem("(none)");
+                       break;
+               case G_SYS_API_CORE:
+                       soundsys->showItem("CoreAudio");
+                       break;
+       }
+
+#endif
+
+       soundsysInitValue = soundsys->value();
+
+       soundsys->callback(cb_deactivate_sounddev, (void*)this);
+
+       sounddevIn->callback(cb_fetchInChans, this);
+       sounddevOut->callback(cb_fetchOutChans, this);
+
+       devOutInfo->callback(cb_showOutputInfo, this);
+       devInInfo->callback(cb_showInputInfo, this);
+
+       if (conf::soundSystem != G_SYS_API_NONE) {
+               fetchSoundDevs();
+               fetchOutChans(sounddevOut->value());
+               fetchInChans(sounddevIn->value());
+
+               /* fill frequency dropdown menu */
+               /* TODO - add fetchFrequencies() */
+
+               int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
+               for (int i=0; i<nfreq; i++) {
+                       int freq = kernelAudio::getFreq(sounddevOut->value(), i);
+                       samplerate->add(gu_itoa(freq).c_str());
+                       if (freq == conf::samplerate)
+                               samplerate->value(i);
+               }
+       }
+       else {
+               sounddevIn->deactivate();
+               sounddevOut->deactivate();
+               channelsIn->deactivate();
+               channelsOut->deactivate();
+               devOutInfo->deactivate();
+               devInInfo->deactivate();
+               samplerate->deactivate();
+       }
+
+       buffersize->add("8");
+       buffersize->add("16");
+       buffersize->add("32");
+       buffersize->add("64");
+       buffersize->add("128");
+       buffersize->add("256");
+       buffersize->add("512");
+       buffersize->add("1024");
+       buffersize->add("2048");
+       buffersize->add("4096");
+       buffersize->showItem(gu_itoa(conf::buffersize).c_str());
+
+       rsmpQuality->add("Sinc best quality (very slow)");
+       rsmpQuality->add("Sinc medium quality (slow)");
+       rsmpQuality->add("Sinc basic quality (medium)");
+       rsmpQuality->add("Zero Order Hold (fast)");
+       rsmpQuality->add("Linear (very fast)");
+       rsmpQuality->value(conf::rsmpQuality);
+
+       delayComp->value(gu_itoa(conf::delayComp).c_str());
+       delayComp->type(FL_INT_INPUT);
+       delayComp->maximum_size(5);
+
+       limitOutput->value(conf::limitOutput);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_deactivate_sounddev(); }
+void geTabAudio::cb_fetchInChans(Fl_Widget *w, void *p)        { ((geTabAudio*)p)->__cb_fetchInChans(); }
+void geTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p)       { ((geTabAudio*)p)->__cb_fetchOutChans(); }
+void geTabAudio::cb_showInputInfo(Fl_Widget *w, void *p)       { ((geTabAudio*)p)->__cb_showInputInfo(); }
+void geTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p)      { ((geTabAudio*)p)->__cb_showOutputInfo(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_fetchInChans()
+{
+       fetchInChans(sounddevIn->value());
+       channelsIn->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_fetchOutChans()
+{
+       fetchOutChans(sounddevOut->value());
+       channelsOut->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_showInputInfo()
+{
+       unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       new gdDevInfo(dev);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_showOutputInfo()
+{
+       unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       new gdDevInfo(dev);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_deactivate_sounddev()
+{
+       /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
+        * If it returns to the original sound system, we re-fill the list by
+        * querying kernelAudio. Watch out if soundsysInitValue == 0: you don't want
+        * to query kernelAudio for '(none)' soundsystem! */
+
+       if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
+               sounddevOut->clear();
+               sounddevIn->clear();
+
+               fetchSoundDevs();
+
+               /* the '?' button is added by fetchSoundDevs */
+
+               fetchOutChans(sounddevOut->value());
+               sounddevOut->activate();
+               channelsOut->activate();
+
+               /* chan menus and '?' button are activated by fetchInChans(...) */
+
+               fetchInChans(sounddevIn->value());
+               sounddevIn->activate();
+               samplerate->activate();
+       }
+       else {
+               sounddevOut->deactivate();
+               sounddevOut->clear();
+               sounddevOut->add("-- restart to fetch device(s) --");
+               sounddevOut->value(0);
+               channelsOut->deactivate();
+               devOutInfo->deactivate();
+               samplerate->deactivate();
+
+               sounddevIn->deactivate();
+               sounddevIn->clear();
+               sounddevIn->add("-- restart to fetch device(s) --");
+               sounddevIn->value(0);
+               channelsIn->deactivate();
+               devInInfo->deactivate();
+       }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchInChans(int menuItem)
+{
+       /* if menuItem==0 device in input is disabled. */
+
+       if (menuItem == 0) {
+               devInInfo ->deactivate();
+               channelsIn->deactivate();
+               delayComp ->deactivate();
+               return;
+       }
+
+       devInInfo ->activate();
+       channelsIn->activate();
+       delayComp ->activate();
+
+       channelsIn->clear();
+
+       unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       unsigned chs = kernelAudio::getMaxInChans(dev);
+
+       if (chs == 0) {
+               channelsIn->add("none");
+               channelsIn->value(0);
+               return;
+       }
+       for (unsigned i=0; i<chs; i+=2) {
+               char str[16];
+               sprintf(str, "%d-%d", (i+1), (i+2));
+               channelsIn->add(str);
+       }
+       channelsIn->value(conf::channelsIn);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchOutChans(int menuItem)
+{
+       channelsOut->clear();
+
+       unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       unsigned chs = kernelAudio::getMaxOutChans(dev);
+
+       if (chs == 0) {
+               channelsOut->add("none");
+               channelsOut->value(0);
+               return;
+       }
+       for (unsigned i=0; i<chs; i+=2) {
+               char str[16];
+               sprintf(str, "%d-%d", (i+1), (i+2));
+               channelsOut->add(str);
+       }
+       channelsOut->value(conf::channelsOut);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geTabAudio::findMenuDevice(geChoice *m, int device)
+{
+       if (device == -1)
+               return 0;
+
+       if (kernelAudio::getStatus() == false)
+               return 0;
+
+       for (int i=0; i<m->size(); i++) {
+               if (kernelAudio::getDeviceName(device) == "")
+                       continue;
+               if (m->text(i) == nullptr)
+                       continue;
+               if (m->text(i) == kernelAudio::getDeviceName(device))
+                       return i;
+       }
+
+       return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchSoundDevs()
+{
+       if (kernelAudio::countDevices() == 0) {
+               sounddevOut->add("-- no devices found --");
+               sounddevOut->value(0);
+               sounddevIn->add("-- no devices found --");
+               sounddevIn->value(0);
+               devInInfo ->deactivate();
+               devOutInfo->deactivate();
+       }
+       else {
+
+               devInInfo ->activate();
+               devOutInfo->activate();
+
+               /* input device may be disabled: now device number -1 is the disabled
+                * one. KernelAudio knows how to handle it. */
+
+               sounddevIn->add("(disabled)");
+
+               for (unsigned i=0; i<kernelAudio::countDevices(); i++) {
+
+                       /* escaping '/', very dangerous in FLTK (it creates a submenu) */
+
+                       string tmp = kernelAudio::getDeviceName(i);
+                       for (unsigned k=0; k<tmp.size(); k++)
+                               if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
+                                       tmp[k] = '-';
+
+                       /* add to list devices with at least 1 channel available. In this
+                        * way we can filter devices only for input or output, e.g. an input
+                        * devices has 0 output channels. */
+
+                       if (kernelAudio::getMaxOutChans(i) > 0)
+                               sounddevOut->add(tmp.c_str());
+
+                       if (kernelAudio::getMaxInChans(i) > 0)
+                               sounddevIn->add(tmp.c_str());
+               }
+
+               /* we show the device saved in the configuration file. */
+
+               if (sounddevOut->size() == 0) {
+                       sounddevOut->add("-- no devices found --");
+                       sounddevOut->value(0);
+                       devOutInfo->deactivate();
+               }
+               else {
+                       int outMenuValue = findMenuDevice(sounddevOut, conf::soundDeviceOut);
+                       sounddevOut->value(outMenuValue);
+               }
+
+               if (sounddevIn->size() == 0) {
+                       sounddevIn->add("-- no devices found --");
+                       sounddevIn->value(0);
+                       devInInfo->deactivate();
+               }
+               else {
+                       int inMenuValue = findMenuDevice(sounddevIn, conf::soundDeviceIn);
+                       sounddevIn->value(inMenuValue);
+               }
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::save()
+{
+       string text = soundsys->text(soundsys->value());
+
+       if (text == "(none)") {
+               conf::soundSystem = G_SYS_API_NONE;
+               return;
+       }
+
+#if defined(__linux__)
+
+       else if (text == "ALSA")
+               conf::soundSystem = G_SYS_API_ALSA;
+       else if (text == "Jack")
+               conf::soundSystem = G_SYS_API_JACK;
+       else if (text == "PulseAudio")
+               conf::soundSystem = G_SYS_API_PULSE;
+
+#elif defined(_WIN32)
+
+       else if (text == "DirectSound")
+               conf::soundSystem = G_SYS_API_DS;
+       else if (text == "ASIO")
+               conf::soundSystem = G_SYS_API_ASIO;
+       else if (text == "WASAPI")
+               conf::soundSystem = G_SYS_API_WASAPI;
+
+#elif defined (__APPLE__)
+
+       else if (text == "CoreAudio")
+               conf::soundSystem = G_SYS_API_CORE;
+
+#endif
+
+       /* use the device name to search into the drop down menu's */
+
+       conf::soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       conf::soundDeviceIn  = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       conf::channelsOut    = channelsOut->value();
+       conf::channelsIn     = channelsIn->value();
+       conf::limitOutput    = limitOutput->value();
+       conf::rsmpQuality    = rsmpQuality->value();
+
+       /* if sounddevOut is disabled (because of system change e.g. alsa ->
+        * jack) its value is equal to -1. Change it! */
+
+       if (conf::soundDeviceOut == -1)
+               conf::soundDeviceOut = 0;
+
+       int bufsize = atoi(buffersize->text());
+       if (bufsize % 2 != 0) bufsize++;
+       if (bufsize < 8)                  bufsize = 8;
+       if (bufsize > 8192)             bufsize = 8192;
+       conf::buffersize = bufsize;
+
+       const Fl_Menu_Item *i = nullptr;
+       i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
+       if (i)
+               conf::samplerate = atoi(i->label());
+
+       conf::delayComp = atoi(delayComp->value());
+}
diff --git a/src/gui/elems/config/tabAudio.h b/src/gui/elems/config/tabAudio.h
new file mode 100644 (file)
index 0000000..9907666
--- /dev/null
@@ -0,0 +1,84 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_TAB_AUDIO_H
+#define GE_TAB_AUDIO_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+class geButton;
+class geInput;
+
+
+class geTabAudio : public Fl_Group
+{
+private:
+
+       static void cb_deactivate_sounddev(Fl_Widget *w, void *p);
+       static void cb_fetchInChans       (Fl_Widget *w, void *p);
+       static void cb_fetchOutChans      (Fl_Widget *w, void *p);
+       static void cb_showInputInfo      (Fl_Widget *w, void *p);
+       static void cb_showOutputInfo     (Fl_Widget *w, void *p);
+       inline void __cb_deactivate_sounddev();
+       inline void __cb_fetchInChans();
+       inline void __cb_fetchOutChans();
+       inline void __cb_showInputInfo();
+       inline void __cb_showOutputInfo();
+
+       void fetchSoundDevs();
+       void fetchInChans(int menuItem);
+       void fetchOutChans(int menuItem);
+       int  findMenuDevice(geChoice *m, int device);
+
+       int soundsysInitValue;
+
+public:
+
+       geChoice *soundsys;
+       geChoice *samplerate;
+       geChoice *rsmpQuality;
+       geChoice *sounddevIn;
+       geButton  *devInInfo;
+       geChoice *channelsIn;
+       geChoice *sounddevOut;
+       geButton  *devOutInfo;
+       geChoice *channelsOut;
+       geCheck  *limitOutput;
+       geChoice *buffersize;
+       geInput  *delayComp;
+
+       geTabAudio(int x, int y, int w, int h);
+
+       void save();
+};
+
+
+#endif
diff --git a/src/gui/elems/config/tabBehaviors.cpp b/src/gui/elems/config/tabBehaviors.cpp
new file mode 100644 (file)
index 0000000..e1b6c6c
--- /dev/null
@@ -0,0 +1,101 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl_Pack.H>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../basics/box.h"
+#include "../basics/radio.h"
+#include "../basics/check.h"
+#include "tabBehaviors.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
+       : Fl_Group(X, Y, W, H, "Behaviors")
+{
+  begin();
+
+  Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
+               new geBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
+               recsStopOnChanHalt_1 = new geRadio(x()+25, y()+35, 280, 20, "stop it immediately");
+               recsStopOnChanHalt_0 = new geRadio(x()+25, y()+55, 280, 20, "play it until finished");
+       radioGrp_1->end();
+
+       Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex
+               new geBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
+               chansStopOnSeqHalt_1 = new geRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
+               chansStopOnSeqHalt_0 = new geRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished");
+       radioGrp_2->end();
+
+       treatRecsAsLoops = new geCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops");
+  inputMonitorDefaultOn = new geCheck(x(), y()+180, 280, 20, "New sample channels have input monitor on by default");
+
+  end();
+
+       labelsize(GUI_FONT_SIZE_BASE);
+
+       conf::recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
+       conf::chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
+       treatRecsAsLoops->value(conf::treatRecsAsLoops);
+       inputMonitorDefaultOn->value(conf::inputMonitorDefaultOn);
+
+       recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
+       recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
+       chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
+       chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((geTabBehaviors*)p)->__cb_radio_mutex(w); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
+{
+       ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::save()
+{
+       conf::recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
+       conf::chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
+       conf::treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
+       conf::inputMonitorDefaultOn = inputMonitorDefaultOn->value() == 1 ? 1 : 0;
+}
diff --git a/src/gui/elems/config/tabBehaviors.h b/src/gui/elems/config/tabBehaviors.h
new file mode 100644 (file)
index 0000000..d5b2802
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_TAB_BEHAVIORS_H
+#define GE_TAB_BEHAVIORS_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geRadio;
+class geCheck;
+
+
+class geTabBehaviors : public Fl_Group
+{
+private:
+
+       static void cb_radio_mutex  (Fl_Widget *w, void *p);
+       inline void __cb_radio_mutex(Fl_Widget *w);
+
+public:
+
+       geRadio *recsStopOnChanHalt_1;
+       geRadio *recsStopOnChanHalt_0;
+       geRadio *chansStopOnSeqHalt_1;
+       geRadio *chansStopOnSeqHalt_0;
+       geCheck *treatRecsAsLoops;
+       geCheck *inputMonitorDefaultOn;
+
+       geTabBehaviors(int x, int y, int w, int h);
+
+       void save();
+};
+
+
+
+#endif
diff --git a/src/gui/elems/config/tabMidi.cpp b/src/gui/elems/config/tabMidi.cpp
new file mode 100644 (file)
index 0000000..4ffbd54
--- /dev/null
@@ -0,0 +1,254 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <rtmidi/RtMidi.h>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/midiMapConf.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../utils/gui.h"
+#include "../basics/box.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "tabMidi.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabMidi::geTabMidi(int X, int Y, int W, int H)
+       : Fl_Group(X, Y, W, H, "MIDI")
+{
+       begin();
+       system    = new geChoice(x()+92, y()+9, 253, 20, "System");
+       portOut   = new geChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port");
+       portIn    = new geChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port");
+       noNoteOff = new geCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff");
+       midiMap   = new geChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map");
+       sync        = new geChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync");
+       new geBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect.");
+       end();
+
+       labelsize(GUI_FONT_SIZE_BASE);
+
+       system->callback(cb_changeSystem, (void*)this);
+
+       fetchSystems();
+       fetchOutPorts();
+       fetchInPorts();
+       fetchMidiMaps();
+
+       noNoteOff->value(conf::noNoteOff);
+
+       sync->add("(disabled)");
+       sync->add("MIDI Clock (master)");
+       sync->add("MTC (master)");
+       if      (conf::midiSync == MIDI_SYNC_NONE)
+               sync->value(0);
+       else if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+               sync->value(1);
+       else if (conf::midiSync == MIDI_SYNC_MTC_M)
+               sync->value(2);
+
+       systemInitValue = system->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchOutPorts()
+{
+       if (kernelMidi::countOutPorts() == 0) {
+               portOut->add("-- no ports found --");
+               portOut->value(0);
+               portOut->deactivate();
+       }
+       else {
+
+               portOut->add("(disabled)");
+
+               for (unsigned i=0; i<kernelMidi::countOutPorts(); i++)
+                       portOut->add(gu_removeFltkChars(kernelMidi::getOutPortName(i)).c_str());
+
+               portOut->value(conf::midiPortOut+1);    // +1 because midiPortOut=-1 is '(disabled)'
+       }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchInPorts()
+{
+       if (kernelMidi::countInPorts() == 0) {
+               portIn->add("-- no ports found --");
+               portIn->value(0);
+               portIn->deactivate();
+       }
+       else {
+
+               portIn->add("(disabled)");
+
+               for (unsigned i=0; i<kernelMidi::countInPorts(); i++)
+                       portIn->add(gu_removeFltkChars(kernelMidi::getInPortName(i)).c_str());
+
+               portIn->value(conf::midiPortIn+1);    // +1 because midiPortIn=-1 is '(disabled)'
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchMidiMaps()
+{
+       if (midimap::maps.size() == 0) {
+               midiMap->add("(no MIDI maps available)");
+               midiMap->value(0);
+               midiMap->deactivate();
+               return;
+       }
+
+       for (unsigned i=0; i<midimap::maps.size(); i++) {
+               const char *imap = midimap::maps.at(i).c_str();
+               midiMap->add(imap);
+               if (conf::midiMapPath == imap)
+                       midiMap->value(i);
+       }
+
+       /* Preselect the 0 midimap if nothing is selected but midimaps exist. */
+
+       if (midiMap->value() == -1 && midimap::maps.size() > 0)
+               midiMap->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::save()
+{
+       string text = system->text(system->value());
+
+       if      (text == "ALSA")
+               conf::midiSystem = RtMidi::LINUX_ALSA;
+       else if (text == "Jack")
+               conf::midiSystem = RtMidi::UNIX_JACK;
+       else if (text == "Multimedia MIDI")
+               conf::midiSystem = RtMidi::WINDOWS_MM;
+       else if (text == "OSX Core MIDI")
+               conf::midiSystem = RtMidi::MACOSX_CORE;
+
+       conf::midiPortOut = portOut->value()-1;   // -1 because midiPortOut=-1 is '(disabled)'
+       conf::midiPortIn  = portIn->value()-1;    // -1 because midiPortIn=-1 is '(disabled)'
+
+       conf::noNoteOff   = noNoteOff->value();
+       conf::midiMapPath = midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
+
+       if      (sync->value() == 0)
+               conf::midiSync = MIDI_SYNC_NONE;
+       else if (sync->value() == 1)
+               conf::midiSync = MIDI_SYNC_CLOCK_M;
+       else if (sync->value() == 2)
+               conf::midiSync = MIDI_SYNC_MTC_M;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchSystems()
+{
+#if defined(__linux__)
+
+       if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
+               system->add("ALSA");
+       if (kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+               system->add("Jack");
+
+#elif defined(_WIN32)
+
+       if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
+               system->add("Multimedia MIDI");
+
+#elif defined (__APPLE__)
+
+       system->add("OSX Core MIDI");
+
+#endif
+
+       switch (conf::midiSystem) {
+               case RtMidi::LINUX_ALSA:  system->showItem("ALSA"); break;
+               case RtMidi::UNIX_JACK:   system->showItem("Jack"); break;
+               case RtMidi::WINDOWS_MM:  system->showItem("Multimedia MIDI"); break;
+               case RtMidi::MACOSX_CORE: system->showItem("OSX Core MIDI"); break;
+               default: system->value(0); break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((geTabMidi*)p)->__cb_changeSystem(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::__cb_changeSystem()
+{
+       /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
+        * If it returns to the original system, we re-fill the list by
+        * querying kernelMidi. */
+
+       if (systemInitValue == system->value()) {
+               portOut->clear();
+               fetchOutPorts();
+               portOut->activate();
+               portIn->clear();
+               fetchInPorts();
+               portIn->activate();
+               noNoteOff->activate();
+               sync->activate();
+       }
+       else {
+               portOut->deactivate();
+               portOut->clear();
+               portOut->add("-- restart to fetch device(s) --");
+               portOut->value(0);
+               portIn->deactivate();
+               portIn->clear();
+               portIn->add("-- restart to fetch device(s) --");
+               portIn->value(0);
+               noNoteOff->deactivate();
+               sync->deactivate();
+       }
+
+}
diff --git a/src/gui/elems/config/tabMidi.h b/src/gui/elems/config/tabMidi.h
new file mode 100644 (file)
index 0000000..e2249d8
--- /dev/null
@@ -0,0 +1,68 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_TAB_MIDI_H
+#define GE_TAB_MIDI_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+
+
+class geTabMidi : public Fl_Group
+{
+private:
+
+       void fetchSystems();
+       void fetchOutPorts();
+       void fetchInPorts();
+       void fetchMidiMaps();
+
+       static void cb_changeSystem  (Fl_Widget *w, void *p);
+       inline void __cb_changeSystem();
+
+       int systemInitValue;
+
+public:
+
+       geChoice *system;
+       geChoice *portOut;
+       geChoice *portIn;
+       geCheck  *noNoteOff;
+       geChoice *midiMap;
+       geChoice *sync;
+
+       geTabMidi(int x, int y, int w, int h);
+
+       void save();
+};
+
+
+#endif
diff --git a/src/gui/elems/config/tabMisc.cpp b/src/gui/elems/config/tabMisc.cpp
new file mode 100644 (file)
index 0000000..2be79e8
--- /dev/null
@@ -0,0 +1,80 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../basics/choice.h"
+#include "tabMisc.h"
+
+
+using namespace giada::m;
+
+
+geTabMisc::geTabMisc(int X, int Y, int W, int H)
+       : Fl_Group(X, Y, W, H, "Misc")
+{
+       begin();
+       debugMsg = new geChoice(x()+92,  y()+9, 253, 20, "Debug messages");
+       end();
+
+       debugMsg->add("(disabled)");
+       debugMsg->add("To standard output");
+       debugMsg->add("To file");
+
+       labelsize(GUI_FONT_SIZE_BASE);
+
+       switch (conf::logMode) {
+               case LOG_MODE_MUTE:
+                       debugMsg->value(0);
+                       break;
+               case LOG_MODE_STDOUT:
+                       debugMsg->value(1);
+                       break;
+               case LOG_MODE_FILE:
+                       debugMsg->value(2);
+                       break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMisc::save()
+{
+       switch(debugMsg->value()) {
+               case 0:
+                       conf::logMode = LOG_MODE_MUTE;
+                       break;
+               case 1:
+                       conf::logMode = LOG_MODE_STDOUT;
+                       break;
+               case 2:
+                       conf::logMode = LOG_MODE_FILE;
+                       break;
+       }
+}
diff --git a/src/gui/elems/config/tabMisc.h b/src/gui/elems/config/tabMisc.h
new file mode 100644 (file)
index 0000000..63bb8b0
--- /dev/null
@@ -0,0 +1,49 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_TAB_MISC_H
+#define GE_TAB_MISC_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+
+
+class geTabMisc : public Fl_Group
+{
+public:
+
+       geChoice *debugMsg;
+
+       geTabMisc(int x, int y, int w, int h);
+
+       void save();
+};
+
+#endif
diff --git a/src/gui/elems/config/tabPlugins.cpp b/src/gui/elems/config/tabPlugins.cpp
new file mode 100644 (file)
index 0000000..b992ebd
--- /dev/null
@@ -0,0 +1,121 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <FL/Fl.H>
+#include "../../core/const.h"
+#include "../../core/conf.h"
+#include "../../core/pluginHost.h"
+#include "../../utils/string.h"
+#include "../../utils/fs.h"
+#include "../basics/box.h"
+#include "../basics/radio.h"
+#include "../basics/check.h"
+#include "../basics/input.h"
+#include "../basics/button.h"
+#include "tabPlugins.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
+       : Fl_Group(X, Y, W, H, "Plugins")
+{
+       folderPath = new geInput(x()+w()-250, y()+8, 250, 20);
+       scanButton = new geButton(x()+w()-120, folderPath->y()+folderPath->h()+8, 120, 20);
+       info       = new geBox(x(), scanButton->y()+scanButton->h()+8, w(), 242);
+
+       end();
+
+       labelsize(GUI_FONT_SIZE_BASE);
+
+       info->label("Scan in progress. Please wait...");
+       info->hide();
+
+       folderPath->value(conf::pluginPath.c_str());
+       folderPath->label("Plugins folder");
+
+       scanButton->callback(cb_scan, (void*) this);
+
+       updateCount();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::updateCount()
+{
+       string scanLabel = "Scan (" + gu_itoa(pluginHost::countAvailablePlugins()) + " found)";
+       scanButton->label(scanLabel.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((geTabPlugins*)p)->__cb_scan(w); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::cb_onScan(float progress, void *p)
+{
+       string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
+       ((geTabPlugins *)p)->info->label(l.c_str());
+       Fl::wait();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::__cb_scan(Fl_Widget *w)
+{
+       info->show();
+       pluginHost::scanDir(folderPath->value(), cb_onScan, (void*) this);
+       pluginHost::saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
+       info->hide();
+       updateCount();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::save()
+{
+       conf::pluginPath = folderPath->value();
+}
+
+
+#endif // WITH_VST
diff --git a/src/gui/elems/config/tabPlugins.h b/src/gui/elems/config/tabPlugins.h
new file mode 100644 (file)
index 0000000..3bdddc3
--- /dev/null
@@ -0,0 +1,68 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_TAB_PLUGINS_H
+#define GE_TAB_PLUGINS_H
+
+
+#ifdef WITH_VST
+
+
+#include <FL/Fl_Group.H>
+
+
+class geInput;
+class geButton;
+class geBox;
+
+
+class geTabPlugins : public Fl_Group
+{
+private:
+
+       static void cb_scan  (Fl_Widget *w, void *p);
+       static void cb_onScan(float progress, void *p);
+       inline void __cb_scan(Fl_Widget *w);
+
+       void updateCount();
+
+public:
+
+       geInput   *folderPath;
+       geButton *scanButton;
+       geBox     *info;
+
+       geTabPlugins(int x, int y, int w, int h);
+
+       void save();
+};
+
+
+#endif
+
+
+#endif
diff --git a/src/gui/elems/envelopeEditor.cpp b/src/gui/elems/envelopeEditor.cpp
deleted file mode 100644 (file)
index 796a3fe..0000000
+++ /dev/null
@@ -1,413 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_envelopeWidget
- *
- * Parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <FL/fl_draw.H>
-#include "../../core/channel.h"
-#include "../../core/recorder.h"
-#include "../../core/mixer.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "envelopeEditor.h"
-
-
-extern Mixer         G_Mixer;
-extern Recorder      G_Recorder;
-extern gdMainWindow *G_MainWin;
-
-
-geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
-       int type, int range, const char *l)
-       :       geBaseActionEditor(x, y, 200, 80, pParent),
-               l                 (l),
-               type              (type),
-               range             (range),
-               selectedPoint     (-1),
-               draggedPoint      (-1)
-{
-       size(pParent->totalWidth, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-geEnvelopeEditor::~geEnvelopeEditor() {
-       clearPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
-       point p;
-       p.frame  = frame;
-       p.iValue = iValue;
-       p.fValue = fValue;
-       p.x = px;
-       p.y = py;
-       points.push_back(p);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::updateActions() {
-       for (unsigned i=0; i<points.size(); i++)
-               points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::draw() {
-
-       baseDraw();
-
-       /* print label */
-
-       fl_color(COLOR_BG_1);
-       fl_font(FL_HELVETICA, 12);
-       fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
-
-       int pxOld = x()-3;
-       int pyOld = y()+1;
-       int pxNew = 0;
-       int pyNew = 0;
-
-       fl_color(COLOR_BG_2);
-
-       for (unsigned i=0; i<points.size(); i++) {
-
-               pxNew = points.at(i).x+x()-3;
-               pyNew = points.at(i).y+y();
-
-               if (selectedPoint == (int) i) {
-                       fl_color(COLOR_BD_1);
-                       fl_rectf(pxNew, pyNew, 7, 7);
-                       fl_color(COLOR_BG_2);
-               }
-               else
-                       fl_rectf(pxNew, pyNew, 7, 7);
-
-               if (i > 0)
-                       fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
-
-               pxOld = pxNew;
-               pyOld = pyNew;
-       }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::handle(int e) {
-
-       /* Adding an action: no further checks required, just record it on frame
-        * mx*pParent->zoom. Deleting action is trickier: find the active
-        * point and derive from it the corresponding frame. */
-
-       int ret = 0;
-       int mx  = Fl::event_x()-x();  // mouse x
-       int my  = Fl::event_y()-y();  // mouse y
-
-       switch (e) {
-
-               case FL_ENTER: {
-                       ret = 1;
-                       break;
-               }
-
-               case FL_MOVE: {
-                       selectedPoint = getSelectedPoint();
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_LEAVE: {
-                       draggedPoint  = -1;
-                       selectedPoint = -1;
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_PUSH: {
-
-                       /* left click on point: drag
-                        * right click on point: delete
-                        * left click on void: add */
-
-                       if (Fl::event_button1()) {
-
-                               if (selectedPoint != -1) {
-                                       draggedPoint = selectedPoint;
-                               }
-                               else {
-
-                                       /* top & border fix */
-
-                                       if (my > h()-8) my = h()-8;
-                                       if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
-
-                                       if (range == RANGE_FLOAT) {
-
-                                               /* if this is the first point ever, add other two points at the beginning
-                                                * and the end of the range */
-
-                                               if (points.size() == 0) {
-                                                       addPoint(0, 0, 1.0f, 0, 1);
-                                                       G_Recorder.rec(pParent->chan->index, type, 0, 0, 1.0f);
-                                                       addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1);
-                                                       G_Recorder.rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f);
-              pParent->chan->hasActions = true;
-                                               }
-
-                                               /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
-
-                                               int frame   = mx * pParent->zoom;
-                                               float value = (my - h() + 8) / (float) (1 - h() + 8);
-                                               addPoint(frame, 0, value, mx, my);
-                                               G_Recorder.rec(pParent->chan->index, type, frame, 0, value);
-            pParent->chan->hasActions = true;
-                                               G_Recorder.sortActions();
-                                               sortPoints();
-                                       }
-                                       else {
-                                               /// TODO
-                                       }
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
-                                       redraw();
-                               }
-                       }
-                       else {
-
-                               /* right click on point 0 or point size-1 deletes the entire envelope */
-
-                               if (selectedPoint != -1) {
-                                       if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
-                                               G_Recorder.clearAction(pParent->chan->index, type);
-            pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-                                               points.clear();
-                                       }
-                                       else {
-                                               G_Recorder.deleteAction(pParent->chan->index,
-              points.at(selectedPoint).frame, type, false, &G_Mixer.mutex_recs);
-            pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-            G_Recorder.sortActions();
-                                               points.erase(points.begin() + selectedPoint);
-                                       }
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
-                                       redraw();
-                               }
-                       }
-
-                       ret = 1;
-                       break;
-               }
-
-               case FL_RELEASE: {
-                       if (draggedPoint != -1) {
-
-                               if (points.at(draggedPoint).x == previousXPoint) {
-                                       //gu_log("nothing to do\n");
-                               }
-                               else {
-                                       int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
-                                       /* x edge correction */
-
-                                       if (newFrame < 0)
-                                               newFrame = 0;
-                                       else if (newFrame > G_Mixer.totalFrames)
-                                               newFrame = G_Mixer.totalFrames;
-
-                                       /* vertical line check */
-
-                                       int vp = verticalPoint(points.at(draggedPoint));
-                                       if (vp == 1)                     newFrame -= 256;
-                                       else if (vp == -1) newFrame += 256;
-
-                                       /*  delete previous point and record a new one */
-
-                                       G_Recorder.deleteAction(pParent->chan->index,
-            points.at(draggedPoint).frame, type, false, &G_Mixer.mutex_recs);
-          pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
-                                       if (range == RANGE_FLOAT) {
-                                               float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
-                                               G_Recorder.rec(pParent->chan->index, type, newFrame, 0, value);
-            pParent->chan->hasActions = true;
-                                       }
-                                       else {
-                                               /// TODO
-                                       }
-
-                                       G_Recorder.sortActions();
-                                       points.at(draggedPoint).frame = newFrame;
-                                       draggedPoint  = -1;
-                                       selectedPoint = -1;
-                               }
-                       }
-                       ret = 1;
-                       break;
-               }
-
-               case FL_DRAG: {
-
-                       if (draggedPoint != -1) {
-
-                               /* y constraint */
-
-                               if (my > h()-8)
-                                       points.at(draggedPoint).y = h()-8;
-                               else
-                               if (my < 1)
-                                       points.at(draggedPoint).y = 1;
-                               else
-                                       points.at(draggedPoint).y = my;
-
-                               /* x constraint
-                                * constrain the point between two ends (leftBorder-point, point-point,
-                                * point-rightBorder). First & last points cannot be shifted on x */
-
-                               if (draggedPoint == 0)
-                                       points.at(draggedPoint).x = x()-8;
-                               else
-                               if ((unsigned) draggedPoint == points.size()-1)
-                                       points.at(draggedPoint).x = pParent->coverX;
-                               else {
-                                       int prevPoint = points.at(draggedPoint-1).x;
-                                       int nextPoint = points.at(draggedPoint+1).x;
-                                       if (mx <= prevPoint)
-                                               points.at(draggedPoint).x = prevPoint;
-                                       else
-                                       if (mx >= nextPoint)
-                                               points.at(draggedPoint).x = nextPoint;
-                                       //else
-                                       //      points.at(draggedPoint).x = mx;
-                                       else {
-                                               if (pParent->gridTool->isOn())
-                                                       points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
-                                               else
-                                                       points.at(draggedPoint).x = mx;
-                                       }
-                               }
-                               redraw();
-                       }
-
-                       ret = 1;
-                       break;
-               }
-       }
-
-       return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::verticalPoint(const point &p) {
-       for (unsigned i=0; i<points.size(); i++) {
-               if (&p == &points.at(i)) {
-                       if (i == 0 || i == points.size()-1)  // first or last point
-                               return 0;
-                       else {
-                               if (points.at(i-1).x == p.x)    // vertical with point[i-1]
-                                       return -1;
-                               else
-                               if (points.at(i+1).x == p.x)    // vertical with point[i+1]
-                                       return 1;
-                       }
-                       break;
-               }
-       }
-       return 0;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::sortPoints() {
-       for (unsigned i=0; i<points.size(); i++)
-               for (unsigned j=0; j<points.size(); j++)
-                       if (points.at(j).x > points.at(i).x)
-                               std::swap(points.at(j), points.at(i));
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::getSelectedPoint() {
-
-       /* point is a 7x7 dot */
-
-       for (unsigned i=0; i<points.size(); i++) {
-               if (Fl::event_x() >= points.at(i).x+x()-4  &&
-                               Fl::event_x() <= points.at(i).x+x()+4  &&
-                               Fl::event_y() >= points.at(i).y+y()    &&
-                               Fl::event_y() <= points.at(i).y+y()+7)
-               return i;
-       }
-       return -1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::fill() {
-       points.clear();
-       for (unsigned i=0; i<G_Recorder.global.size(); i++)
-               for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-                       Recorder::action *a = G_Recorder.global.at(i).at(j);
-                       if (a->type == type && a->chan == pParent->chan->index) {
-                               if (range == RANGE_FLOAT)
-                                       addPoint(
-                                               a->frame,                      // frame
-                                               0,                             // int value (unused)
-                                               a->fValue,                     // float value
-                                               a->frame / pParent->zoom,       // x
-                                               ((1-h()+8)*a->fValue)+h()-8);  // y = (b-a)x + a (line between two points)
-                               // else: TODO
-                       }
-               }
-
-}
diff --git a/src/gui/elems/envelopeEditor.h b/src/gui/elems/envelopeEditor.h
deleted file mode 100644 (file)
index f7dade4..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_envelopeWidget
- *
- * parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 __GE_ENVELOPECHANNEL_H__
-#define __GE_ENVELOPECHANNEL_H__
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include "../../utils/fs.h"
-#include "baseActionEditor.h"
-
-
-using std::vector;
-
-
-class geEnvelopeEditor : public geBaseActionEditor
-{
-       const char *l;      // internal label
-       int         type;   // type of action
-       int         range;
-
-       /* point
-        * a single dot in the graph. x = relative frame, y = relative value */
-
-       struct point
-       {
-               int   frame;
-               int   iValue;
-               float fValue;
-               int   x;
-               int   y;
-       };
-
-       /* points
-        * array of points, filled by fillPoints() */
-
-       vector<point> points;
-
-       /* selectedPoint
-        * which point we are selecting? */
-
-       int selectedPoint;
-
-       /* draggedPoint
-        * which point we are dragging? */
-
-       int draggedPoint;
-
-       /* previousXPoint
-        * x coordinate of point at time t-1. Used to check effective shifts */
-
-       int previousXPoint;
-
-       void draw();
-
-       int handle(int e);
-
-       int getSelectedPoint();
-
-       void sortPoints();
-
-       /* verticalPoint
-        * check if two points form a vertical line. In that case the frame value
-        * would be the same and recorder would go crazy, so shift by a small value
-        * of frames to create a minimal fadein/fadeout level. Return 0: no
-        * vertical points; return 1: vertical with the next one, return -1: vertical
-        * with the previous one. */
-
-       int verticalPoint(const point &p);
-
-public:
-
-       geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range, const char *l);
-       ~geEnvelopeEditor();
-
-       /* addPoint
-        * add a point made of frame+value to internal points[]. */
-
-       void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
-
-       void updateActions();
-
-       /* fill
-        * parse recorder's stack and fill the widget with points. It's up to
-        * the caller to call this method as initialization. */
-
-       void fill();
-
-       inline void clearPoints() { points.clear(); }
-};
-
-#endif
diff --git a/src/gui/elems/ge_mixed.cpp b/src/gui/elems/ge_mixed.cpp
deleted file mode 100644 (file)
index 913f023..0000000
+++ /dev/null
@@ -1,603 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_mixed
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <math.h>
-#include "../../core/const.h"
-#include "../../core/mixer.h"
-#include "../../core/graphics.h"
-#include "../../core/recorder.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../utils/gui.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "basics/boxtypes.h"
-#include "ge_mixed.h"
-
-
-extern Mixer         G_Mixer;
-extern unsigned      G_beats;
-extern bool          G_audio_status;
-extern gdMainWindow *mainWin;
-
-
-void __cb_window_closer(Fl_Widget *v, void *p)
-{
-  delete (Fl_Window*)p;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gButton::gButton(int X, int Y, int W, int H, const char *L, const char **imgOff, const char **imgOn)
-  : gClick(X, Y, W, H, L, imgOff, imgOn) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gClick::gClick(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
-: gBaseButton(x, y, w, h, L),
-  imgOff(imgOff),
-  imgOn(imgOn),
-  bgColor0(COLOR_BG_0),
-  bgColor1(COLOR_BG_1),
-  bdColor(COLOR_BD_0),
-  txtColor(COLOR_TEXT_0)  {}
-
-void gClick::draw()
-{
-  if (!active()) txtColor = bdColor;
-  else           txtColor = COLOR_TEXT_0;
-
-  fl_rect(x(), y(), w(), h(), bdColor);             // borders
-  if (value()) {                                    // -- clicked
-    if (imgOn != NULL)
-      fl_draw_pixmap(imgOn, x()+1, y()+1);
-    else
-      fl_rectf(x(), y(), w(), h(), bgColor1);       // covers the border
-  }
-  else {                                            // -- not clicked
-    fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border
-    if (imgOff != NULL)
-      fl_draw_pixmap(imgOff, x()+1, y()+1);
-  }
-  if (!active())
-    fl_color(FL_INACTIVE_COLOR);
-
-  fl_color(txtColor);
-  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-  fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gClickRepeat::gClickRepeat(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
-: Fl_Repeat_Button(x, y, w, h, L), imgOff(imgOff), imgOn(imgOn) {}
-
-void gClickRepeat::draw()
-{
-  if (value()) {                               // -- clicked
-    fl_rectf(x(), y(), w(), h(), COLOR_BG_1);  // bg
-    if (imgOn != NULL)
-      fl_draw_pixmap(imgOn, x()+1, y()+1);
-  }
-  else {                                       // -- not clicked
-    fl_rectf(x(), y(), w(), h(), COLOR_BG_0);  // bg
-    fl_rect(x(), y(), w(), h(), COLOR_BD_0);   // border
-    if (imgOff != NULL)
-      fl_draw_pixmap(imgOff, x()+1, y()+1);
-  }
-  if (!active())
-    fl_color(FL_INACTIVE_COLOR);
-
-  fl_color(COLOR_TEXT_0);
-  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-  fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gInput::gInput(int x, int y, int w, int h, const char *L)
-: Fl_Input(x, y, w, h, L)
-{
-  //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
-  box(G_CUSTOM_BORDER_BOX);
-  labelsize(GUI_FONT_SIZE_BASE);
-  labelcolor(COLOR_TEXT_0);
-  color(COLOR_BG_DARK);
-  textcolor(COLOR_TEXT_0);
-  cursor_color(COLOR_TEXT_0);
-  selection_color(COLOR_BD_0);
-  textsize(GUI_FONT_SIZE_BASE);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gDial::gDial(int x, int y, int w, int h, const char *L)
-: Fl_Dial(x, y, w, h, L)
-{
-  labelsize(GUI_FONT_SIZE_BASE);
-  labelcolor(COLOR_TEXT_0);
-  align(FL_ALIGN_LEFT);
-  type(FL_FILL_DIAL);
-  angles(0, 360);
-  color(COLOR_BG_0);            // background
-  selection_color(COLOR_BG_1);   // selection
-}
-
-void gDial::draw()
-{
-  double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
-
-  fl_color(COLOR_BG_0);
-  fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
-
-  fl_color(COLOR_BD_0);
-  fl_arc(x(), y(), w(), h(), 0, 360);
-  fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-gBox::gBox(int x, int y, int w, int h, const char *L, Fl_Align al)
-: Fl_Box(x, y, w, h)
-{
-  copy_label(L);
-  labelsize(GUI_FONT_SIZE_BASE);
-  box(FL_NO_BOX);
-  labelcolor(COLOR_TEXT_0);
-  if (al != 0)
-    align(al | FL_ALIGN_INSIDE);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gCheck::gCheck(int x, int y, int w, int h, const char *L)
-: Fl_Check_Button(x, y, w, h, L) {}
-
-void gCheck::draw()
-{
-  int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
-
-  if (value()) {
-    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
-    fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
-  }
-  else {
-    fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
-    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
-  }
-
-  fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
-  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-  fl_color(!active() ? FL_INACTIVE_COLOR : COLOR_TEXT_0);
-  fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gRadio::gRadio(int x, int y, int w, int h, const char *L)
-: Fl_Radio_Button(x, y, w, h, L) {}
-
-void gRadio::draw()
-{
-  int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
-
-  if (value()) {
-    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
-    fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
-  }
-  else {
-    fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
-    fl_rect(x(), y(), 12, 12, (Fl_Color) color);
-  }
-
-  fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
-  fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-  fl_color(COLOR_TEXT_0);
-  fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gProgress::gProgress(int x, int y, int w, int h, const char *L)
-: Fl_Progress(x, y, w, h, L) {
-  color(COLOR_BG_0, COLOR_BD_0);
-  box(G_CUSTOM_BORDER_BOX);
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gSoundMeter::gSoundMeter(int x, int y, int w, int h, const char *L)
-  : Fl_Box(x, y, w, h, L),
-    clip(false),
-    mixerPeak(0.0f),
-    peak(0.0f),
-    db_level(0.0f),
-    db_level_old(0.0f) {}
-
-void gSoundMeter::draw()
-{
-  fl_rect(x(), y(), w(), h(), COLOR_BD_0);
-
-  /* peak = the highest value inside the frame */
-
-  peak = 0.0f;
-  float tmp_peak = 0.0f;
-
-  tmp_peak = fabs(mixerPeak);
-  if (tmp_peak > peak)
-    peak = tmp_peak;
-
-  clip = peak >= 1.0f ? true : false; // 1.0f is considered clip
-
-
-  /*  dBFS (full scale) calculation, plus decay of -2dB per frame */
-
-  db_level = 20 * log10(peak);
-  if (db_level < db_level_old)
-    if (db_level_old > -DB_MIN_SCALE)
-      db_level = db_level_old - 2.0f;
-
-  db_level_old = db_level;
-
-  /* graphical part */
-
-  float px_level = 0.0f;
-  if (db_level < 0.0f)
-    px_level = ((w()/DB_MIN_SCALE) * db_level) + w();
-  else
-    px_level = w();
-
-  fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);
-  fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !G_audio_status ? COLOR_ALERT : COLOR_BD_0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gChoice::gChoice(int x, int y, int w, int h, const char *l, bool ang)
-  : Fl_Choice(x, y, w, h, l), angle(ang)
-{
-  labelsize(GUI_FONT_SIZE_BASE);
-  labelcolor(COLOR_TEXT_0);
-  box(FL_BORDER_BOX);
-  textsize(GUI_FONT_SIZE_BASE);
-  textcolor(COLOR_TEXT_0);
-  color(COLOR_BG_0);
-}
-
-
-void gChoice::draw()
-{
-  fl_rectf(x(), y(), w(), h(), COLOR_BG_0);              // bg
-  fl_rect(x(), y(), w(), h(), (Fl_Color) COLOR_BD_0);    // border
-  if (angle)
-    fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
-
-  /* pick up the text() from the selected item (value()) and print it in
-   * the box and avoid overflows */
-
-  fl_color(!active() ? COLOR_BD_0 : COLOR_TEXT_0);
-  if (value() != -1) {
-    if (fl_width(text(value())) < w()-8) {
-      fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
-    }
-    else {
-      std::string tmp = text(value());
-      int size        = tmp.size();
-      while (fl_width(tmp.c_str()) >= w()-16) {
-        tmp.resize(size);
-        size--;
-      }
-      tmp += "...";
-      fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
-    }
-
-  }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gLiquidScroll::gLiquidScroll(int x, int y, int w, int h, const char *l)
-  : Fl_Scroll(x, y, w, h, l)
-{
-  type(Fl_Scroll::VERTICAL);
-  scrollbar.color(COLOR_BG_0);
-  scrollbar.selection_color(COLOR_BG_1);
-  scrollbar.labelcolor(COLOR_BD_1);
-  scrollbar.slider(G_CUSTOM_BORDER_BOX);
-}
-
-
-void gLiquidScroll::resize(int X, int Y, int W, int H)
-{
-  int nc = children()-2;                // skip hscrollbar and vscrollbar
-  for ( int t=0; t<nc; t++) {           // tell children to resize to our new width
-    Fl_Widget *c = child(t);
-    c->resize(c->x(), c->y(), W-24, c->h());    // W-24: leave room for scrollbar
-  }
-  init_sizes();   // tell scroll children changed in size
-  Fl_Scroll::resize(X,Y,W,H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gSlider::gSlider(int x, int y, int w, int h, const char *l)
-  : Fl_Slider(x, y, w, h, l)
-{
-  type(FL_HOR_FILL_SLIDER);
-
-  labelsize(GUI_FONT_SIZE_BASE);
-  align(FL_ALIGN_LEFT);
-  labelcolor(COLOR_TEXT_0);
-
-  box(G_CUSTOM_BORDER_BOX);
-  color(COLOR_BG_0);
-  selection_color(COLOR_BD_0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical)
-  : Fl_Box(X,Y,W,H), vertical(vertical)
-{
-  last_y = 0;
-  min_h  = 30;
-  if (vertical) {
-    orig_h = H;
-    labelsize(H);
-  }
-  else {
-    orig_h = W;
-    labelsize(W);
-  }
-  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
-  labelfont(FL_COURIER);
-  visible_focus(0);
-}
-
-/*
-gResizerBar::~gResizerBar()
-{
-  gu_log("------ resizerbar %p destroyed\n", (void*)this);
-}
-*/
-
-void gResizerBar::HandleDrag(int diff)
-{
-  Fl_Scroll *grp = (Fl_Scroll*)parent();
-  int top;
-  int bot;
-  if (vertical) {
-    top = y();
-    bot = y()+h();
-  }
-  else {
-    top = x();
-    bot = x()+w();
-  }
-
-  // First pass: find widget directly above us with common edge
-  //    Possibly clamp 'diff' if widget would get too small..
-
-  for (int t=0; t<grp->children(); t++) {
-    Fl_Widget *wd = grp->child(t);
-    if (vertical) {
-      if ((wd->y()+wd->h()) == top) {                           // found widget directly above?
-        if ((wd->h()+diff) < min_h)
-          diff = wd->h() - min_h;                              // clamp
-        wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff);       // change height
-        break;                                                // done with first pass
-      }
-    }
-    else {
-      if ((wd->x()+wd->w()) == top) {                           // found widget directly above?
-        if ((wd->w()+diff) < min_h)
-          diff = wd->w() - min_h;                              // clamp
-        wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h());       // change height
-        break;                                                // done with first pass
-      }
-    }
-  }
-
-  // Second pass: find widgets below us, move based on clamped diff
-
-  for (int t=0; t<grp->children(); t++) {
-    Fl_Widget *wd = grp->child(t);
-    if (vertical) {
-      if (wd->y() >= bot)                                     // found widget below us?
-        wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h());      // change position
-    }
-    else {
-      if (wd->x() >= bot)
-        wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h());
-    }
-  }
-
-  // Change our position last
-
-  if (vertical)
-    resize(x(), y()+diff, w(), h());
-  else
-    resize(x()+diff, y(), w(), h());
-
-  grp->init_sizes();
-  grp->redraw();
-}
-
-
-int gResizerBar::handle(int e)
-{
-  int ret = 0;
-  int this_y;
-  if (vertical)
-    this_y = Fl::event_y_root();
-  else
-    this_y = Fl::event_x_root();
-  switch (e) {
-    case FL_FOCUS:
-      ret = 1;
-      break;
-    case FL_ENTER:
-      ret = 1;
-      fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE);
-      break;
-    case FL_LEAVE:
-      ret = 1;
-      fl_cursor(FL_CURSOR_DEFAULT);
-      break;
-    case FL_PUSH:
-      ret = 1;
-      last_y = this_y;
-      break;
-    case FL_DRAG:
-      HandleDrag(this_y-last_y);
-      last_y = this_y;
-      ret = 1;
-      break;
-    default: break;
-  }
-  return(Fl_Box::handle(e) | ret);
-}
-
-
-void gResizerBar::resize(int X,int Y,int W,int H)
-{
-  if (vertical)
-    Fl_Box::resize(X,Y,W,orig_h);                                // height of resizer stays constant size
-  else
-    Fl_Box::resize(X,Y,orig_h,H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gBaseButton::gBaseButton(int x, int y, int w, int h, const char *l)
-  : Fl_Button(x, y, w, h, l)
-{
-  initLabel = l ? l : "";
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::trimLabel()
-{
-  if (initLabel.empty())
-    return;
-
-  std::string out;
-  if (w() > 20) {
-    out = initLabel;
-    int len = initLabel.size();
-    while (fl_width(out.c_str(), out.size()) > w()) {
-      out = initLabel.substr(0, len) + "...";
-      len--;
-    }
-  }
-  else {
-    out = "";
-  }
-  copy_label(out.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::label(const char *l)
-{
-  Fl_Button::label(l);
-  initLabel = l;
-  trimLabel();
-}
-
-const char *gBaseButton::label()
-{
-  return Fl_Button::label();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::resize(int X, int Y, int W, int H)
-{
-  trimLabel();
-  Fl_Button::resize(X, Y, W, H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gFxButton::gFxButton(int x, int y, int w, int h, const char **imgOff, const char **imgOn)
-  : gClick(x, y, w, h, NULL, imgOff, imgOn), full(false) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gFxButton::draw()
-{
-  gClick::draw();
-  if (full)
-    fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0);
-}
diff --git a/src/gui/elems/ge_mixed.h b/src/gui/elems/ge_mixed.h
deleted file mode 100644 (file)
index 4b1090e..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_mixed
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_MIXED_H
-#define GE_MIXED_H
-
-#include <stdio.h>
-#include <dirent.h>
-#include <stdint.h>  // for intptr_t
-#include <string>
-#include <FL/Fl.H>
-#include <FL/Fl_Menu_Window.H>
-#include <FL/Fl_Button.H>
-#include <FL/Fl_Repeat_Button.H>
-#include <FL/Fl_Check_Button.H>
-#include <FL/Fl_Box.H>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Dial.H>
-#include <FL/Fl_Pixmap.H>
-#include <FL/Fl_Menu_Button.H>
-#include <FL/Fl_Hold_Browser.H>
-#include <FL/Fl_Radio_Button.H>
-#include <FL/Fl_Progress.H>
-#include <FL/Fl_Input.H>
-#include <FL/Fl_Int_Input.H>
-#include <FL/Fl_Choice.H>
-#include <FL/Fl_Scroll.H>
-
-#ifdef _WIN32
-       #include <shlobj.h>  // for SHGetFolderPath
-#endif
-
-
-/* cb_window_closer
- * callback for when closing windows. Deletes the widget (delete). */
-
-void __cb_window_closer(Fl_Widget *v, void *p);
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBaseButton : public Fl_Button
-{
-private:
-       std::string initLabel;
-
-       void trimLabel();
-
-public:
-  gBaseButton(int x, int y, int w, int h, const char *l=0);
-  void resize(int x, int y, int w, int h);
-       void label(const char *l);
-       const char *label();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gClick
- * a normal button. */
-
-class gClick : public gBaseButton
-{
-public:
-       gClick(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
-       void draw();
-       const char **imgOff;
-       const char **imgOn;
-       Fl_Color bgColor0;   // background not clicked
-       Fl_Color bgColor1;   // background clicked
-       Fl_Color bdColor;    // border
-       Fl_Color txtColor;       // testo
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gClickRepeat : public Fl_Repeat_Button
-{
-public:
-       gClickRepeat(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
-       void draw();
-       const char **imgOff;
-       const char **imgOn;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gButton
- * exactly as gClick but with a unique id inside of it. Used for the buttons in
- * channels and for FXs. */
- /* TODO - is this really useful? */
-
-class gButton : public gClick
-{
-public:
-       gButton(int X,int Y,int W,int H,const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
-       int key;
-       int id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gInput : public Fl_Input
-{
-public:
-       gInput(int x, int y, int w, int h, const char *L=0);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gDial : public Fl_Dial
-{
-public:
-       gDial(int x, int y, int w, int h, const char *L=0);
-       void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBox : public Fl_Box
-{
-public:
-       gBox(int x, int y, int w, int h, const char *L=0, Fl_Align al=FL_ALIGN_CENTER);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gCheck : public Fl_Check_Button
-{
-public:
-       gCheck(int x, int y, int w, int h, const char *L=0);
-       void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gRadio : public Fl_Radio_Button
-{
-public:
-       gRadio(int x, int y, int w, int h, const char *L=0);
-       void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gProgress : public Fl_Progress
-{
-public:
-       gProgress(int x, int y, int w, int h, const char *L=0);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gSoundMeter : public Fl_Box
-{
-public:
-       gSoundMeter(int X,int Y,int W,int H,const char *L=0);
-       void draw();
-       bool clip;
-       float mixerPeak;        // peak from mixer
-private:
-       float peak;
-       float db_level;
-       float db_level_old;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBeatMeter : public Fl_Box
-{
-public:
-       gBeatMeter(int X,int Y,int W,int H,const char *L=0);
-       void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gChoice : public Fl_Choice
-{
-public:
-
-       gChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
-       void draw();
-
-       inline void showItem(const char *c) {value(find_index(c)); }
-
-       bool angle;
-       int  id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gLiquidScroll
- * custom scroll that tells children to follow scroll's width when
- * resized. Thanks to Greg Ercolano from FLTK dev team.
- * http://seriss.com/people/erco/fltk/ */
-
-class gLiquidScroll : public Fl_Scroll
-{
-public:
-       gLiquidScroll(int x, int y, int w, int h, const char *l=0);
-       void resize(int x, int y, int w, int h);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-/* gResizerBar
- * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
- * FLTK dev team. http://seriss.com/people/erco/fltk/
- *
- * Shows a resize cursor when hovered over.
- * Assumes:
- *     - Parent is an Fl_Scroll
- *     - All children of Fl_Scroll are vertically arranged
- *     - The widget above us has a bottom edge touching our top edge
- *       ie. (w->y()+w->h() == this->y())
- *
- * When this widget is dragged:
- *     - The widget above us (with a common edge) will be /resized/
- *       vertically
- *     - All children below us will be /moved/ vertically */
-
-/* TODO - use more general variable names
- * (last_y -> last_?, min_h -> min_?, ...) */
-
-class gResizerBar : public Fl_Box
-{
-private:
-  bool vertical;
-       int  orig_h;
-       int  last_y;
-       int  min_h;   // min height for widget above us
-
-       void HandleDrag(int diff);
-
-public:
-
-  /* 'vertical' defines the bar movement. Vertical=true: the bar moves
-   * vertically (up and down). */
-
-       gResizerBar(int x, int y, int w, int h, bool vertical=true);
-       //~gResizerBar();
-
-  inline void setMinSize(int val) { min_h = val; }
-  inline int  getMinSize()        { return min_h; }
-
-  int  handle(int e);
-  void resize(int x, int y, int w, int h);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gSlider : public Fl_Slider
-{
-public:
-       gSlider(int x, int y, int w, int h, const char *l=0);
-       int id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gFxButton
- * a simple gClick with 'full' parameter (i.e. has plugins). If 'full' is true,
- * draw something somewhere. */
-
-class gFxButton : public gClick
-{
-public:
-       gFxButton(int x, int y, int w, int h, const char **imgOff=NULL, const char **imgOn=NULL);
-       void draw();
-       bool full;
-};
-
-
-#endif
diff --git a/src/gui/elems/ge_pluginBrowser.cpp b/src/gui/elems/ge_pluginBrowser.cpp
deleted file mode 100644 (file)
index 2754fea..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pluginBrowser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#include "../../core/plugin.h"
-#include "../../core/const.h"
-#include "../../core/pluginHost.h"
-#include "ge_mixed.h"
-#include "basics/boxtypes.h"
-#include "ge_pluginBrowser.h"
-
-
-extern PluginHost G_PluginHost;
-
-
-using std::vector;
-
-
-gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
-       : Fl_Browser(x, y, w, h)
-{
-       box(G_CUSTOM_BORDER_BOX);
-       textsize(GUI_FONT_SIZE_BASE);
-       textcolor(COLOR_TEXT_0);
-       selection_color(COLOR_BG_1);
-       color(COLOR_BG_0);
-
-       this->scrollbar.color(COLOR_BG_0);
-       this->scrollbar.selection_color(COLOR_BG_1);
-       this->scrollbar.labelcolor(COLOR_BD_1);
-       this->scrollbar.slider(G_CUSTOM_BORDER_BOX);
-
-       this->hscrollbar.color(COLOR_BG_0);
-       this->hscrollbar.selection_color(COLOR_BG_1);
-       this->hscrollbar.labelcolor(COLOR_BD_1);
-       this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
-
-       type(FL_HOLD_BROWSER);
-
-       computeWidths();
-
-  column_widths(widths);
-  column_char('\t');       // tabs as column delimiters
-
-       refresh();
-
-       end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePluginBrowser::refresh()
-{
-       clear();
-
-       add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
-       add("---\t---\t---\t---\t---");
-
-       for (int i=0; i<G_PluginHost.countAvailablePlugins(); i++) {
-               PluginHost::PluginInfo pi = G_PluginHost.getAvailablePluginInfo(i);
-               string m = G_PluginHost.doesPluginExist(pi.uid) ? "" : "@-";
-               string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
-                               pi.category +   "\t" + m + pi.format + "\t" + m + pi.uid;
-               add(s.c_str());
-       }
-
-       for (unsigned i=0; i<G_PluginHost.countUnknownPlugins(); i++) {
-               string s = "?\t?\t?\t?\t? " + G_PluginHost.getUnknownPluginInfo(i) + " ?";
-               add(s.c_str());
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePluginBrowser::computeWidths()
-{
-       int w0, w1, w3;
-       for (int i=0; i<G_PluginHost.countAvailablePlugins(); i++) {
-               PluginHost::PluginInfo pi = G_PluginHost.getAvailablePluginInfo(i);
-               w0 = (int) fl_width(pi.name.c_str());
-               w1 = (int) fl_width(pi.manufacturerName.c_str());
-               w3 = (int) fl_width(pi.format.c_str());
-               if (w0 > widths[0]) widths[0] = w0;
-               if (w1 > widths[1]) widths[1] = w1;
-               if (w3 > widths[3]) widths[3] = w3;
-       }
-       widths[0] += 60;
-       widths[1] += 60;
-       widths[2] = fl_width("CATEGORY") + 60;
-       widths[3] += 60;
-       widths[4] = 0;
-}
-
-
-#endif
diff --git a/src/gui/elems/ge_pluginBrowser.h b/src/gui/elems/ge_pluginBrowser.h
deleted file mode 100644 (file)
index 79378b9..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pluginBrowser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-#ifndef GE_PLUGIN_BROWSER_H
-#define GE_PLUGIN_BROWSER_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Browser.H>
-
-
-using std::vector;
-
-
-class gePluginBrowser : public Fl_Browser
-{
-private:
-
-       int widths[5] = {0};
-
-       void computeWidths();
-
-public:
-
-       gePluginBrowser(int x, int y, int w, int h);
-
-       void refresh();
-};
-
-#endif
-
-#endif
diff --git a/src/gui/elems/ge_waveTools.cpp b/src/gui/elems/ge_waveTools.cpp
deleted file mode 100644 (file)
index e2ec4b7..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gg_waveTools
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../core/graphics.h"
-#include "../../core/mixer.h"
-#include "../../core/const.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/ge_waveform.h"
-#include "basics/boxtypes.h"
-#include "ge_waveTools.h"
-
-
-gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l)
-       : Fl_Scroll(x, y, w, h, l)
-{
-       type(Fl_Scroll::HORIZONTAL_ALWAYS);
-       hscrollbar.color(COLOR_BG_0);
-       hscrollbar.selection_color(COLOR_BG_1);
-       hscrollbar.labelcolor(COLOR_BD_1);
-       hscrollbar.slider(G_CUSTOM_BORDER_BOX);
-
-       waveform = new gWaveform(x, y, w, h-24, ch);
-
-
-       //resizable(waveform);
-}
-
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveTools::updateWaveform()
-{
-       waveform->alloc(w());
-       waveform->redraw();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveTools::resize(int x, int y, int w, int h)
-{
-       if (this->w() == w || (this->w() != w && this->h() != h)) {   // vertical or both resize
-               Fl_Widget::resize(x, y, w, h);
-               waveform->resize(x, y, waveform->w(), h-24);
-               updateWaveform();
-       }
-       else {                                                        // horizontal resize
-               Fl_Widget::resize(x, y, w, h);
-       }
-
-       if (this->w() > waveform->w())
-               waveform->stretchToWindow();
-
-       int offset = waveform->x() + waveform->w() - this->w() - this->x();
-       if (offset < 0)
-               waveform->position(waveform->x()-offset, this->y());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveTools::handle(int e)
-{
-       int ret = Fl_Group::handle(e);
-       switch (e) {
-               case FL_MOUSEWHEEL: {
-                       waveform->setZoom(Fl::event_dy());
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-       }
-       return ret;
-}
diff --git a/src/gui/elems/ge_waveTools.h b/src/gui/elems/ge_waveTools.h
deleted file mode 100644 (file)
index 051dca2..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gg_waveTools
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_WAVETOOLS_H
-#define GE_WAVETOOLS_H
-
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include <FL/Fl_Scroll.H>
-
-
-class gWaveTools : public Fl_Scroll {
-public:
-       class gWaveform *waveform;
-
-       gWaveTools(int X,int Y,int W, int H, class SampleChannel *ch, const char *L=0);
-       void resize(int x, int y, int w, int h);
-       int  handle(int e);
-
-       void updateWaveform();
-};
-
-#endif
diff --git a/src/gui/elems/ge_waveform.cpp b/src/gui/elems/ge_waveform.cpp
deleted file mode 100644 (file)
index 022257f..0000000
+++ /dev/null
@@ -1,840 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_waveform
- * an element which represents a waveform.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <FL/Fl_Menu_Item.H>
-#include <FL/Fl_Menu_Button.H>
-#include <samplerate.h>
-#include "../../core/wave.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/mixer.h"
-#include "../../core/waveFx.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../glue/channel.h"
-#include "../dialogs/gd_editor.h"
-#include "ge_waveTools.h"
-#include "ge_mixed.h"
-#include "basics/boxtypes.h"
-#include "ge_waveform.h"
-
-
-extern Mixer G_Mixer;
-extern Conf  G_Conf;
-
-
-gWaveform::gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l)
-: Fl_Widget(x, y, w, h, l),
-  chan(ch),
-  menuOpen(false),
-  chanStart(0),
-  chanStartLit(false),
-  chanEnd(0),
-  chanEndLit(false),
-  ratio(0.0f),
-  selectionA(0),
-  selectionB(0),
-  selectionA_abs(0),
-  selectionB_abs(0)
-{
-  data.sup  = NULL;
-  data.inf  = NULL;
-  data.size = 0;
-
-  grid.snap  = G_Conf.sampleEditorGridOn;
-  grid.level = G_Conf.sampleEditorGridVal;
-
-  stretchToWindow();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWaveform::~gWaveform()
-{
-  freeData();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::freeData()
-{
-  if (data.sup != NULL) {
-    free(data.sup);
-    free(data.inf);
-    data.sup  = NULL;
-    data.inf  = NULL;
-    data.size = 0;
-  }
-  grid.points.clear();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::alloc(int datasize)
-{
-  ratio = chan->wave->size / (float) datasize;
-
-  if (ratio < 2)
-    return 0;
-
-  freeData();
-
-  data.size = datasize;
-  data.sup  = (int*) malloc(data.size * sizeof(int));
-  data.inf  = (int*) malloc(data.size * sizeof(int));
-
-  int offset = h() / 2;
-  int zero   = y() + offset; // center, zero amplitude (-inf dB)
-
-  /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
-   * even, as always */
-
-  int gridFreq = 0;
-  if (grid.level != 0) {
-    gridFreq = chan->wave->size / grid.level;
-    if (gridFreq % 2 != 0)
-      gridFreq--;
-  }
-
-  for (int i=0; i<data.size; i++) {
-
-    int pp;  // point prev
-    int pn;  // point next
-
-    /* resampling the waveform, hardcore way. Many thanks to
-     * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
-     * Note: we use
-     *   p = j * (m-1 / n)
-     * instead of
-     *   p = j * (m-1 / n-1)
-     * in order to obtain 'datasize' cells to parse (and not datasize-1) */
-
-    pp = i * ((chan->wave->size - 1) / (float) datasize);
-    pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
-
-    if (pp % 2 != 0) pp -= 1;
-    if (pn % 2 != 0) pn -= 1;
-
-    float peaksup = 0.0f;
-    float peakinf = 0.0f;
-
-    /* scan the original data in chunks */
-
-    int k = pp;
-    while (k < pn) {
-
-      if (chan->wave->data[k] > peaksup)
-        peaksup = chan->wave->data[k];    // FIXME - Left data only
-      else
-      if (chan->wave->data[k] <= peakinf)
-        peakinf = chan->wave->data[k];    // FIXME - Left data only
-
-      /* print grid */
-
-      if (gridFreq != 0)
-        if (k % gridFreq == 0 && k != 0)
-          grid.points.push_back(i);
-
-      k += 2;
-    }
-
-    data.sup[i] = zero - (peaksup * chan->boost * offset);
-    data.inf[i] = zero - (peakinf * chan->boost * offset);
-
-    // avoid window overflow
-
-    if (data.sup[i] < y())       data.sup[i] = y();
-    if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
-  }
-
-  recalcPoints();
-  return 1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::recalcPoints()
-{
-  selectionA = relativePoint(selectionA_abs);
-  selectionB = relativePoint(selectionB_abs);
-  chanStart  = relativePoint(chan->begin / 2);
-
-  /* fix the rounding error when chanEnd is set on the very end of the
-   * sample */
-
-  if (chan->end == chan->wave->size)
-    chanEnd = data.size - 2; // 2 px border
-  else
-    chanEnd = relativePoint(chan->end / 2);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::draw()
-{
-  /* blank canvas */
-
-  fl_rectf(x(), y(), w(), h(), COLOR_BG_0);
-
-  /* draw selection (if any) */
-
-  if (selectionA != selectionB) {
-
-    int a_x = selectionA + x() - BORDER; // - start;
-    int b_x = selectionB + x() - BORDER; //  - start;
-
-    if (a_x < 0)
-      a_x = 0;
-    if (b_x >= w()-1)
-      b_x = w()-1;
-
-    if (selectionA < selectionB)
-      fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0);
-    else
-      fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0);
-  }
-
-  /* draw waveform from x1 (offset driven by the scrollbar) to x2
-   * (width of parent window). We don't draw the entire waveform,
-   * only the visibile part. */
-
-  int offset = h() / 2;
-  int zero   = y() + offset; // sample zero (-inf dB)
-
-  int wx1 = abs(x() - ((gWaveTools*)parent())->x());
-  int wx2 = wx1 + ((gWaveTools*)parent())->w();
-  if (x()+w() < ((gWaveTools*)parent())->w())
-    wx2 = x() + w() - BORDER;
-
-  fl_color(0, 0, 0);
-  for (int i=wx1; i<wx2; i++) {
-    fl_line(i+x(), zero, i+x(), data.sup[i]);
-    fl_line(i+x(), zero, i+x(), data.inf[i]);
-
-    /* print grid */
-
-    for (unsigned k=0; k<grid.points.size(); k++) {
-      if (grid.points.at(k) == i) {
-        //gu_log("draw grid line at %d\n", i);
-        fl_color(fl_rgb_color(54, 54, 54));
-        fl_line_style(FL_DASH, 0, NULL);
-        fl_line(i+x(), y(), i+x(), y()+h());
-        fl_color(0, 0, 0);
-        fl_line_style(FL_SOLID, 0, NULL);
-        break;
-      }
-    }
-  }
-
-  /* border box */
-
-  fl_rect(x(), y(), w(), h(), COLOR_BD_0);
-
-  /* print chanStart */
-
-  int lineX = x()+chanStart+1;
-
-  if (chanStartLit) fl_color(COLOR_BD_1);
-  else              fl_color(COLOR_BD_0);
-
-  /* vertical line */
-
-  fl_line(lineX, y()+1, lineX, y()+h()-2);
-
-  /* print flag and avoid overflow */
-
-  if (lineX+FLAG_WIDTH > w()+x()-2)
-    fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
-  else  {
-    fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
-    fl_color(255, 255, 255);
-    fl_draw("s", lineX+4, y()+h()-3);
-  }
-
-  /* print chanEnd */
-
-  lineX = x()+chanEnd;
-  if (chanEndLit) fl_color(COLOR_BD_1);
-  else            fl_color(COLOR_BD_0);
-
-  fl_line(lineX, y()+1, lineX, y()+h()-2);
-
-  if (lineX-FLAG_WIDTH < x())
-    fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
-  else {
-    fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
-    fl_color(255, 255, 255);
-    fl_draw("e", lineX-10, y()+10);
-  }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::handle(int e)
-{
-  int ret = 0;
-
-  switch (e) {
-
-    case FL_PUSH: {
-
-      mouseX = Fl::event_x();
-      pushed = true;
-
-      if (!mouseOnEnd() && !mouseOnStart()) {
-
-        /* right button? show the menu. Don't set selectionA,B,etc */
-
-        if (Fl::event_button3()) {
-          openEditMenu();
-        }
-        else
-        if (mouseOnSelectionA() || mouseOnSelectionB()) {
-          resized = true;
-        }
-        else {
-          dragged = true;
-          selectionA = Fl::event_x() - x();
-
-          if (selectionA >= data.size) selectionA = data.size;
-
-          selectionB = selectionA;
-          selectionA_abs = absolutePoint(selectionA);
-          selectionB_abs = selectionA_abs;
-        }
-      }
-
-      ret = 1;
-      break;
-    }
-
-    case FL_RELEASE: {
-
-      /* don't recompute points if something is selected */
-
-      if (selectionA != selectionB) {
-        pushed  = false;
-        dragged = false;
-        ret = 1;
-        break;
-      }
-
-      int realChanStart = chan->begin;
-      int realChanEnd   = chan->end;
-
-      if (chanStartLit)
-        realChanStart = absolutePoint(chanStart)*2;
-      else
-      if (chanEndLit)
-        realChanEnd = absolutePoint(chanEnd)*2;
-
-      glue_setBeginEndChannel((gdEditor *) window(), chan, realChanStart, realChanEnd, false);
-
-      pushed  = false;
-      dragged = false;
-
-      redraw();
-      ret = 1;
-      break;
-    }
-
-    case FL_ENTER: {  // enables FL_DRAG
-      ret = 1;
-      break;
-    }
-
-    case FL_LEAVE: {
-      if (chanStartLit || chanEndLit) {
-        chanStartLit = false;
-        chanEndLit   = false;
-        redraw();
-      }
-      ret = 1;
-      break;
-    }
-
-    case FL_MOVE: {
-      mouseX = Fl::event_x();
-      mouseY = Fl::event_y();
-
-      if (mouseOnStart()) {
-        chanStartLit = true;
-        redraw();
-      }
-      else
-      if (chanStartLit) {
-        chanStartLit = false;
-        redraw();
-      }
-
-      if (mouseOnEnd()) {
-        chanEndLit = true;
-        redraw();
-      }
-      else
-      if (chanEndLit) {
-        chanEndLit = false;
-        redraw();
-      }
-
-      if (mouseOnSelectionA())
-        fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-      else
-      if (mouseOnSelectionB())
-        fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-      else
-        fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
-      ret = 1;
-      break;
-    }
-
-    case FL_DRAG: {
-
-      /* here the mouse is on the chanStart tool */
-
-      if (chanStartLit && pushed) {
-
-        chanStart = Fl::event_x() - x();
-
-        if (grid.snap)
-          chanStart = applySnap(chanStart);
-
-        if (chanStart < 0)
-          chanStart = 0;
-        else
-        if (chanStart >= chanEnd)
-          chanStart = chanEnd-2;
-
-        redraw();
-      }
-      else
-      if (chanEndLit && pushed) {
-
-        chanEnd = Fl::event_x() - x();
-
-        if (grid.snap)
-          chanEnd = applySnap(chanEnd);
-
-        if (chanEnd >= data.size - 2)
-          chanEnd = data.size - 2;
-        else
-        if (chanEnd <= chanStart)
-          chanEnd = chanStart + 2;
-
-        redraw();
-      }
-
-      /* here the mouse is on the waveform, i.e. a selection */
-
-      else
-      if (dragged) {
-
-        selectionB = Fl::event_x() - x();
-
-        if (selectionB >= data.size)
-          selectionB = data.size;
-
-        if (selectionB <= 0)
-          selectionB = 0;
-
-        if (grid.snap)
-          selectionB = applySnap(selectionB);
-
-        selectionB_abs = absolutePoint(selectionB);
-        redraw();
-      }
-
-      /* here the mouse is on a selection boundary i.e. resize */
-
-      else
-      if (resized) {
-        int pos = Fl::event_x() - x();
-        if (mouseOnSelectionA()) {
-          selectionA     = grid.snap ? applySnap(pos) : pos;
-          selectionA_abs = absolutePoint(selectionA);
-        }
-        else
-        if (mouseOnSelectionB()) {
-          selectionB     = grid.snap ? applySnap(pos) : pos;
-          selectionB_abs = absolutePoint(selectionB);
-        }
-        redraw();
-      }
-      mouseX = Fl::event_x();
-      ret = 1;
-      break;
-    }
-  }
-  return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* pixel snap disances (10px) must be equal to those defined in
- * gWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */
-/* TODO - use constant for 10px */
-
-int gWaveform::applySnap(int pos)
-{
-  for (unsigned i=0; i<grid.points.size(); i++) {
-    if (pos >= grid.points.at(i) - 10 &&
-        pos <= grid.points.at(i) + 10)
-    {
-      return grid.points.at(i);
-    }
-  }
-  return pos;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::mouseOnStart()
-{
-  return mouseX-10 >  chanStart + x() - BORDER              &&
-         mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH &&
-         mouseY    >  h() + y() - FLAG_HEIGHT;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::mouseOnEnd()
-{
-  return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH &&
-         mouseX-10 <= chanEnd + x() - BORDER              &&
-         mouseY    <= y() + FLAG_HEIGHT + 1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* pixel boundaries (10px) must be equal to the snap factor distance
- * defined in gWaveform::applySnap() */
-
-bool gWaveform::mouseOnSelectionA()
-{
-  if (selectionA == selectionB)
-    return false;
-  return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x();
-}
-
-
-bool gWaveform::mouseOnSelectionB()
-{
-  if (selectionA == selectionB)
-    return false;
-  return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::absolutePoint(int p)
-{
-  if (p <= 0)
-    return 0;
-
-  if (p > data.size)
-    return chan->wave->size / 2;
-
-  return (p * ratio) / 2;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::relativePoint(int p)
-{
-  return (ceilf(p / ratio)) * 2;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::openEditMenu()
-{
-  if (selectionA == selectionB)
-    return;
-
-  menuOpen = true;
-
-  Fl_Menu_Item menu[] = {
-    {"Cut"},
-    {"Trim"},
-    {"Silence"},
-    {"Fade in"},
-    {"Fade out"},
-    {"Smooth edges"},
-    {"Set start/end here"},
-    {0}
-  };
-
-  if (chan->status == STATUS_PLAY) {
-    menu[0].deactivate();
-    menu[1].deactivate();
-  }
-
-  Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
-  b->box(G_CUSTOM_BORDER_BOX);
-  b->textsize(GUI_FONT_SIZE_BASE);
-  b->textcolor(COLOR_TEXT_0);
-  b->color(COLOR_BG_0);
-
-  const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-  if (!m) {
-    menuOpen = false;
-    return;
-  }
-
-  /* straightSel() to ensure that point A is always lower than B */
-
-  straightSel();
-
-  if (strcmp(m->label(), "Silence") == 0) {
-    wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
-    selectionA = 0;
-    selectionB = 0;
-
-    stretchToWindow();
-    redraw();
-    menuOpen = false;
-    return;
-  }
-
-  if (strcmp(m->label(), "Set start/end here") == 0) {
-
-    glue_setBeginEndChannel(
-        (gdEditor *) window(), // parent
-        chan,
-        absolutePoint(selectionA) * 2,  // stereo!
-        absolutePoint(selectionB) * 2,  // stereo!
-        false, // no recalc (we do it here)
-        false  // don't check
-        );
-
-    selectionA     = 0;
-    selectionB     = 0;
-    selectionA_abs = 0;
-    selectionB_abs = 0;
-
-    recalcPoints();
-    redraw();
-    menuOpen = false;
-    return;
-  }
-
-  if (strcmp(m->label(), "Cut") == 0) {
-    wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
-    /* for convenience reset start/end points */
-
-    glue_setBeginEndChannel(
-      (gdEditor *) window(),
-      chan,
-      0,
-      chan->wave->size,
-      false);
-
-    selectionA     = 0;
-    selectionB     = 0;
-    selectionA_abs = 0;
-    selectionB_abs = 0;
-
-    setZoom(0);
-
-    menuOpen = false;
-    return;
-  }
-
-  if (strcmp(m->label(), "Trim") == 0) {
-    wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
-    glue_setBeginEndChannel(
-      (gdEditor *) window(),
-      chan,
-      0,
-      chan->wave->size,
-      false);
-
-    selectionA     = 0;
-    selectionB     = 0;
-    selectionA_abs = 0;
-    selectionB_abs = 0;
-
-    stretchToWindow();
-    menuOpen = false;
-    redraw();
-    return;
-  }
-
-  if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) {
-
-    int type = !strcmp(m->label(), "Fade in") ? 0 : 1;
-    wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type);
-
-    selectionA = 0;
-    selectionB = 0;
-
-    stretchToWindow();
-    redraw();
-    menuOpen = false;
-    return;
-  }
-
-  if (!strcmp(m->label(), "Smooth edges")) {
-
-    wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
-    selectionA = 0;
-    selectionB = 0;
-
-    stretchToWindow();
-    redraw();
-    menuOpen = false;
-    return;
-  }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::straightSel()
-{
-  if (selectionA > selectionB) {
-    unsigned tmp = selectionB;
-    selectionB = selectionA;
-    selectionA = tmp;
-  }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::setZoom(int type)
-{
-  int newSize;
-  if (type == -1) newSize = data.size*2;  // zoom in
-  else            newSize = data.size/2;  // zoom out
-
-  if (alloc(newSize)) {
-    size(data.size, h());
-
-    /* zoom to pointer */
-
-    int shift;
-    if (x() > 0)
-      shift = Fl::event_x() - x();
-    else
-    if (type == -1)
-      shift = Fl::event_x() + abs(x());
-    else
-      shift = (Fl::event_x() + abs(x())) / -2;
-
-    if (x() - shift > BORDER)
-      shift = 0;
-
-    position(x() - shift, y());
-
-
-    /* avoid overflow when zooming out with scrollbar like that:
-     * |----------[scrollbar]|
-     *
-     * offset vs smaller:
-     * |[wave------------| offset > 0  smaller = false
-     * |[wave----]       | offset < 0, smaller = true
-     * |-------------]   | offset < 0, smaller = false  */
-
-    int  parentW = ((gWaveTools*)parent())->w();
-    int  thisW   = x() + w() - BORDER;           // visible width, not full width
-
-    if (thisW < parentW)
-      position(x() + parentW - thisW, y());
-    if (smaller())
-      stretchToWindow();
-
-    redraw();
-  }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::stretchToWindow()
-{
-  int s = ((gWaveTools*)parent())->w();
-  alloc(s);
-  position(BORDER, y());
-  size(s, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::smaller()
-{
-  return w() < ((gWaveTools*)parent())->w();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::setGridLevel(int l)
-{
-  grid.points.clear();
-  grid.level = l;
-  alloc(data.size);
-  redraw();
-}
diff --git a/src/gui/elems/ge_waveform.h b/src/gui/elems/ge_waveform.h
deleted file mode 100644 (file)
index 77a528f..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_waveform
- * an element which represents a waveform.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_WAVEFORM_H
-#define GE_WAVEFORM_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Widget.H>
-#include <FL/fl_draw.H>
-#include <math.h>
-#include "../../utils/fs.h"
-
-
-using std::vector;
-
-
-#define  FLAG_WIDTH  14
-#define  FLAG_HEIGHT 12
-#define  BORDER      8                         // window border <-> widget border
-
-
-class gWaveform : public Fl_Widget {
-
-private:
-
-       /* data
-        * real graphic stuff from the underlying waveform */
-
-       struct data {
-               int *sup;
-               int *inf;
-               int  size;
-       } data;
-
-       /* grid */
-
-       struct grid {
-               bool snap;
-               int  level;
-               vector<int> points;
-       } grid;
-
-       /* chan
-        * chan in use. */
-
-       class SampleChannel *chan;
-
-       /* menuOpen
-        * is the menu open? */
-
-       bool menuOpen;
-
-       /* mouseOnStart/end
-        * is mouse on start or end flag? */
-
-       bool mouseOnStart();
-       bool mouseOnEnd();
-
-       /* mouseOnSelectionA/B
-        * as above, for the selection */
-
-       bool mouseOnSelectionA();
-       bool mouseOnSelectionB();
-
-       /* absolutePoint
-        * from a relative 'p' point (zoom affected) returns the same point
-        * zoom 1:1 based */
-
-       int absolutePoint(int p);
-
-       /* relativePoint
-        * from an absolute 'p' point (1:1 zoom), returns the same point zoom
-        * affected */
-
-       int relativePoint(int p);
-
-       /* straightSel
-        * helper function which flattens the selection if it was made from
-        * right to left (inverse selection) */
-
-       void straightSel();
-
-       /* freeData
-        * destroy any graphical buffer */
-
-       void freeData();
-
-       /* smaller
-        * is the waveform smaller than the parent window? */
-
-       bool smaller();
-
-  /* applySnap
-   * snap a point at 'pos' pixel */
-
-  int applySnap(int pos);
-
-public:
-
-       gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0);
-       ~gWaveform();
-       void draw();
-       int  handle(int e);
-
-       /* alloc
-        * allocate memory for the picture */
-
-       int alloc(int datasize=0);
-
-       /* recalcPoints
-        * re-calc chanStart, chanEnd, ... */
-
-       void recalcPoints();
-
-       /* openEditMenu
-        * show edit menu on right-click */
-
-       void openEditMenu();
-
-       /* displayRatio
-        * how much of the waveform is being displayed on screen */
-
-       inline float displayRatio() { return 1.0f / (data.size / (float) w()); };
-
-       /* zoom
-        * type == 1 : zoom out, type == -1: zoom in */
-
-       void setZoom(int type);
-
-       /* strecthToWindow
-        * shrink or enlarge the waveform to match parent's width (gWaveTools) */
-
-       void stretchToWindow();
-
-       /* setGridLevel
-        * set a new frequency level for the grid. 0 means disabled. */
-
-       void setGridLevel(int l);
-
-  inline void setSnap(bool v) { grid.snap = v; }
-  inline bool getSnap()       { return grid.snap; }
-
-       inline int getSize() { return data.size; }
-
-       int  chanStart;
-       bool chanStartLit;
-       int  chanEnd;
-       bool chanEndLit;
-       bool pushed;
-       bool dragged;
-       bool resized;
-
-       float ratio;
-
-  /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */
-       int  mouseX;                                     // mouse pos for drag.n.drop
-       int  mouseY;
-
-       /* selectionA/B  = portion of the selected wave
-        * " " "" " _abs = selectionA/B not affected by zoom */
-       /** TODO - change selectionA to selectionA_rel
-           TODO - change selectionB to selectionB_rel */
-       int selectionA;
-       int selectionB;
-       int selectionA_abs;
-       int selectionB_abs;
-};
-
-
-#endif
diff --git a/src/gui/elems/ge_window.cpp b/src/gui/elems/ge_window.cpp
deleted file mode 100644 (file)
index e14fcb8..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_window
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "ge_window.h"
-#include "../../utils/log.h"
-
-
-gWindow::gWindow(int x, int y, int w, int h, const char *title, int id)
-       : Fl_Double_Window(x, y, w, h, title), id(id), parent(NULL) { }
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow::gWindow(int w, int h, const char *title, int id)
-       : Fl_Double_Window(w, h, title), id(id), parent(NULL) { }
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow::~gWindow() {
-
-       /* delete all subwindows in order to empty the stack */
-
-       for (unsigned i=0; i<subWindows.size(); i++)
-               delete subWindows.at(i);
-       subWindows.clear();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* this is the default callback of each window, fired when the user closes
- * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */
-
-void gWindow::cb_closeChild(Fl_Widget *v, void *p) {
-       gWindow *child = (gWindow*) v;
-       if (child->getParent() != NULL)
-               (child->getParent())->delSubWindow(child);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::addSubWindow(gWindow *w) {
-
-       /** TODO - useless: delete ---------------------------------------- */
-       for (unsigned i=0; i<subWindows.size(); i++)
-               if (w->getId() == subWindows.at(i)->getId()) {
-                       //gu_log("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
-                       delete w;
-                       return;
-               }
-       /** --------------------------------------------------------------- */
-
-       w->setParent(this);
-       w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
-       subWindows.push_back(w);
-       //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::delSubWindow(gWindow *w) {
-       for (unsigned i=0; i<subWindows.size(); i++)
-               if (w->getId() == subWindows.at(i)->getId()) {
-                       delete subWindows.at(i);
-                       subWindows.erase(subWindows.begin() + i);
-                       //debug();
-                       return;
-               }
-       //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::delSubWindow(int id) {
-       for (unsigned i=0; i<subWindows.size(); i++)
-               if (subWindows.at(i)->getId() == id) {
-                       delete subWindows.at(i);
-                       subWindows.erase(subWindows.begin() + i);
-                       //debug();
-                       return;
-               }
-       //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWindow::getId() {
-       return id;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::setId(int id) {
-       this->id = id;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::debug() {
-       gu_log("---- window stack (id=%d): ----\n", getId());
-       for (unsigned i=0; i<subWindows.size(); i++)
-               gu_log("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
-       gu_log("----\n");
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow *gWindow::getParent() {
-       return parent;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::setParent(gWindow *w) {
-       parent = w;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWindow::hasWindow(int id) {
-       for (unsigned i=0; i<subWindows.size(); i++)
-               if (id == subWindows.at(i)->getId())
-                       return true;
-       return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow *gWindow::getChild(int id) {
-       for (unsigned i=0; i<subWindows.size(); i++)
-               if (id == subWindows.at(i)->getId())
-                       return subWindows.at(i);
-       return NULL;
-}
diff --git a/src/gui/elems/ge_window.h b/src/gui/elems/ge_window.h
deleted file mode 100644 (file)
index e986d8d..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_window
- * A custom window.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 __GE_WINDOW_H__
-#define __GE_WINDOW_H__
-
-
-#include <vector>
-#include <FL/Fl_Double_Window.H>
-#include "../../utils/fs.h"
-
-
-using std::vector;
-
-
-class gWindow : public Fl_Double_Window {
-
-protected:
-       vector <gWindow *> subWindows;
-       int id;
-       gWindow *parent;
-
-public:
-       gWindow(int x, int y, int w, int h, const char *title=0, int id=0);
-       gWindow(int w, int h, const char *title=0, int id=0);
-       ~gWindow();
-
-       static void cb_closeChild(Fl_Widget *v, void *p);
-
-       void addSubWindow(gWindow *w);
-       void delSubWindow(gWindow *w);
-       void delSubWindow(int id);
-
-       int  getId();
-       void setId(int id);
-       void debug();
-
-       void     setParent(gWindow *);
-       gWindow *getParent();
-       gWindow *getChild(int id);
-
-       /* hasWindow
-        * true if the window with id 'id' exists in the stack. */
-
-       bool hasWindow(int id);
-
-};
-
-
-#endif
index d0bb0770276a832f4c73d84e730ae4f99351830c..7b9262e892d7dea2ad5575e3cfef1e3106aa3e15 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/fl_draw.H>
 #include "../../../core/const.h"
 #include "../../../core/mixer.h"
+#include "../../../core/clock.h"
 #include "beatMeter.h"
 
 
-extern Mixer G_Mixer;
+using namespace giada::m;
 
 
 geBeatMeter::geBeatMeter(int x, int y, int w, int h, const char *L)
@@ -45,25 +46,25 @@ geBeatMeter::geBeatMeter(int x, int y, int w, int h, const char *L)
 
 void geBeatMeter::draw()
 {
-  int cursorW = w() / MAX_BEATS;
-  int greyX   = G_Mixer.beats * cursorW;
+  int cursorW = w() / G_MAX_BEATS;
+  int greyX   = clock::getBeats() * cursorW;
 
   fl_rect(x(), y(), w(), h(), COLOR_BD_0);                            // border
   fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR);          // bg
-  fl_rectf(x()+(G_Mixer.actualBeat*cursorW)+3, y()+3, cursorW-5, h()-6,
+  fl_rectf(x()+(clock::getCurrentBeat()*cursorW)+3, y()+3, cursorW-5, h()-6,
     COLOR_BG_2); // cursor
 
   /* beat cells */
 
   fl_color(COLOR_BD_0);
-  for (int i=1; i<=G_Mixer.beats; i++)
+  for (int i=1; i<=clock::getBeats(); i++)
     fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2);
 
   /* bar line */
 
   fl_color(COLOR_BG_2);
-  int delta = G_Mixer.beats / G_Mixer.bars;
-  for (int i=1; i<G_Mixer.bars; i++)
+  int delta = clock::getBeats() / clock::getBars();
+  for (int i=1; i<clock::getBars(); i++)
     fl_line(x()+cursorW*(i*delta), y()+1, x()+cursorW*(i*delta), y()+h()-2);
 
   /* unused grey area */
index 5ca7b3240bc84db4d37f1d27220ca9eb5ea999d7..c5cdf422695a25fabc1826663331bb4fab1997e0 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index 1114d7c7d605dcd83548322a388c89f7a5b3ca4c..8a59077c5b0706d2df92cf6cbd9cb2fba03e6c8f 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -27,6 +25,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/Fl.H>
 #include "../../../../core/const.h"
 #include "../../../../core/channel.h"
 #include "../../../../core/graphics.h"
@@ -35,6 +34,8 @@
 #include "../../../../glue/channel.h"
 #include "../../../dialogs/gd_mainWindow.h"
 #include "../../../dialogs/gd_pluginList.h"
+#include "../../basics/idButton.h"
+#include "../../basics/dial.h"
 #include "column.h"
 #include "channelButton.h"
 #include "channel.h"
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
 geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch)
- : Fl_Group(X, Y, W, H, NULL),
+ : Fl_Group(X, Y, W, H, nullptr),
    ch      (ch),
    type    (type)
 {
@@ -95,7 +99,7 @@ void geChannel::__cb_solo()
 
 void geChannel::__cb_changeVol()
 {
-       glue_setChanVol(ch, vol->value());
+       glue_setVolume(ch, vol->value());
 }
 
 
@@ -105,7 +109,7 @@ void geChannel::__cb_changeVol()
 #ifdef WITH_VST
 void geChannel::__cb_openFxWindow()
 {
-       gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
+       gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::CHANNEL, ch), WID_FX_LIST);
 }
 #endif
 
index f76378f495ff0461a395d7c863bfa12c803166a0..fd1efd9bb5003354b91594b1540a11736c453f3c 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_channel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Group.H>
 
 
+class Channel;
+class geIdButton;
+class geChannelStatus;
+class geButton;
+class geChannelButton;
+class geDial;
+#ifdef WITH_VST
+class geStatusButton;
+#endif
+
+
 class geChannel : public Fl_Group
 {
 protected:
@@ -90,7 +99,7 @@ protected:
 
 public:
 
-       geChannel(int x, int y, int w, int h, int type, class Channel *ch);
+       geChannel(int x, int y, int w, int h, int type, Channel *ch);
 
        /* reset
         * reset channel to initial status. */
@@ -119,17 +128,17 @@ public:
 
        int getColumnIndex();
 
-       class Channel *ch;
+       Channel *ch;
 
-       class gButton         *button;
-       class geChannelStatus *status;
-       class gClick            *arm;
-       class geChannelButton *mainButton;
-       class gClick            *mute;
-       class gClick            *solo;
-       class gDial           *vol;
+       geIdButton      *button;
+       geChannelStatus *status;
+       geButton              *arm;
+       geChannelButton *mainButton;
+       geButton              *mute;
+       geButton              *solo;
+       geDial           *vol;
 #ifdef WITH_VST
-       class gFxButton       *fx;
+       geStatusButton  *fx;
 #endif
 
        int type;
index 9a17eb437909417618553f7a7b559b850f908e4a..e3545002f0dc26e975eb4d432bf74256dcfa50f9 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_channelButton
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -27,6 +25,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/fl_draw.H>
 #include "../../../../core/const.h"
 #include "channelButton.h"
 
@@ -35,7 +34,7 @@ using std::string;
 
 
 geChannelButton::geChannelButton(int x, int y, int w, int h, const char *l)
-  : gClick(x, y, w, h, l), key("") {}
+  : geButton(x, y, w, h, l), key("") {}
 
 
 /* -------------------------------------------------------------------------- */
@@ -67,7 +66,7 @@ void geChannelButton::setKey(int k)
 
 void geChannelButton::draw()
 {
-  gClick::draw();
+  geButton::draw();
 
   if (key == "")
     return;
index 516398330e6111bc6c5f7ed658ce660f178b2e36..78c6f68c86f460305ce96e86b4dc61b3a600118f 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_channelButton
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #define GE_CHANNEL_BUTTON_H
 
 
-#include "../../ge_mixed.h"
+#include "../../basics/button.h"
 
 
-class geChannelButton : public gClick
+class geChannelButton : public geButton
 {
 private:
 
@@ -46,7 +44,8 @@ public:
 
        virtual int handle(int e) = 0;
 
-       void draw();
+       void draw() override;
+  
        void setKey(const std::string &k);
        void setKey(int k);
        void setPlayMode();
index 5babd63318f08cfc31afb62dda645bd6821ec585..8231291d83cbaf0293c45602ce2c113a45fab227 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index 0829be97d369b98f0f6762139ee05dcbbc22b25d..2bbdcd0552603f1b663558ef962d005caf21f7fe 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index bc8923995a9a8bad519d86becfbcc32cc76220fc..bcd3c4708d5c85b1af59be3b48eefde718ea9688 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_status
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include <FL/fl_draw.H>
 #include "../../../../core/mixer.h"
+#include "../../../../core/clock.h"
 #include "../../../../core/sampleChannel.h"
 #include "../../../../core/recorder.h"
 #include "../../../../core/const.h"
 #include "channelStatus.h"
 
 
-extern Mixer    G_Mixer;
-extern Recorder G_Recorder;
+using namespace giada::m;
 
 
 geChannelStatus::geChannelStatus(int x, int y, int w, int h, SampleChannel *ch,
@@ -52,7 +50,7 @@ void geChannelStatus::draw()
   fl_rect(x(), y(), w(), h(), COLOR_BD_0);              // reset border
   fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);     // reset background
 
-  if (ch != NULL) {
+  if (ch != nullptr) {
     if (ch->status    & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) ||
         ch->recStatus & (REC_WAITING | REC_ENDING))
     {
@@ -65,10 +63,10 @@ void geChannelStatus::draw()
       fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);     // status empty
 
 
-    if (G_Mixer.recording && ch->armed)
+    if (mixer::recording && ch->armed)
       fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3);     // take in progress
     else
-    if (G_Recorder.active && G_Recorder.canRec(ch, &G_Mixer))
+    if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording))
       fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4);     // action record
 
     /* equation for the progress bar:
index 6e911cbda592669012ea97e9436cf8846eea2c4f..5063c9d7b0de9dfbff4147af29ab085b60627031 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index 6518aedfcebe14bd45b2b2bdba2dea75346ba797..5d94a7c985e95f53edace632bf21b36d3a69173d 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_column
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/fl_draw.H>
+#include <FL/Fl_Menu_Button.H>
 #include "../../../../core/sampleChannel.h"
 #include "../../../../glue/channel.h"
 #include "../../../../utils/log.h"
+#include "../../../../utils/fs.h"
 #include "../../../../utils/string.h"
 #include "../../../dialogs/gd_warnings.h"
 #include "../../../elems/basics/boxtypes.h"
+#include "../../../elems/basics/resizerBar.h"
 #include "keyboard.h"
 #include "sampleChannel.h"
 #include "midiChannel.h"
 #include "column.h"
 
 
+using std::vector;
+
+
 geColumn::geColumn(int X, int Y, int W, int H, int index, geKeyboard *parent)
        : Fl_Group(X, Y, W, H), parent(parent), index(index)
 {
   /* geColumn does a bit of a mess: we pass a pointer to its parent (geKeyboard) and
   the geColumn itself deals with the creation of another widget, outside geColumn
-  and inside geKeyboard, which handles the vertical resize bar (gResizerBar).
+  and inside geKeyboard, which handles the vertical resize bar (geResizerBar).
   The resizer cannot stay inside geColumn: it needs a broader view on the other
   side widgets. The view can be obtained from geKeyboard only (the upper level).
-  Unfortunately, parent() can be NULL: at this point (i.e the constructor)
+  Unfortunately, parent() can be nullptr: at this point (i.e the constructor)
   geColumn is still detached from any parent. We use a custom geKeyboard *parent
   instead. */
 
        begin();
-       addChannelBtn = new gClick(x(), y(), w(), 20, "Add new channel");
+       addChannelBtn = new geButton(x(), y(), w(), 20, "Add new channel");
        end();
 
-  resizer = new gResizerBar(x()+w(), y(), 16, h(), false);
-  resizer->setMinSize(MIN_COLUMN_WIDTH);
+  resizer = new geResizerBar(x()+w(), y(), 16, h(), false);
+  resizer->setMinSize(G_MIN_COLUMN_WIDTH);
   parent->add(resizer);
 
        addChannelBtn->callback(cb_addChannel, (void*)this);
@@ -187,7 +192,7 @@ void geColumn::cb_addChannel(Fl_Widget *v, void *p) { ((geColumn*)p)->__cb_addCh
 geChannel *geColumn::addChannel(Channel *ch)
 {
        int currentY = y() + children() * 24;
-       geChannel *gch = NULL;
+       geChannel *gch = nullptr;
        if (ch->type == CHANNEL_SAMPLE)
                gch = (geSampleChannel*) new geSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch);
        else
index 1d78540f94dd06af43527aa516ff87445fcf16f9..8c1152821733d0b639cab935a51ff245d5d63a7a 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_column
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Group.H>
 
 
+class Channel;
+class geButton;
+class geChannel;
+class geResizerBar;
+class geKeyboard;
+
+
 class geColumn : public Fl_Group
 {
 private:
@@ -43,22 +48,22 @@ private:
 
        int openTypeMenu();
 
-       class gClick      *addChannelBtn;
-       class gResizerBar *resizer;
-       class geKeyboard   *parent;
+       geButton    *addChannelBtn;
+       geResizerBar *resizer;
+       geKeyboard  *parent;
 
        int index;
 
 public:
 
-       geColumn(int x, int y, int w, int h, int index, class geKeyboard *parent);
+       geColumn(int x, int y, int w, int h, int index, geKeyboard *parent);
        ~geColumn();
 
        /* addChannel
         * add a new channel in this column and set the internal pointer
         * to channel to 'ch'. */
 
-       class geChannel *addChannel(class Channel *ch);
+       geChannel *addChannel(Channel *ch);
 
        /* handle */
 
index 00d2bef7a9abd7ce356e295c87d67ddac88cdc6b..088c2fbe7bf05e7fb64fa9a6616a69255def6067 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gg_keyboard
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include "../../../../core/sampleChannel.h"
-#include "../../../../glue/main.h"
+#include "../../../../glue/transport.h"
 #include "../../../../glue/io.h"
 #include "../../../../utils/log.h"
 #include "../../../dialogs/gd_warnings.h"
 #include "../../basics/boxtypes.h"
 #include "column.h"
 #include "sampleChannel.h"
+#include "channelButton.h"
 #include "keyboard.h"
 
 
@@ -49,7 +48,7 @@ geKeyboard::geKeyboard(int X, int Y, int W, int H)
        bckspcPressed(false),
        endPressed   (false),
        spacePressed (false),
-       addColumnBtn (NULL)
+       addColumnBtn (nullptr)
 {
        color(COLOR_BG_MAIN);
        type(Fl_Scroll::BOTH_ALWAYS);
@@ -62,7 +61,7 @@ geKeyboard::geKeyboard(int X, int Y, int W, int H)
        hscrollbar.labelcolor(COLOR_BD_1);
        hscrollbar.slider(G_CUSTOM_BORDER_BOX);
 
-       addColumnBtn = new gClick(8, y(), 200, 20, "Add new column");
+       addColumnBtn = new geButton(8, y(), 200, 20, "Add new column");
        addColumnBtn->callback(cb_addColumn, (void*) this);
        add(addColumnBtn);
 
@@ -161,7 +160,7 @@ void geKeyboard::organizeColumns()
 
 void geKeyboard::cb_addColumn(Fl_Widget *v, void *p)
 {
-       ((geKeyboard*)p)->__cb_addColumn(DEFAULT_COLUMN_WIDTH);
+       ((geKeyboard*)p)->__cb_addColumn(G_DEFAULT_COLUMN_WIDTH);
 }
 
 
@@ -205,7 +204,7 @@ geColumn *geKeyboard::getColumnByIndex(int index)
        for (unsigned i=0; i<columns.size(); i++)
                if (columns.at(i)->getIndex() == index)
                        return columns.at(i);
-       return NULL;
+       return nullptr;
 }
 
 
@@ -232,25 +231,25 @@ int geKeyboard::handle(int e)
                        if (e == FL_KEYDOWN) {
                                if (Fl::event_key() == FL_BackSpace && !bckspcPressed) {
                                        bckspcPressed = true;
-                                       glue_rewindSeq();
+                                       glue_rewindSeq(false);          // not from GUI
                                        ret = 1;
                                        break;
                                }
                                else if (Fl::event_key() == FL_End && !endPressed) {
                                        endPressed = true;
-                                       glue_startStopInputRec(false);  // update gui
+                                       glue_startStopInputRec(false);  // not from GUI
                                        ret = 1;
                                        break;
                                }
                                else if (Fl::event_key() == FL_Enter && !enterPressed) {
                                        enterPressed = true;
-                                       glue_startStopActionRec(false); // update gui
+                                       glue_startStopActionRec(false); // not from GUI
                                        ret = 1;
                                        break;
                                }
                                else if (Fl::event_key() == ' ' && !spacePressed) {
                                        spacePressed = true;
-          glue_startStopSeq(false);   // update gui
+          glue_startStopSeq(false);      // unot from GUI
                                        ret = 1;
                                        break;
                                }
index 8778c2aeca72b219c2fed5950f54b147d468f879..a73cdab3ad93949c55920a0dcb3709d4eae71a10 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gg_keyboard
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../../../../core/const.h"
 
 
+class Channel;
+class geButton;
+class geColumn;
+class geChannel;
+class geSampleChannel;
+
+
 class geKeyboard : public Fl_Scroll
 {
 private:
@@ -47,7 +52,7 @@ private:
        void refreshColIndexes();
 
        static void cb_addColumn  (Fl_Widget *v, void *p);
-       inline void __cb_addColumn(int width=DEFAULT_COLUMN_WIDTH);
+       inline void __cb_addColumn(int width=G_DEFAULT_COLUMN_WIDTH);
 
        bool bckspcPressed;
        bool endPressed;
@@ -59,12 +64,12 @@ private:
 
        static int indexColumn;
 
-       class gClick *addColumnBtn;
+       geButton *addColumnBtn;
 
        /* columns
         * a vector of columns which in turn contain channels. */
 
-       std::vector<class geColumn*> columns;
+       std::vector<geColumn*> columns;
 
 public:
 
@@ -83,7 +88,7 @@ public:
         * set to true, also generate the corresponding column if column (index) does
         * not exist yet. */
 
-       class geChannel *addChannel(int column, class Channel *ch, bool build=false);
+       geChannel *addChannel(int column, Channel *ch, bool build=false);
 
        /* addColumn
         * add a new column to the top of the stack. */
@@ -118,7 +123,7 @@ public:
        void refreshColumns();
 
        /* getColumnByIndex
-        * return the column with index 'index', or NULL if not found. */
+        * return the column with index 'index', or nullptr if not found. */
 
        geColumn *getColumnByIndex(int index);
 
@@ -135,7 +140,7 @@ public:
        /* setChannelWithActions
         * add 'R' button if channel has actions, and set recorder to active. */
 
-       void setChannelWithActions(class geSampleChannel *gch);
+       void setChannelWithActions(geSampleChannel *gch);
 
        /* printChannelMessage
         * given any output by glue_loadChannel, print the message on screen
index 183bc71ce0b751ac22f632a056474e80e9c211f9..56bef49ed1b5369a7bb0a4ed20595596c1671bf2 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_midiChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/Fl_Menu_Button.H>
 #include "../../../../core/const.h"
 #include "../../../../core/graphics.h"
 #include "../../../../core/midiChannel.h"
+#include "../../../../utils/gui.h"
 #include "../../../../glue/channel.h"
 #include "../../../../glue/io.h"
+#include "../../../../glue/recorder.h"
 #include "../../../dialogs/gd_mainWindow.h"
-#include "../../../dialogs/gd_editor.h"
+#include "../../../dialogs/sampleEditor.h"
 #include "../../../dialogs/gd_actionEditor.h"
 #include "../../../dialogs/gd_warnings.h"
-#include "../../../dialogs/gd_browser.h"
 #include "../../../dialogs/gd_keyGrabber.h"
 #include "../../../dialogs/gd_pluginList.h"
 #include "../../../dialogs/midiIO/midiInputChannel.h"
 #include "../../../dialogs/midiIO/midiOutputMidiCh.h"
 #include "../../basics/boxtypes.h"
+#include "../../basics/idButton.h"
+#include "../../basics/statusButton.h"
+#include "../../basics/dial.h"
 #include "midiChannel.h"
 
 
-extern Recorder                         G_Recorder;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
+namespace
+{
+enum class Menu
+{
+  EDIT_ACTIONS = 0,
+  CLEAR_ACTIONS,
+  CLEAR_ACTIONS_ALL,
+  __END_SUBMENU__,
+  SETUP_KEYBOARD_INPUT,
+  SETUP_MIDI_INPUT,
+  SETUP_MIDI_OUTPUT,
+  CLONE_CHANNEL,
+  DELETE_CHANNEL
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void menuCallback(Fl_Widget *w, void *v)
+{
+  geMidiChannel *gch = static_cast<geMidiChannel*>(w);
+  Menu selectedItem = (Menu) (intptr_t) v;
+
+  switch (selectedItem)
+  {
+    case Menu::CLEAR_ACTIONS:
+    case Menu::__END_SUBMENU__:
+      break;
+    case Menu::EDIT_ACTIONS:
+      gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+      break;
+    case Menu::CLEAR_ACTIONS_ALL:
+      glue_clearAllActions(gch);
+      break;
+    case Menu::SETUP_KEYBOARD_INPUT:
+      gu_openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0);
+      break;
+    case Menu::SETUP_MIDI_INPUT:
+      gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+      break;
+    case Menu::SETUP_MIDI_OUTPUT:
+      gu_openSubWindow(G_MainWin,
+        new gdMidiOutputMidiCh(static_cast<MidiChannel*>(gch->ch)), 0);
+      break;
+    case Menu::CLONE_CHANNEL:
+      glue_cloneChannel(gch->ch);
+      break;
+    case Menu::DELETE_CHANNEL:
+      glue_deleteChannel(gch->ch);
+      break;
+  }
+}
+
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch)
        : geChannel(X, Y, W, H, CHANNEL_MIDI, ch)
 {
@@ -60,16 +125,16 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch)
        int delta = 120; // (5 widgets * 20) + (5 paddings * 4)
 #endif
 
-       button     = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
-       arm        = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+       button     = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+       arm        = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
        mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, 20, "-- MIDI --");
-       mute       = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
-       solo       = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+       mute       = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+       solo       = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
 #if defined(WITH_VST)
-       fx         = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
-       vol        = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+       fx         = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+       vol        = new geDial(fx->x()+fx->w()+4, y(), 20, 20);
 #else
-       vol        = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+       vol        = new geDial(solo->x()+solo->w()+4, y(), 20, 20);
 #endif
 
        end();
@@ -125,22 +190,22 @@ void geMidiChannel::__cb_button()
 void geMidiChannel::__cb_openMenu()
 {
        Fl_Menu_Item rclick_menu[] = {
-               {"Edit actions..."},                        // 0
-               {"Clear actions", 0, 0, 0, FL_SUBMENU},     // 1
-                       {"All"},                                  // 2
-                       {0},                                      // 3
-               {"Setup keyboard input..."},                // 5
-               {"Setup MIDI input..."},                    // 6
-               {"Setup MIDI output..."},                   // 7
-               {"Clone channel"},                          // 8
-               {"Delete channel"},                         // 9
+               {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
+               {"Clear actions",   0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
+                       {"All",           0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+                       {0},
+               {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
+               {"Setup MIDI input...",     0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
+               {"Setup MIDI output...",    0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
+               {"Clone channel",           0, menuCallback, (void*) Menu::CLONE_CHANNEL},
+               {"Delete channel",          0, menuCallback, (void*) Menu::DELETE_CHANNEL},
                {0}
        };
 
-       /* no 'clear actions' if there are no actions */
+       /* No 'clear actions' if there are no actions. */
 
        if (!ch->hasActions)
-               rclick_menu[1].deactivate();
+               rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate();
 
        Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
        b->box(G_CUSTOM_BORDER_BOX);
@@ -148,51 +213,10 @@ void geMidiChannel::__cb_openMenu()
        b->textcolor(COLOR_TEXT_0);
        b->color(COLOR_BG_0);
 
-       const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (!m) return;
-
-       if (strcmp(m->label(), "Delete channel") == 0) {
-               if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
-                       return;
-               glue_deleteChannel(ch);
-               return;
-       }
-
-       if (strcmp(m->label(), "Clone channel") == 0) {
-               glue_cloneChannel(ch);
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup keyboard input...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdKeyGrabber(ch),       0);
-               //new gdKeyGrabber(ch);
-               return;
-       }
-
-       if (strcmp(m->label(), "All") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
-                       return;
-               G_Recorder.clearChan(ch->index);
-    ch->hasActions = false;
-               gu_refreshActionEditor(); // refresh a.editor window, it could be open
-               return;
-       }
-
-       if (strcmp(m->label(), "Edit actions...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdActionEditor(ch),     WID_ACTION_EDITOR);
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup MIDI input...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup MIDI output...") == 0) {
-               //gu_openSubWindow(G_MainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0);
-               gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh((MidiChannel*) ch), 0);
-               return;
-       }
+  const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+  if (m)
+    m->do_callback(this, m->user_data());
+  return;
 }
 
 
@@ -236,7 +260,7 @@ void geMidiChannel::update()
        mainButton->setKey(ch->key);
 
 #ifdef WITH_VST
-       fx->full = ch->plugins.size() > 0;
+       fx->status = ch->plugins.size() > 0;
        fx->redraw();
 #endif
 }
@@ -280,5 +304,5 @@ geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const char
 int geMidiChannelButton::handle(int e)
 {
        // MIDI drag-n-drop does nothing so far.
-       return gClick::handle(e);
+       return geButton::handle(e);
 }
index cd06ddc8dd219ec7d55057a4165de1174f6d6415..25de771d7ffa18f450fc1d158827488f83337750 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_midiChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -35,6 +33,9 @@
 #include "channelButton.h"
 
 
+class MidiChannel;
+
+
 class geMidiChannel : public geChannel
 {
 private:
@@ -48,7 +49,7 @@ private:
 
 public:
 
-       geMidiChannel(int x, int y, int w, int h,  class MidiChannel *ch);
+       geMidiChannel(int x, int y, int w, int h, MidiChannel *ch);
 
        void reset   ();
        void update  ();
index fa3289c70ea27f1c8e77e6b63ed1b282b5173b2d..276a9ff737d17d96df16ec660938099a4cc56404 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_sampleChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include "../../../../core/mixer.h"
 #include "../../../../core/conf.h"
-#include "../../../../core/recorder.h"
+#include "../../../../core/clock.h"
 #include "../../../../core/graphics.h"
 #include "../../../../core/wave.h"
 #include "../../../../core/sampleChannel.h"
-#include "../../../../glue/main.h"
 #include "../../../../glue/io.h"
 #include "../../../../glue/channel.h"
+#include "../../../../glue/recorder.h"
 #include "../../../../glue/storage.h"
-#include "../../../../utils/string.h"
+#include "../../../../utils/gui.h"
 #include "../../../dialogs/gd_mainWindow.h"
 #include "../../../dialogs/gd_keyGrabber.h"
-#include "../../../dialogs/gd_editor.h"
+#include "../../../dialogs/sampleEditor.h"
 #include "../../../dialogs/gd_actionEditor.h"
 #include "../../../dialogs/gd_warnings.h"
-#include "../../../dialogs/gd_browser.h"
+#include "../../../dialogs/browser/browserSave.h"
+#include "../../../dialogs/browser/browserLoad.h"
 #include "../../../dialogs/midiIO/midiOutputSampleCh.h"
 #include "../../../dialogs/midiIO/midiInputChannel.h"
 #include "../../basics/boxtypes.h"
+#include "../../basics/idButton.h"
+#include "../../basics/statusButton.h"
+#include "../../basics/dial.h"
 #include "channelStatus.h"
 #include "channelMode.h"
+#include "sampleChannelButton.h"
 #include "keyboard.h"
 #include "sampleChannel.h"
 
 
-extern Mixer                G_Mixer;
-extern Conf                 G_Conf;
-extern Recorder                         G_Recorder;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
+namespace
+{
+enum class Menu
+{
+  INPUT_MONITOR = 0,
+  LOAD_SAMPLE,
+  EXPORT_SAMPLE,
+  SETUP_KEYBOARD_INPUT,
+  SETUP_MIDI_INPUT,
+  SETUP_MIDI_OUTPUT,
+  EDIT_SAMPLE,
+  EDIT_ACTIONS,
+  CLEAR_ACTIONS,
+  CLEAR_ACTIONS_ALL,
+  CLEAR_ACTIONS_MUTE,
+  CLEAR_ACTIONS_VOLUME,
+  CLEAR_ACTIONS_START_STOP,
+  __END_SUBMENU__,
+  CLONE_CHANNEL,
+  FREE_CHANNEL,
+  DELETE_CHANNEL
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void menuCallback(Fl_Widget *w, void *v)
+{
+  geSampleChannel *gch = static_cast<geSampleChannel*>(w);
+  Menu selectedItem = (Menu) (intptr_t) v;
+
+  switch (selectedItem)
+  {
+    case Menu::INPUT_MONITOR: {
+      glue_toggleInputMonitor(gch->ch);
+      break;
+    }
+    case Menu::LOAD_SAMPLE: {
+      gdWindow *w = new gdBrowserLoad(conf::browserX, conf::browserY,
+        conf::browserW, conf::browserH, "Browse sample",
+        conf::samplePath.c_str(), glue_loadSample, gch->ch);
+      gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+      break;
+    }
+    case Menu::EXPORT_SAMPLE: {
+      gdWindow *w = new gdBrowserSave(conf::browserX, conf::browserY,
+        conf::browserW, conf::browserH, "Save sample",
+        conf::samplePath.c_str(), "", glue_saveSample, gch->ch);
+      gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+               break;
+    }
+    case Menu::SETUP_KEYBOARD_INPUT: {
+               new gdKeyGrabber(gch->ch); // FIXME - use gu_openSubWindow
+               break;
+    }
+    case Menu::SETUP_MIDI_INPUT: {
+      gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+      break;
+    }
+    case Menu::SETUP_MIDI_OUTPUT: {
+      gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast<SampleChannel*>(gch->ch)), 0);
+      break;
+    }
+    case Menu::EDIT_SAMPLE: {
+      gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast<SampleChannel*>(gch->ch)), WID_SAMPLE_EDITOR);
+      break;
+    }
+    case Menu::EDIT_ACTIONS: {
+      gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+      break;
+    }
+    case Menu::CLEAR_ACTIONS:
+    case Menu::__END_SUBMENU__:
+      break;
+    case Menu::CLEAR_ACTIONS_ALL: {
+      glue_clearAllActions(gch);
+      break;
+    }
+    case Menu::CLEAR_ACTIONS_MUTE: {
+      glue_clearMuteActions(gch);
+      break;
+    }
+    case Menu::CLEAR_ACTIONS_VOLUME: {
+      glue_clearVolumeActions(gch);
+      break;
+    }
+    case Menu::CLEAR_ACTIONS_START_STOP: {
+      glue_clearStartStopActions(gch);
+      break;
+    }
+    case Menu::CLONE_CHANNEL: {
+      glue_cloneChannel(gch->ch);
+      break;
+    }
+    case Menu::FREE_CHANNEL: {
+               glue_freeChannel(gch->ch);
+      break;
+    }
+    case Menu::DELETE_CHANNEL: {
+      glue_deleteChannel(gch->ch);
+      break;
+    }
+  }
+}
+
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch)
        : geChannel(X, Y, W, H, CHANNEL_SAMPLE, (Channel*) ch)
 {
        begin();
 
-       button      = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
-       arm         = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+       button      = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+       arm         = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
        status      = new geChannelStatus(arm->x()+arm->w()+4, y(), 20, 20, ch);
        mainButton  = new geSampleChannelButton(status->x()+status->w()+4, y(), 20, 20, "-- no sample --");
-       readActions = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm);
+       readActions = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm);
        modeBox     = new geChannelMode(readActions->x()+readActions->w()+4, y(), 20, 20, ch);
-       mute        = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
-       solo        = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+       mute        = new geButton(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+       solo        = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
 #ifdef WITH_VST
-       fx          = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
-       vol         = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+       fx          = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+       vol         = new geDial(fx->x()+fx->w()+4, y(), 20, 20);
 #else
-       vol         = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+       vol         = new geDial(solo->x()+solo->w()+4, y(), 20, 20);
 #endif
 
        end();
@@ -141,48 +256,45 @@ void geSampleChannel::__cb_openMenu()
        /* If you're recording (input or actions) no menu is allowed; you can't do
        anything, especially deallocate the channel */
 
-       if (G_Mixer.recording || G_Recorder.active)
+       if (mixer::recording || recorder::active)
                return;
 
-       /* the following is a trash workaround for a FLTK menu. We need a gMenu
-        * widget asap */
-
        Fl_Menu_Item rclick_menu[] = {
-               {"Load new sample..."},                     // 0
-               {"Export sample to file..."},               // 1
-               {"Setup keyboard input..."},                // 2
-               {"Setup MIDI input..."},                    // 3
-               {"Setup MIDI output..."},                   // 4
-               {"Edit sample..."},                         // 5
-               {"Edit actions..."},                        // 6
-               {"Clear actions", 0, 0, 0, FL_SUBMENU},     // 7
-                       {"All"},                                  // 8
-                       {"Mute"},                                 // 9
-                       {"Volume"},                               // 10
-                       {"Start/Stop"},                           // 11
-                       {0},                                      // 12
-               {"Clone channel"},                          // 13
-               {"Free channel"},                           // 14
-               {"Delete channel"},                         // 15
+               {"Input monitor",            0, menuCallback, (void*) Menu::INPUT_MONITOR,
+      FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast<SampleChannel*>(ch)->inputMonitor ? FL_MENU_VALUE : 0)},
+               {"Load new sample...",       0, menuCallback, (void*) Menu::LOAD_SAMPLE},
+               {"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE},
+               {"Setup keyboard input...",  0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
+               {"Setup MIDI input...",      0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
+               {"Setup MIDI output...",     0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
+               {"Edit sample...",           0, menuCallback, (void*) Menu::EDIT_SAMPLE},
+               {"Edit actions...",          0, menuCallback, (void*) Menu::EDIT_ACTIONS},
+               {"Clear actions",            0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
+                       {"All",        0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+                       {"Mute",       0, menuCallback, (void*) Menu::CLEAR_ACTIONS_MUTE},
+                       {"Volume",     0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME},
+                       {"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP},
+                       {0},
+               {"Clone channel",  0, menuCallback, (void*) Menu::CLONE_CHANNEL},
+               {"Free channel",   0, menuCallback, (void*) Menu::FREE_CHANNEL},
+               {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
                {0}
        };
 
        if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
-               rclick_menu[1].deactivate();
-               rclick_menu[5].deactivate();
-               rclick_menu[14].deactivate();
+               rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate();
+               rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate();
+               rclick_menu[(int) Menu::FREE_CHANNEL].deactivate();
        }
 
-       /* no 'clear actions' if there are no actions */
-
        if (!ch->hasActions)
-               rclick_menu[7].deactivate();
+               rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate();
 
-       /* no 'clear start/stop actions' for those channels in loop mode:
-        * they cannot have start/stop actions. */
+       /* No 'clear start/stop actions' for those channels in loop mode: they cannot
+  have start/stop actions. */
 
-       if (((SampleChannel*)ch)->mode & LOOP_ANY)
-               rclick_menu[11].deactivate();
+       if (static_cast<SampleChannel*>(ch)->mode & LOOP_ANY)
+               rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate();
 
        Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
        b->box(G_CUSTOM_BORDER_BOX);
@@ -191,130 +303,9 @@ void geSampleChannel::__cb_openMenu()
        b->color(COLOR_BG_0);
 
        const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (!m) return;
-
-       if (strcmp(m->label(), "Load new sample...") == 0) {
-    gWindow *w = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
-      G_Conf.browserW, G_Conf.browserH, "Browse sample",
-      G_Conf.samplePath.c_str(), glue_loadSample, ch);
-    gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup keyboard input...") == 0) {
-               new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup MIDI input...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
-               return;
-       }
-
-       if (strcmp(m->label(), "Setup MIDI output...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh((SampleChannel*) ch), 0);
-               return;
-       }
-
-       if (strcmp(m->label(), "Edit sample...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdEditor((SampleChannel*) ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor
-               return;
-       }
-
-       if (strcmp(m->label(), "Export sample to file...") == 0) {
-    gWindow *w = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
-      G_Conf.browserW, G_Conf.browserH, "Save sample", \
-      G_Conf.samplePath.c_str(), "", glue_saveSample, ch);
-    gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
-               return;
-       }
-
-       if (strcmp(m->label(), "Delete channel") == 0) {
-               if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
-                       return;
-               glue_deleteChannel(ch);
-               return;
-       }
-
-       if (strcmp(m->label(), "Free channel") == 0) {
-               if (ch->status == STATUS_PLAY) {
-                       if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
-                               return;
-               }
-               else if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
-                       return;
-
-               glue_freeChannel(ch);
-
-               /* delete any related subwindow */
-
-               /** FIXME - use gu_closeAllSubwindows() */
-
-               G_MainWin->delSubWindow(WID_FILE_BROWSER);
-               G_MainWin->delSubWindow(WID_ACTION_EDITOR);
-               G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
-               G_MainWin->delSubWindow(WID_FX_LIST);
-
-               return;
-       }
-
-       if (strcmp(m->label(), "Clone channel") == 0) {
-               glue_cloneChannel(ch);
-               return;
-       }
-
-       if (strcmp(m->label(), "Mute") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
-                       return;
-               G_Recorder.clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF);
-    ch->hasActions = G_Recorder.hasActions(ch->index);
-
-               if (!ch->hasActions)
-                       hideActionButton();
-
-               /* TODO - set mute=false */
-
-               gu_refreshActionEditor(); // refresh a.editor window, it could be open
-               return;
-       }
-
-       if (strcmp(m->label(), "Start/Stop") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
-                       return;
-               G_Recorder.clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN);
-    ch->hasActions = G_Recorder.hasActions(ch->index);
-
-               if (!ch->hasActions)
-                       hideActionButton();
-               gu_refreshActionEditor();  // refresh a.editor window, it could be open
-               return;
-       }
-
-       if (strcmp(m->label(), "Volume") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
-                       return;
-               G_Recorder.clearAction(ch->index, ACTION_VOLUME);
-    ch->hasActions = G_Recorder.hasActions(ch->index);
-               if (!ch->hasActions)
-                       hideActionButton();
-               gu_refreshActionEditor();  // refresh a.editor window, it could be open
-               return;
-       }
-
-       if (strcmp(m->label(), "All") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
-                       return;
-               G_Recorder.clearChan(ch->index);
-    ch->hasActions = false;
-               hideActionButton();
-               gu_refreshActionEditor(); // refresh a.editor window, it could be open
-               return;
-       }
-
-       if (strcmp(m->label(), "Edit actions...") == 0) {
-               gu_openSubWindow(G_MainWin, new gdActionEditor(ch),     WID_ACTION_EDITOR);
-               return;
-       }
+  if (m)
+    m->do_callback(this, m->user_data());
+  return;
 }
 
 
@@ -337,11 +328,11 @@ void geSampleChannel::refresh()
 
        setColorsByStatus(ch->status, ch->recStatus);
 
-       if (((SampleChannel*) ch)->wave != NULL) {
-               if (G_Mixer.recording && ch->armed)
+       if (((SampleChannel*) ch)->wave != nullptr) {
+               if (mixer::recording && ch->armed)
                        mainButton->setInputRecordMode();
-               if (G_Recorder.active) {
-                       if (G_Recorder.canRec(ch, &G_Mixer))
+               if (recorder::active) {
+                       if (recorder::canRec(ch, clock::isRunning(), mixer::recording))
                                mainButton->setActionRecordMode();
                }
                status->redraw(); // status invisible? sampleButton too (see below)
@@ -405,7 +396,7 @@ void geSampleChannel::update()
        mainButton->setKey(ch->key);
 
 #ifdef WITH_VST
-       fx->full = ch->plugins.size() > 0;
+       fx->status = ch->plugins.size() > 0;
        fx->redraw();
 #endif
 }
@@ -461,39 +452,3 @@ void geSampleChannel::resize(int X, int Y, int W, int H)
 
        packWidgets();
 }
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const char *l)
-       : geChannelButton(x, y, w, h, l) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geSampleChannelButton::handle(int e)
-{
-       int ret = gClick::handle(e);
-       switch (e) {
-               case FL_DND_ENTER:
-               case FL_DND_DRAG:
-               case FL_DND_RELEASE: {
-                       ret = 1;
-                       break;
-               }
-               case FL_PASTE: {
-      geSampleChannel *gch = (geSampleChannel*) parent();   // parent is geSampleChannel
-      SampleChannel   *ch  = (SampleChannel*) gch->ch;
-      int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
-                       if (result != SAMPLE_LOADED_OK)
-                               G_MainWin->keyboard->printChannelMessage(result);
-                       ret = 1;
-                       break;
-               }
-       }
-       return ret;
-}
index 5c52151e6fc09f50b6de80c5ecec9fb5f898d3fc..875682d45a7aee496cc423bdf1b982efa67b3e1d 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * ge_sampleChannel
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 
 #include "channel.h"
-#include "channelButton.h"
+
+
+class SampleChannel;
+class geChannelMode;
+class geButton;
 
 
 class geSampleChannel : public geChannel
@@ -49,7 +51,7 @@ private:
 
 public:
 
-       geSampleChannel(int x, int y, int w, int h, class SampleChannel *ch);
+       geSampleChannel(int x, int y, int w, int h, SampleChannel *ch);
 
        void reset   ();
        void update  ();
@@ -62,19 +64,8 @@ public:
        void showActionButton();
        void hideActionButton();
 
-       class geChannelMode *modeBox;
-       class gClick     *readActions;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class geSampleChannelButton : public geChannelButton
-{
-public:
-       geSampleChannelButton(int x, int y, int w, int h, const char *l=0);
-       int handle(int e);
+       geChannelMode *modeBox;
+       geButton            *readActions;
 };
 
 
diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp
new file mode 100644 (file)
index 0000000..781437a
--- /dev/null
@@ -0,0 +1,74 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../../../../core/const.h"
+#include "../../../../core/sampleChannel.h"
+#include "../../../../utils/string.h"
+#include "../../../../utils/fs.h"
+#include "../../../../glue/channel.h"
+#include "../../../dialogs/gd_mainWindow.h"
+#include "sampleChannel.h"
+#include "keyboard.h"
+#include "sampleChannelButton.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h,
+  const char *l)
+       : geChannelButton(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geSampleChannelButton::handle(int e)
+{
+       int ret = geButton::handle(e);
+       switch (e) {
+               case FL_DND_ENTER:
+               case FL_DND_DRAG:
+               case FL_DND_RELEASE: {
+                       ret = 1;
+                       break;
+               }
+               case FL_PASTE: {
+      geSampleChannel *gch = static_cast<geSampleChannel*>(parent());
+      SampleChannel   *ch  = static_cast<SampleChannel*>(gch->ch);
+      int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
+                       if (result != SAMPLE_LOADED_OK)
+                               G_MainWin->keyboard->printChannelMessage(result);
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h
new file mode 100644 (file)
index 0000000..4857c44
--- /dev/null
@@ -0,0 +1,44 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_SAMPLE_CHANNEL_BUTTON_H
+#define GE_SAMPLE_CHANNEL_BUTTON_H
+
+
+#include "channelButton.h"
+
+
+class geSampleChannelButton : public geChannelButton
+{
+public:
+
+  geSampleChannelButton(int x, int y, int w, int h, const char *l=0);
+       int handle(int e);
+};
+
+
+#endif
index 7e61889fd16d031dd595fcc429adf6a73015757c..098785ab0a9a9d3d4927084fffbd43ba2a416d85 100644 (file)
@@ -2,11 +2,9 @@
 *
 * Giada - Your Hardcore Loopmachine
 *
-* mainIO
-*
 * -----------------------------------------------------------------------------
 *
-* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
 *
 * This file is part of Giada - Your Hardcore Loopmachine.
 *
 #include "../../../core/pluginHost.h"
 #include "../../../glue/main.h"
 #include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
+#include "../../elems/soundMeter.h"
+#include "../../elems/basics/statusButton.h"
+#include "../../elems/basics/dial.h"
 #include "../../dialogs/gd_mainWindow.h"
 #include "../../dialogs/gd_pluginList.h"
 #include "mainIO.h"
 
 
-extern Mixer         G_Mixer;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
 geMainIO::geMainIO(int x, int y)
        : Fl_Group(x, y, 396, 20)
 {
        begin();
 
 #if defined(WITH_VST)
-       masterFxIn  = new gFxButton  (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
-       inVol               = new gDial      (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
-       inMeter     = new gSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12);
-       inToOut     = new gClick     (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm);
-       outMeter    = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12);
-       outVol            = new gDial      (outMeter->x()+outMeter->w()+4, y, 20, 20);
-       masterFxOut = new gFxButton  (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
+       masterFxIn  = new geStatusButton  (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
+       inVol               = new geDial      (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
+       inMeter     = new geSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12);
+       inToOut     = new geButton   (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm);
+       outMeter    = new geSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12);
+       outVol            = new geDial      (outMeter->x()+outMeter->w()+4, y, 20, 20);
+       masterFxOut = new geStatusButton  (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
 #else
-       inVol               = new gDial      (x+62, y, 20, 20);
-       inMeter     = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12);
-       outMeter    = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12);
-       outVol            = new gDial      (outMeter->x()+outMeter->w()+4, y, 20, 20);
+       inVol               = new geDial      (x+62, y, 20, 20);
+       inMeter     = new geSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12);
+       outMeter    = new geSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12);
+       outVol            = new geDial      (outMeter->x()+outMeter->w()+4, y, 20, 20);
 #endif
 
        end();
 
-       resizable(NULL);   // don't resize any widget
+       resizable(nullptr);   // don't resize any widget
 
        outVol->callback(cb_outVol, (void*)this);
-       outVol->value(G_Mixer.outVol);
+       outVol->value(mixer::outVol);
        inVol->callback(cb_inVol, (void*)this);
-       inVol->value(G_Mixer.inVol);
+       inVol->value(mixer::inVol);
 
 #ifdef WITH_VST
        masterFxOut->callback(cb_masterFxOut, (void*)this);
@@ -117,17 +119,17 @@ void geMainIO::__cb_inVol()
 #ifdef WITH_VST
 void geMainIO::__cb_masterFxOut()
 {
-       gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST);
+       gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::MASTER_OUT), WID_FX_LIST);
 }
 
 void geMainIO::__cb_masterFxIn()
 {
-       gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST);
+       gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::MASTER_IN), WID_FX_LIST);
 }
 
 void geMainIO::__cb_inToOut()
 {
-       G_Mixer.inToOut = inToOut->value();
+       mixer::inToOut = inToOut->value();
 }
 #endif
 
@@ -154,14 +156,14 @@ void geMainIO::setInVol(float v)
 
 void geMainIO::setMasterFxOutFull(bool v)
 {
-  masterFxOut->full = v;
+  masterFxOut->status = v;
   masterFxOut->redraw();
 }
 
 
 void geMainIO::setMasterFxInFull(bool v)
 {
-  masterFxIn->full = v;
+  masterFxIn->status = v;
   masterFxIn->redraw();
 }
 
@@ -173,8 +175,8 @@ void geMainIO::setMasterFxInFull(bool v)
 
 void geMainIO::refresh()
 {
-       outMeter->mixerPeak = G_Mixer.peakOut;
-       inMeter->mixerPeak  = G_Mixer.peakIn;
+       outMeter->mixerPeak = mixer::peakOut;
+       inMeter->mixerPeak  = mixer::peakIn;
        outMeter->redraw();
        inMeter->redraw();
 }
index f2752111342280ebdbfe6c4fcacbb624f50fc0e3..e65579c674bf40c9376b8049b1799fa76322e6ec 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainIO
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 
 #include <FL/Fl_Group.H>
 
+class geSoundMeter;
+class geDial;
+#ifdef WITH_VST
+class geStatusButton;
+class geButton;
+#endif
 
 class geMainIO : public Fl_Group
 {
 private:
 
-       class gSoundMeter *outMeter;
-       class gSoundMeter *inMeter;
-       class gDial                             *outVol;
-       class gDial                             *inVol;
+       geSoundMeter *outMeter;
+       geSoundMeter *inMeter;
+       geDial        *outVol;
+       geDial        *inVol;
 #ifdef WITH_VST
-       class gFxButton         *masterFxOut;
-       class gFxButton         *masterFxIn;
-       class gClick      *inToOut;
+  geStatusButton *masterFxOut;
+  geStatusButton *masterFxIn;
+  geButton       *inToOut;
 #endif
 
        static void cb_outVol     (Fl_Widget *v, void *p);
index 5c9a0f4cf77668f24a34ea3bb38c154a8d3b344b..9b0dac6aac8608c7ebec9cc08db0cee3aff1617e 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainMenu
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <FL/Fl_Menu_Button.H>
 #include "../../../core/const.h"
 #include "../../../core/mixer.h"
+#include "../../../core/mixerHandler.h"
 #include "../../../core/conf.h"
 #include "../../../core/patch.h"
 #include "../../../core/channel.h"
 #include "../../../utils/gui.h"
 #include "../../../glue/storage.h"
 #include "../../../glue/main.h"
-#include "../../elems/ge_mixed.h"
 #include "../../elems/basics/boxtypes.h"
+#include "../../elems/basics/button.h"
 #include "../../dialogs/gd_mainWindow.h"
 #include "../../dialogs/gd_about.h"
 #include "../../dialogs/gd_config.h"
-#include "../../dialogs/gd_browser.h"
 #include "../../dialogs/gd_warnings.h"
+#include "../../dialogs/browser/browserLoad.h"
+#include "../../dialogs/browser/browserSave.h"
 #include "../../dialogs/midiIO/midiInputMaster.h"
 #include "keyboard/keyboard.h"
 #include "mainMenu.h"
 
 
-extern Mixer         G_Mixer;
-extern Patch         G_Patch;
-extern Conf          G_Conf;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
 geMainMenu::geMainMenu(int x, int y)
        : Fl_Group(x, y, 300, 20)
 {
        begin();
 
-       file   = new gClick(x, y, 70, 21, "file");
-       edit   = new gClick(file->x()+file->w()+4,  y, 70, 21, "edit");
-       config = new gClick(edit->x()+edit->w()+4, y, 70, 21, "config");
-       about    = new gClick(config->x()+config->w()+4, y, 70, 21, "about");
+       file   = new geButton(x, y, 70, 21, "file");
+       edit   = new geButton(file->x()+file->w()+4,  y, 70, 21, "edit");
+       config = new geButton(edit->x()+edit->w()+4, y, 70, 21, "config");
+       about    = new geButton(config->x()+config->w()+4, y, 70, 21, "about");
 
        end();
 
-       resizable(NULL);   // don't resize any widget
+       resizable(nullptr);   // don't resize any widget
 
        about->callback(cb_about, (void*)this);
        file->callback(cb_file, (void*)this);
@@ -127,26 +128,26 @@ void geMainMenu::__cb_file()
        if (!m) return;
 
        if (strcmp(m->label(), "Open patch or project...") == 0) {
-               gWindow *childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
-                               G_Conf.browserW, G_Conf.browserH, "Load patch or project",
-                               G_Conf.patchPath, glue_loadPatch, NULL);
+               gdWindow *childWin = new gdBrowserLoad(conf::browserX, conf::browserY,
+                               conf::browserW, conf::browserH, "Load patch or project",
+                               conf::patchPath, glue_loadPatch, nullptr);
                gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
        if (strcmp(m->label(), "Save patch...") == 0) {
-               if (G_Mixer.hasLogicalSamples() || G_Mixer.hasEditedSamples())
+               if (mh::hasLogicalSamples() || mh::hasEditedSamples())
                        if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples."))
                                return;
-               gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
-                               G_Conf.browserW, G_Conf.browserH, "Save patch",
-                               G_Conf.patchPath, G_Patch.name, glue_savePatch, NULL);
+               gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
+                               conf::browserW, conf::browserH, "Save patch",
+                               conf::patchPath, patch::name, glue_savePatch, nullptr);
                gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
        if (strcmp(m->label(), "Save project...") == 0) {
-               gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
-                               G_Conf.browserW, G_Conf.browserH, "Save project",
-                               G_Conf.patchPath, G_Patch.name, glue_saveProject, NULL);
+               gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
+                               conf::browserW, conf::browserH, "Save project",
+                               conf::patchPath, patch::name, glue_saveProject, nullptr);
                gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
@@ -176,14 +177,14 @@ void geMainMenu::__cb_edit()
 
        menu[1].deactivate();
 
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++)
-               if (G_Mixer.channels.at(i)->hasActions) {
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+               if (mixer::channels.at(i)->hasActions) {
                        menu[1].activate();
                        break;
                }
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++)
-               if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
-                       if (((SampleChannel*)G_Mixer.channels.at(i))->wave != NULL) {
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+               if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
+                       if (((SampleChannel*)mixer::channels.at(i))->wave != nullptr) {
                                menu[0].activate();
                                break;
                        }
index a8e6f4f44026383080f1e88101d349c9f527ec99..879e8f1392dca251f85ae7ddd4aaafa0987f7599 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainMenu
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Group.H>
 
 
+class geButton;
+
+
 class geMainMenu : public Fl_Group
 {
 private:
 
-       class gClick *file;
-       class gClick *edit;
-       class   gClick *config;
-       class gClick *about;
+  geButton *file;
+  geButton *edit;
+  geButton *config;
+  geButton *about;
 
        static void cb_about (Fl_Widget *v, void *p);
        static void cb_config(Fl_Widget *v, void *p);
index e1c0d94ea88d7432d28e6a88377bb92a2e898a8b..334d6c850418f5376b872cfb15b1e10c7485061e 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainTimer
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include "../../../core/const.h"
 #include "../../../core/mixer.h"
 #include "../../../core/graphics.h"
+#include "../../../core/clock.h"
 #include "../../../glue/main.h"
 #include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/choice.h"
 #include "../../dialogs/gd_mainWindow.h"
 #include "../../dialogs/gd_bpmInput.h"
 #include "../../dialogs/gd_beatsInput.h"
 #include "mainTimer.h"
 
 
-extern Mixer         G_Mixer;
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada::m;
+
+
 geMainTimer::geMainTimer(int x, int y)
        : Fl_Group(x, y, 180, 20)
 {
        begin();
 
-       quantizer  = new gChoice(x, y, 40, 20, "", false);
-       bpm        = new gClick (quantizer->x()+quantizer->w()+4,  y, 40, 20);
-       meter      = new gClick (bpm->x()+bpm->w()+8,  y, 40, 20, "4/1");
-       multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
-       divider    = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
+       quantizer  = new geChoice(x, y, 40, 20, "", false);
+       bpm        = new geButton (quantizer->x()+quantizer->w()+4,  y, 40, 20);
+       meter      = new geButton (bpm->x()+bpm->w()+8,  y, 40, 20, "4/1");
+       multiplier = new geButton (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
+       divider    = new geButton (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
 
        end();
 
-       resizable(NULL);   // don't resize any widget
+       resizable(nullptr);   // don't resize any widget
 
-       char buf[6]; snprintf(buf, 6, "%f", G_Mixer.bpm);
+       char buf[6]; snprintf(buf, 6, "%f", clock::getBpm());
        bpm->copy_label(buf);
 
        bpm->callback(cb_bpm, (void*)this);
index 8be1d82c623f432f8cad8883a266fea3e23864bf..3db015193c9488789fbe336ccb056a8153623c29 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainTimer
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Group.H>
 
 
+class geButton;
+class geChoice;
+
+
 class geMainTimer : public Fl_Group
 {
 private:
 
-       class gClick   *bpm;
-       class gClick   *meter;
-       class gChoice  *quantizer;
-       class gClick   *multiplier;
-       class gClick   *divider;
+       geButton *bpm;
+       geButton *meter;
+       geChoice  *quantizer;
+       geButton *multiplier;
+       geButton *divider;
 
        static void cb_bpm       (Fl_Widget *v, void *p);
        static void cb_meter     (Fl_Widget *v, void *p);
index 9b54c22330072b8042a298530e4461f22b717807..354fb06a1294e07e88ff5a28fd3f25003b9cbe29 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainTransport
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -28,9 +26,9 @@
 
 
 #include "../../../core/graphics.h"
-#include "../../../glue/main.h"
+#include "../../../glue/transport.h"
 #include "../../../glue/io.h"
-#include "../ge_mixed.h"
+#include "../../elems/basics/button.h"
 #include "mainTransport.h"
 
 
@@ -39,15 +37,15 @@ geMainTransport::geMainTransport(int x, int y)
 {
        begin();
 
-       rewind    = new gClick(x,  y, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
-       play      = new gClick(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm);
-       recAction = new gClick(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm);
-       recInput  = new gClick(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm);
-       metronome = new gClick(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
+       rewind    = new geButton(x,  y, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+       play      = new geButton(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm);
+       recAction = new geButton(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm);
+       recInput  = new geButton(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm);
+       metronome = new geButton(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
 
        end();
 
-       resizable(NULL);   // don't resize any widget
+       resizable(nullptr);   // don't resize any widget
 
        rewind->callback(cb_rewind, (void*)this);
 
@@ -80,7 +78,7 @@ void geMainTransport::cb_metronome(Fl_Widget *v, void *p) { ((geMainTransport*)p
 
 void geMainTransport::__cb_rewind()
 {
-       glue_rewindSeq();
+       glue_rewindSeq(true);
 }
 
 
index 57b83e32300c16abe40cea24e115349747fb4f4a..26821efdee501533538416ce53c61a1372bd5b20 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * mainTransport
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <FL/Fl_Group.H>
 
 
+class geButton;
+
+
 class geMainTransport : public Fl_Group
 {
 private:
 
-       class gClick *rewind;
-       class gClick *play;
-       class gClick *recAction;
-       class gClick *recInput;
-       class gClick *metronome;
+       geButton *rewind;
+       geButton *play;
+       geButton *recAction;
+       geButton *recInput;
+       geButton *metronome;
 
        static void cb_rewind   (Fl_Widget *v, void *p);
        static void cb_play     (Fl_Widget *v, void *p);
index f63b2086a3d93e56ac1cac100e7e053afe445f7a..5da78b4917bda50415e5bc9ab0a33b4bb74dd239 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiLearner
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include "ge_mixed.h"
 #include "basics/boxtypes.h"
+#include "basics/button.h"
+#include "basics/box.h"
 #include "midiLearner.h"
 
 
-extern KernelMidi G_KernelMidi;
+using namespace giada::m;
 
 
 geMidiLearner::geMidiLearner(int X, int Y, int W, const char *l,
-  KernelMidi::cb_midiLearn *cb, uint32_t *param)
+  kernelMidi::cb_midiLearn *cb, uint32_t *param)
        : Fl_Group(X, Y, W, 20),
                callback(cb),
                param   (param)
 {
        begin();
-       text   = new gBox(x(), y(), 156, 20, l);
-       value  = new gClick(text->x()+text->w()+4, y(), 80, 20);
-       button = new gButton(value->x()+value->w()+4, y(), 40, 20, "learn");
+       text   = new geBox(x(), y(), 156, 20, l);
+       value  = new geButton(text->x()+text->w()+4, y(), 80, 20);
+       button = new geButton(value->x()+value->w()+4, y(), 40, 20, "learn");
        end();
 
        text->box(G_CUSTOM_BORDER_BOX);
@@ -103,8 +102,8 @@ void geMidiLearner::__cb_button()
        if (button->value() == 1) {
                cbData.window  = (gdMidiInput*) parent();  // parent = gdMidiInput
                cbData.learner = this;
-               G_KernelMidi.startMidiLearn(callback, (void*)&cbData);
+               kernelMidi::startMidiLearn(callback, (void*)&cbData);
        }
        else
-               G_KernelMidi.stopMidiLearn();
+               kernelMidi::stopMidiLearn();
 }
index 9d5b0a06c1c1201e43ca4848a60a52ae37f6155c..9b7e89a28854dcce5a2b556d23732d7b1039ec51 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiLearner
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef GE_LEARNER_H
-#define GE_LEARNER_H
+#ifndef GE_MIDI_LEARNER_H
+#define GE_MIDI_LEARNER_H
 
 
 #include <FL/Fl_Group.H>
 #include "../../core/kernelMidi.h"
 
 
-extern KernelMidi G_KernelMidi;
+class gdMidiInput;
+class geMidiLearner;
+class geBox;
+class geButton;
 
 
 class geMidiLearner : public Fl_Group
@@ -47,11 +48,11 @@ private:
         * uint32_t msg - MIDI message
         * void   *data - extra data */
 
-       KernelMidi::cb_midiLearn *callback;
+       giada::m::kernelMidi::cb_midiLearn *callback;
 
-       class gBox    *text;
-       class gClick  *value;
-       class gButton *button;
+       geBox     *text;
+       geButton *value;
+       geButton *button;
 
        static void cb_button(Fl_Widget *v, void *p);
        static void cb_value (Fl_Widget *v, void *p);
@@ -65,8 +66,8 @@ public:
 
   struct cbData_t
   {
-               class gdMidiInput   *window;
-               class geMidiLearner *learner;
+               gdMidiInput   *window;
+               geMidiLearner *learner;
        } cbData;
 
        /* param
@@ -74,8 +75,8 @@ public:
 
        uint32_t *param;
 
-       geMidiLearner(int x, int y, int w, const char *l, KernelMidi::cb_midiLearn *cb,
-    uint32_t *param);
+       geMidiLearner(int x, int y, int w, const char *l, 
+               giada::m::kernelMidi::cb_midiLearn *cb, uint32_t *param);
 
        void updateValue();
 };
diff --git a/src/gui/elems/muteEditor.cpp b/src/gui/elems/muteEditor.cpp
deleted file mode 100644 (file)
index 1c9ae3b..0000000
+++ /dev/null
@@ -1,415 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../core/recorder.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../glue/main.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "muteEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-extern Mixer         G_Mixer;
-extern Recorder      G_Recorder;
-
-
-geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
- : geBaseActionEditor(x, y, 200, 80, pParent),
-   draggedPoint      (-1),
-   selectedPoint     (-1)
-{
-       size(pParent->totalWidth, h());
-       extractPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::draw()
-{
-       baseDraw();
-
-       /* print label */
-
-       fl_color(COLOR_BG_1);
-       fl_font(FL_HELVETICA, 12);
-       fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
-       /* draw "on" and "off" labels. Must stay in background */
-
-       fl_color(COLOR_BG_1);
-       fl_font(FL_HELVETICA, 9);
-       fl_draw("on",  x()+4, y(),        w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-       fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-
-       /* draw on-off points. On = higher rect, off = lower rect. It always
-        * starts with a note_off */
-
-       fl_color(COLOR_BG_2);
-
-       int pxOld = x()+1;
-       int pxNew = 0;
-       int py    = y()+h()-5;
-       int pyDot = py-6;
-
-       for (unsigned i=0; i<points.size(); i++) {
-
-               /* next px */
-
-               pxNew = points.at(i).x+x();
-
-               /* draw line from pxOld to pxNew.
-                * i % 2 == 0: first point, mute_on
-                * i % 2 != 0: second point, mute_off */
-
-               fl_line(pxOld, py, pxNew, py);
-               pxOld = pxNew;
-
-               py = i % 2 == 0 ? y()+4 : y()+h()-5;
-
-               /* draw dots (handles) */
-
-               fl_line(pxNew, y()+h()-5, pxNew, y()+4);
-
-               if (selectedPoint == (int) i) {
-                       fl_color(COLOR_BD_1);
-                       fl_rectf(pxNew-3, pyDot, 7, 7);
-                       fl_color(COLOR_BG_2);
-               }
-               else
-                       fl_rectf(pxNew-3, pyDot, 7, 7);
-       }
-
-       /* last section */
-
-       py = y()+h()-5;
-       fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::extractPoints()
-{
-       points.clear();
-
-       /* actions are already sorted by G_Recorder.sortActions() */
-
-       for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
-               for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-                       if (G_Recorder.global.at(i).at(j)->chan == pParent->chan->index) {
-                               if (G_Recorder.global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) {
-                                       point p;
-                                       p.frame = G_Recorder.frames.at(i);
-                                       p.type  = G_Recorder.global.at(i).at(j)->type;
-                                       p.x     = p.frame / pParent->zoom;
-                                       points.push_back(p);
-                                       //gu_log("[geMuteEditor::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
-                               }
-                       }
-               }
-       }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::updateActions() {
-       for (unsigned i=0; i<points.size(); i++)
-               points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::handle(int e) {
-
-       int ret = 0;
-       int mouseX = Fl::event_x()-x();
-
-       switch (e) {
-
-               case FL_ENTER: {
-                       ret = 1;
-                       break;
-               }
-
-               case FL_MOVE: {
-                       selectedPoint = getSelectedPoint();
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_LEAVE: {
-                       draggedPoint  = -1;
-                       selectedPoint = -1;
-                       redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_PUSH: {
-
-                       /* left click on point: drag
-                        * right click on point: delete
-                        * left click on void: add */
-
-                       if (Fl::event_button1())  {
-
-                               if (selectedPoint != -1) {
-                                       draggedPoint   = selectedPoint;
-                                       previousXPoint = points.at(selectedPoint).x;
-                               }
-                               else {
-
-                                       /* click on the grey area leads to nowhere */
-
-                                       if (mouseX > pParent->coverX) {
-                                               ret = 1;
-                                               break;
-                                       }
-
-                                       /* click in the middle of a long mute_on (between two points): new actions
-                                        * must be added in reverse: first mute_off then mute_on. Let's find the
-                                        * next point from here. */
-
-                                       unsigned nextPoint = points.size();
-                                       for (unsigned i=0; i<points.size(); i++) {
-                                               if (mouseX < points.at(i).x) {
-                                                       nextPoint = i;
-                                                       break;
-                                               }
-                                       }
-
-                                       /* next point odd = mute_on [click here] mute_off
-                                        * next point even = mute_off [click here] mute_on */
-
-                                       int frame_a = mouseX * pParent->zoom;
-                                       int frame_b = frame_a+2048;
-
-                                       if (pParent->gridTool->isOn()) {
-                                               frame_a = pParent->gridTool->getSnapFrame(mouseX);
-                                               frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
-
-                                               /* with snap=on a point can fall onto another */
-
-                                               if (pointCollides(frame_a) || pointCollides(frame_b)) {
-                                                       ret = 1;
-                                                       break;
-                                               }
-                                       }
-
-                                       /* ensure frame parity */
-
-                                       if (frame_a % 2 != 0) frame_a++;
-                                       if (frame_b % 2 != 0) frame_b++;
-
-                                       /* avoid overflow: frame_b must be within the sequencer range. In that
-                                        * case shift the ON-OFF block */
-
-                                       if (frame_b >= G_Mixer.totalFrames) {
-                                               frame_b = G_Mixer.totalFrames;
-                                               frame_a = frame_b-2048;
-                                       }
-
-                                       if (nextPoint % 2 != 0) {
-                                               G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_a);
-                                               G_Recorder.rec(pParent->chan->index, ACTION_MUTEON,  frame_b);
-                                       }
-                                       else {
-                                               G_Recorder.rec(pParent->chan->index, ACTION_MUTEON,  frame_a);
-                                               G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_b);
-                                       }
-          pParent->chan->hasActions = true;
-                                       G_Recorder.sortActions();
-
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
-                                       extractPoints();
-                                       redraw();
-                               }
-                       }
-                       else {
-
-                               /* delete points pair */
-
-                               if (selectedPoint != -1) {
-
-                                       unsigned a;
-                                       unsigned b;
-
-                                       if (points.at(selectedPoint).type == ACTION_MUTEOFF) {
-                                               a = selectedPoint-1;
-                                               b = selectedPoint;
-                                       }
-                                       else {
-                                               a = selectedPoint;
-                                               b = selectedPoint+1;
-                                       }
-
-                                       //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
-                                       //              a, b, points.at(a).frame, points.at(b).frame);
-
-                                       G_Recorder.deleteAction(pParent->chan->index, points.at(a).frame,
-          points.at(a).type, false, &G_Mixer.mutex_recs); // false = don't check vals
-                                       G_Recorder.deleteAction(pParent->chan->index,   points.at(b).frame,
-          points.at(b).type, false, &G_Mixer.mutex_recs); // false = don't check vals
-          pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
-          G_Recorder.sortActions();
-
-                                       G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
-                                       extractPoints();
-                                       redraw();
-                               }
-                       }
-                       ret = 1;
-                       break;
-               }
-
-               case FL_RELEASE: {
-
-                       if (draggedPoint != -1) {
-
-                               if (points.at(draggedPoint).x == previousXPoint) {
-                                       //gu_log("nothing to do\n");
-                               }
-                               else {
-
-                                       int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
-                                       G_Recorder.deleteAction(pParent->chan->index,
-            points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
-            &G_Mixer.mutex_recs);  // don't check values
-          pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
-                                       G_Recorder.rec(
-                                                       pParent->chan->index,
-                                                       points.at(draggedPoint).type,
-                                                       newFrame);
-
-          pParent->chan->hasActions = true;
-                                       G_Recorder.sortActions();
-
-                                       points.at(draggedPoint).frame = newFrame;
-                               }
-                       }
-                       draggedPoint  = -1;
-                       selectedPoint = -1;
-
-                       ret = 1;
-                       break;
-               }
-
-               case FL_DRAG: {
-
-                       if (draggedPoint != -1) {
-
-                               /* constrain the point between two ends (leftBorder-point,
-                                * point-point, point-rightBorder) */
-
-                               int prevPoint;
-                               int nextPoint;
-
-                               if (draggedPoint == 0) {
-                                       prevPoint = 0;
-                                       nextPoint = points.at(draggedPoint+1).x - 1;
-                                       if (pParent->gridTool->isOn())
-                                               nextPoint -= pParent->gridTool->getCellSize();
-                               }
-                               else
-                               if ((unsigned) draggedPoint == points.size()-1) {
-                                       prevPoint = points.at(draggedPoint-1).x + 1;
-                                       nextPoint = pParent->coverX-x();
-                                       if (pParent->gridTool->isOn())
-                                               prevPoint += pParent->gridTool->getCellSize();
-                               }
-                               else {
-                                       prevPoint = points.at(draggedPoint-1).x + 1;
-                                       nextPoint = points.at(draggedPoint+1).x - 1;
-                                       if (pParent->gridTool->isOn()) {
-                                               prevPoint += pParent->gridTool->getCellSize();
-                                               nextPoint -= pParent->gridTool->getCellSize();
-                                       }
-                               }
-
-                               if (mouseX <= prevPoint)
-                                       points.at(draggedPoint).x = prevPoint;
-                               else
-                               if (mouseX >= nextPoint)
-                                       points.at(draggedPoint).x = nextPoint;
-                               else
-                               if (pParent->gridTool->isOn())
-                                       points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
-                               else
-                                       points.at(draggedPoint).x = mouseX;
-
-                               redraw();
-                       }
-                       ret = 1;
-                       break;
-               }
-       }
-
-
-       return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool geMuteEditor::pointCollides(int frame) {
-       for (unsigned i=0; i<points.size(); i++)
-               if (frame == points.at(i).frame)
-                       return true;
-       return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::getSelectedPoint() {
-
-       /* point is a 7x7 dot */
-
-       for (unsigned i=0; i<points.size(); i++) {
-               if (Fl::event_x() >= points.at(i).x+x()-3 &&
-                               Fl::event_x() <= points.at(i).x+x()+3)
-               return i;
-       }
-       return -1;
-}
diff --git a/src/gui/elems/muteEditor.h b/src/gui/elems/muteEditor.h
deleted file mode 100644 (file)
index 620f82a..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_MUTECHANNEL_H
-#define GE_MUTECHANNEL_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Widget.H>
-#include <FL/fl_draw.H>
-#include "../../utils/fs.h"
-#include "baseActionEditor.h"
-
-
-using std::vector;
-
-
-class geMuteEditor : public geBaseActionEditor
-{
-private:
-
-       /* point
-        * a single dot in the graph. */
-
-       struct point
-       {
-               int  frame;
-               char type;
-               int  x;
-       };
-
-       /* points
-        * array of on/off points, in frames */
-
-       vector<point> points;
-
-       /* draggedPoint
-        * which point we are dragging? */
-
-       int draggedPoint;
-
-       /* selectedPoint
-        * which point we are selecting? */
-
-       int selectedPoint;
-
-       /* previousXPoint
-        * x coordinate of point at time t-1. Used to check effective shifts */
-
-       int previousXPoint;
-
-       /* extractPoints
-        * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
-        * interessanti mute_on o mute_off. Li mette poi nel vector points. */
-       void extractPoints();
-
-       /* getSelectedPoint
-        * ritorna l'indice di points[] in base al punto selezionato (quello
-        * con il mouse hover). Ritorna -1 se non trova niente. */
-       int getSelectedPoint();
-
-       /* pointCollides
-        * true if a point collides with another. Used while adding new points
-        * with snap active.*/
-
-       bool pointCollides(int frame);
-
-public:
-
-       geMuteEditor(int x, int y, class gdActionEditor *pParent);
-       void draw();
-       int  handle(int e);
-
-       /* updateActions
-        * calculates new points affected by the zoom. Call this one after
-        * each zoom update. */
-
-       void updateActions();
-};
-
-#endif
diff --git a/src/gui/elems/noteEditor.cpp b/src/gui/elems/noteEditor.cpp
deleted file mode 100644 (file)
index 19940a2..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 <FL/fl_draw.H>
-#include "../../core/const.h"
-#include "../../core/conf.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "pianoItem.h"
-#include "pianoRoll.h"
-#include "noteEditor.h"
-
-
-extern Conf G_Conf;
-
-
-geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent)
-  : Fl_Scroll(x, y, 200, 422),
-    pParent  (pParent)
-{
-       size(pParent->totalWidth, G_Conf.pianoRollH);
-       pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geNoteEditor::~geNoteEditor()
-{
-       clear();
-       G_Conf.pianoRollH = h();
-       G_Conf.pianoRollY = pianoRoll->y();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geNoteEditor::updateActions()
-{
-       pianoRoll->updateActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geNoteEditor::draw()
-{
-       pianoRoll->size(this->w(), pianoRoll->h());  /// <--- not optimal
-
-       /* clear background */
-
-       fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
-       /* clip pianoRoll to pianoRollContainer size */
-
-       fl_push_clip(x(), y(), w(), h());
-       draw_child(*pianoRoll);
-       fl_pop_clip();
-
-       fl_color(COLOR_BD_0);
-       fl_line_style(0);
-       fl_rect(x(), y(), pParent->totalWidth, h());
-}
diff --git a/src/gui/elems/noteEditor.h b/src/gui/elems/noteEditor.h
deleted file mode 100644 (file)
index 0ca600f..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_NOTE_EDITOR_H
-#define GE_NOTE_EDITOR_H
-
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include "../../core/recorder.h"
-
-
-class geNoteEditor : public Fl_Scroll
-{
-private:
-
-       class gdActionEditor *pParent;
-       class gePianoRoll    *pianoRoll;
-
-public:
-
-       geNoteEditor(int x, int y, class gdActionEditor *parent);
-       ~geNoteEditor();
-       void draw();
-       void updateActions();
-};
-
-
-#endif
diff --git a/src/gui/elems/pianoItem.cpp b/src/gui/elems/pianoItem.cpp
deleted file mode 100644 (file)
index c6449cb..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../core/kernelMidi.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../core/midiChannel.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "noteEditor.h"
-#include "pianoRoll.h"
-#include "pianoItem.h"
-
-
-extern KernelMidi G_KernelMidi;
-extern Mixer      G_Mixer;
-extern Recorder          G_Recorder;
-
-
-gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, Recorder::action *_a,
-  Recorder::action *_b, gdActionEditor *pParent)
-       : Fl_Box  (X, Y, MIN_WIDTH, gePianoRoll::CELL_H),
-         a       (_a),
-         b       (_b),
-               pParent (pParent),
-               selected(false),
-               event_a (0x00),
-               event_b (0x00),
-               changed (false)
-{
-       /* a is a pointer: action exists, needs to be displayed */
-
-       if (a) {
-               note    = G_KernelMidi.getB2(a->iValue);
-               frame_a = a->frame;
-               frame_b = b->frame;
-               event_a = a->iValue;
-               event_b = b->iValue;
-               int newX = rel_x + (frame_a / pParent->zoom);
-               int newY = rel_y + getY(note);
-               int newW = (frame_b - frame_a) / pParent->zoom;
-               resize(newX, newY, newW, h());
-       }
-
-       /* a is null: action needs to be recorded from scratch */
-
-       else {
-               note    = getNote(rel_y);
-               frame_a = rel_x * pParent->zoom;
-               frame_b = (rel_x + 20) * pParent->zoom;
-               record();
-               size((frame_b - frame_a) / pParent->zoom, h());
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gePianoItem::overlap()
-{
-       /* when 2 segments overlap?
-        * start = the highest value between the two starting points
-        * end   = the lowest value between the two ending points
-        * if start < end then there's an overlap of end-start pixels. */
-
-       geNoteEditor *pPiano = (geNoteEditor*) parent();
-
-       for (int i=0; i<pPiano->children(); i++) {
-
-               gePianoItem *pItem = (gePianoItem*) pPiano->child(i);
-
-               /* don't check against itself and with different y positions */
-
-               if (pItem == this || pItem->y() != y())
-                       continue;
-
-               int start = pItem->x() >= x() ? pItem->x() : x();
-               int end   = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
-               if (start < end)
-                       return true;
-       }
-
-       return false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::draw()
-{
-       int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH;
-       fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::record()
-{
-       /* avoid frame overflow */
-
-       int overflow = frame_b - G_Mixer.totalFrames;
-       if (overflow > 0) {
-               frame_b -= overflow;
-               frame_a -= overflow;
-       }
-
-       event_a |= (MIDI_NOTE_ON);
-       event_a |= (note << 16);   // note value
-       event_a |= (MIDI_VELOCITY);
-       event_a |= (0x00);
-
-       event_b |= (MIDI_NOTE_OFF);
-       event_b |= (note << 16);   // note value
-       event_b |= (MIDI_VELOCITY);
-       event_b |= (0x00);
-
-       G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a);
-       G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b);
-  pParent->chan->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::remove()
-{
-       G_Recorder.deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true,
-    &G_Mixer.mutex_recs, event_a, 0.0);
-       G_Recorder.deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true,
-    &G_Mixer.mutex_recs, event_b, 0.0);
-
-       /* send a note-off in case we are deleting it in a middle of a key_on
-        * key_off sequence. */
-
-       ((MidiChannel*) pParent->chan)->sendMidi(event_b);
-
-  ((gePianoRoll*) parent())->cursorOnItem = false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::handle(int e)
-{
-       int ret = 0;
-
-       switch (e) {
-
-               case FL_ENTER: {
-      ((gePianoRoll*) parent())->cursorOnItem = true;
-                       selected = true;
-                       ret = 1;
-                       redraw();
-                       break;
-               }
-
-               case FL_LEAVE: {
-                       fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-      ((gePianoRoll*) parent())->cursorOnItem = false;
-                       selected = false;
-                       ret = 1;
-                       redraw();
-                       break;
-               }
-
-               case FL_MOVE: {
-                       onLeftEdge  = false;
-                       onRightEdge = false;
-
-                       if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
-                               onLeftEdge = true;
-                               fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-                       }
-                       else
-                       if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
-                               onRightEdge = true;
-                               fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
-                       }
-                       else
-                               fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
-                       ret = 1;
-                       break;
-               }
-
-               case FL_PUSH: {
-
-                       push_x = Fl::event_x() - x();
-                       old_x  = x();
-                       old_w  = w();
-
-                       if (Fl::event_button3()) {
-                               fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-                               remove();
-                               hide();   // for Windows
-                               Fl::delete_widget(this);
-                               ((geNoteEditor*)parent())->redraw();
-                       }
-                       ret = 1;
-                       break;
-               }
-
-               case FL_DRAG: {
-
-                       changed = true;
-
-                       geNoteEditor *pr = (geNoteEditor*) parent();
-                       int coverX     = pParent->coverX + pr->x(); // relative coverX
-                       int nx, ny, nw;
-
-                       if (onLeftEdge) {
-                               nx = Fl::event_x();
-                               ny = y();
-                               nw = x()-Fl::event_x()+w();
-                               if (nx < pr->x()) {
-                                       nx = pr->x();
-                                       nw = w()+x()-pr->x();
-                               }
-                               else
-                               if (nx > x()+w()-MIN_WIDTH) {
-                                       nx = x()+w()-MIN_WIDTH;
-                                       nw = MIN_WIDTH;
-                               }
-                               resize(nx, ny, nw, h());
-                       }
-                       else
-                       if (onRightEdge) {
-                               nw = Fl::event_x()-x();
-                               if (Fl::event_x() < x()+MIN_WIDTH)
-                                       nw = MIN_WIDTH;
-                               else
-                               if (Fl::event_x() > coverX)
-                                       nw = coverX-x();
-                               size(nw, h());
-                       }
-                       else {
-                               nx = Fl::event_x() - push_x;
-                               if (nx < pr->x()+1)
-                                       nx = pr->x()+1;
-                               else
-                               if (nx+w() > coverX)
-                                       nx = coverX-w();
-
-                               /* snapping */
-
-                               if (pParent->gridTool->isOn())
-                                       nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
-
-                               position(nx, y());
-                       }
-
-                       /* update screen */
-
-                       redraw();
-                       ((geNoteEditor*)parent())->redraw();
-                       ret = 1;
-                       break;
-               }
-
-               case FL_RELEASE: {
-
-                       /* delete & record the action, only if it doesn't overlap with
-                        * another one */
-
-                       if (overlap()) {
-                               resize(old_x, y(), old_w, h());
-                               redraw();
-                       }
-                       else
-                       if (changed) {
-                               remove();
-                               note    = getNote(getRelY());
-                               frame_a = getRelX() * pParent->zoom;
-                               frame_b = (getRelX()+w()) * pParent->zoom;
-                               record();
-                               changed = false;
-                       }
-
-                       ((geNoteEditor*)parent())->redraw();
-
-                       ret = 1;
-                       break;
-               }
-       }
-       return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getNote(int rel_y)
-{
-  return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getRelY()
-{
-  return y() - parent()->y();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getRelX()
-{
-  return x() - parent()->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getY(int note)
-{
-  return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
-}
diff --git a/src/gui/elems/pianoItem.h b/src/gui/elems/pianoItem.h
deleted file mode 100644 (file)
index 0aa4b2b..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_PIANO_ITEM_H
-#define GE_PIANO_ITEM_H
-
-
-#include <FL/Fl_Box.H>
-#include "../../core/recorder.h"
-
-
-class gePianoItem : public Fl_Box
-{
-private:
-
-       /* getRelX/Y
-        * return x/y point of this item, relative to piano roll (and not to
-        * entire screen) */
-
-       int getRelY();
-       int getRelX();
-
-       /* getNote
-        * from a relative_y return the real MIDI note, range 0-127. 15 is
-        * the hardcoded value for note height in pixels */
-
-       int getNote(int rel_y);
-
-       /* getY
-        * from a note, return the y position on piano roll */
-
-       int getY(int note);
-
-       /* overlap
-        * check if this item don't overlap with another one. */
-
-       bool overlap();
-
-       struct Recorder::action *a;
-       struct Recorder::action *b;
-       class gdActionEditor *pParent;
-
-       bool selected;
-       int  push_x;
-
-       /* MIDI note, start frame, end frame - Used only if it's a newly added
-        * action */ /** FIXME - is it true? */
-
-       int  note;
-       int  frame_a;
-       int  frame_b;
-
-       /* event - bitmasked MIDI events, generated by record() or by ctor if
-        * not newly added action */
-
-       int event_a;
-       int event_b;
-
-       /* changed - if Item has been moved or resized: re-recording needed */
-
-       bool changed;
-
-       /* onLeft,RightEdge - if cursor is on a widget's edge */
-
-       bool onLeftEdge;
-       bool onRightEdge;
-
-       /* old_x, old_w - store previous width and position while dragging
-        * and moving, in order to restore it if overlap */
-
-       int old_x, old_w;
-
-public:
-
-       static const int MIN_WIDTH    = 10;
-       static const int HANDLE_WIDTH = 5;
-
-       /* pianoItem ctor
-        * if action *a == NULL, record a new action */
-
-       gePianoItem(int x, int y, int rel_x, int rel_y, struct Recorder::action *a,
-               struct Recorder::action *b, class gdActionEditor *pParent);
-
-       void draw();
-       int  handle(int e);
-       void record();
-       void remove();
-
-       int getFrame_a() { return frame_a; }
-       int getFrame_b() { return frame_b; }
-       int getNote()    { return note;    }
-};
-
-
-#endif
diff --git a/src/gui/elems/pianoRoll.cpp b/src/gui/elems/pianoRoll.cpp
deleted file mode 100644 (file)
index b8f776b..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 "../../core/conf.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../core/recorder.h"
-#include "../../core/kernelMidi.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "pianoItem.h"
-#include "noteEditor.h"
-#include "pianoRoll.h"
-
-
-extern Conf          G_Conf;
-extern Recorder                G_Recorder;
-extern Mixer      G_Mixer;
-extern KernelMidi G_KernelMidi;
-
-
-gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent)
-  : geBaseActionEditor(X, Y, W, 40, pParent),
-    cursorOnItem      (false)
-{
-       resizable(nullptr);                   // don't resize children (i.e. pianoItem)
-       size(W, (MAX_KEYS+1) * CELL_H);      // 128 MIDI channels * CELL_H height
-
-       if (G_Conf.pianoRollY == -1)
-               position(x(), y()-(h()/2));  // center
-       else
-               position(x(), G_Conf.pianoRollY);
-
-       drawSurface1();
-       drawSurface2();
-
-       /* add actions when the window is opened. Position is zoom-based. MIDI
-        * actions come always in pair: start + end. */
-
-       G_Recorder.sortActions();
-
-       Recorder::action *a2   = nullptr;
-       Recorder::action *prev = nullptr;
-
-       for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
-               for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-
-                       /* don't show actions > than the grey area */
-                       /** FIXME - can we move this to the outer cycle? */
-
-                       if (G_Recorder.frames.at(i) > G_Mixer.totalFrames)
-                               continue;
-
-                       Recorder::action *a1 = G_Recorder.global.at(i).at(j);
-
-      /* Skip action if:
-        - does not belong to this channel
-        - is not a MIDI action (we only want MIDI things here)
-        - is the previous one (we have already checked it)
-        - (later on) if it's NOTE_OFF (0x80): we want note on only */
-
-      if (a1->chan != pParent->chan->index)
-                               continue;
-      if (a1->type != ACTION_MIDI)
-        continue;
-                       if (a1 == prev)
-                               continue;
-
-                       /* extract MIDI infos from a1: if is note off skip it, we are looking
-                        * for note on only */
-
-                       int a1_type = G_KernelMidi.getB1(a1->iValue);
-                       int a1_note = G_KernelMidi.getB2(a1->iValue);
-
-                       if (a1_type == 0x80) // NOTE_OFF
-                               continue;
-
-                       /* Search for the next action. Must have: same channel, ACTION_MIDI,
-      greater than a1->frame and with MIDI properties of note_off (0x80), same
-      note of a1, any velocity value (0xFF) because we just don't care about the
-      velocity of a note_off. */
-
-                       G_Recorder.getNextAction(a1->chan, ACTION_MIDI, a1->frame, &a2,
-                                       G_KernelMidi.getIValue(0x80, a1_note, 0xFF));
-
-                       /* next action note_off found: add a new gePianoItem to piano roll */
-
-                       if (a2) {
-                               new gePianoItem(0, 0, x(), y(), a1, a2, pParent);
-                               prev = a2;
-                               a2 = nullptr;
-                       }
-                       else
-                               gu_log("[geNoteEditor] recorder didn't find requested action!\n");
-        // TODO - create new gOrphanedPianoItem
-         }
-       }
-
-       end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::drawSurface1()
-{
-       surface1 = fl_create_offscreen(CELL_W, h());
-       fl_begin_offscreen(surface1);
-
-       /* warning: only w() and h() come from this widget, x and y coordinates
-        * are absolute, since we are writing in a memory chunk */
-
-       fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
-
-       fl_line_style(FL_DASH, 0, nullptr);
-       fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-
-       int octave = MAX_OCTAVES;
-
-       for (int i=1; i<=MAX_KEYS+1; i++) {
-
-               /* print key note label. C C# D D# E F F# G G# A A# B */
-
-               char note[6];
-               switch (i % KEYS) {
-                       case (int) Notes::G:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               sprintf(note, "%dG", octave);
-                               break;
-                       case (int) Notes::FS:
-                               sprintf(note, "%dF#", octave);
-                               break;
-                       case (int) Notes::F:
-                               sprintf(note, "%dF", octave);
-                               break;
-                       case (int) Notes::E:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               sprintf(note, "%dE", octave);
-                               break;
-                       case (int) Notes::DS:
-                               sprintf(note, "%dD#", octave);
-                               break;
-                       case (int) Notes::D:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               sprintf(note, "%dD", octave);
-                               break;
-                       case (int) Notes::CS:
-                               sprintf(note, "%dC#", octave);
-                               break;
-                       case (int) Notes::C:
-                               sprintf(note, "%dC", octave);
-                               break;
-                       case (int) Notes::B:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               sprintf(note, "%dB", octave);
-                               break;
-                       case (int) Notes::AS:
-                               sprintf(note, "%dA#", octave);
-                               break;
-                       case (int) Notes::A:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               sprintf(note, "%dA", octave);
-                               break;
-                       case (int) Notes::GS:
-                               sprintf(note, "%dG#", octave);
-                               octave--;
-                               break;
-               }
-
-    /* Print note name */
-
-               fl_color(COLOR_BG_LINE);
-               fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
-      (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
-               /* Print horizontal line */
-
-               if (i < MAX_KEYS+1)
-                       fl_line(0, i*CELL_H, CELL_W, +i*CELL_H);
-       }
-
-       fl_line_style(0);
-       fl_end_offscreen();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::drawSurface2()
-{
-       surface2 = fl_create_offscreen(CELL_W, h());
-       fl_begin_offscreen(surface2);
-       fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
-       fl_color(COLOR_BG_LINE);
-       fl_line_style(FL_DASH, 0, nullptr);
-       for (int i=1; i<=MAX_KEYS+1; i++) {
-               switch (i % KEYS) {
-                       case (int) Notes::G:
-                       case (int) Notes::E:
-                       case (int) Notes::D:
-                       case (int) Notes::B:
-                       case (int) Notes::A:
-                               fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
-                               break;
-               }
-               if (i < MAX_KEYS+1) {
-                       fl_color(COLOR_BG_LINE);
-                       fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
-               }
-       }
-       fl_line_style(0);
-       fl_end_offscreen();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::draw()
-{
-       fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
-
-#if defined(__APPLE__)
-       for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
-               fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
-#else
-       for (int i=CELL_W; i<pParent->totalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster
-               fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
-#endif
-
-       baseDraw(false);
-       draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoRoll::handle(int e)
-{
-       int ret = Fl_Group::handle(e);
-
-       switch (e) {
-               case FL_PUSH:   {
-
-                       /* avoid click on grey area */
-
-                       if (Fl::event_x() >= pParent->coverX) {
-                               ret = 1;
-                               break;
-                       }
-
-
-                       push_y = Fl::event_y() - y();
-
-                       if (Fl::event_button1()) {
-
-                               /* ax is driven by grid, ay by the height in px of each note */
-
-                               int ax = Fl::event_x();
-                               int ay = Fl::event_y();
-
-                               /* vertical snap */
-
-                               int edge = (ay-y()) % CELL_H;
-                               if (edge != 0) ay -= edge;
-
-                               /* if no overlap, add new piano item. Also check that it doesn't
-                                * overflow on the grey area, by shifting it to the left if
-                                * necessary. */
-
-        if (!cursorOnItem) {
-                                       int greyover = ax+20 - pParent->coverX-x();
-                                       if (greyover > 0)
-                                               ax -= greyover;
-                                       add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent));
-                                       redraw();
-                               }
-                       }
-                       ret = 1;
-                       break;
-               }
-               case FL_DRAG:   {
-
-                       if (Fl::event_button3()) {
-
-                               geNoteEditor *prc = (geNoteEditor*) parent();
-                               position(x(), Fl::event_y() - push_y);
-
-                               if (y() > prc->y())
-                                       position(x(), prc->y());
-                               else
-                               if (y() < prc->y()+prc->h()-h())
-                                       position(x(), prc->y()+prc->h()-h());
-
-                               prc->redraw();
-                       }
-                       ret = 1;
-                       break;
-               }
-               case FL_MOUSEWHEEL: {   // nothing to do, just avoid small internal scroll
-                       ret = 1;
-                       break;
-               }
-       }
-       return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::updateActions()
-{
-       /* when zooming, don't delete and re-add actions, just MOVE them. This
-        * function shifts the action by a zoom factor. Those singlepress are
-        * stretched, as well */
-
-       gePianoItem *i;
-       for (int k=0; k<children(); k++) {
-               i = (gePianoItem*) child(k);
-
-               //gu_log("found point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
-
-               int newX = x() + (i->getFrame_a() / pParent->zoom);
-               int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom);
-               if (newW < 8)
-                       newW = 8;
-               i->resize(newX, i->y(), newW, i->h());
-               i->redraw();
-
-               //gu_log("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
-       }
-}
diff --git a/src/gui/elems/pianoRoll.h b/src/gui/elems/pianoRoll.h
deleted file mode 100644 (file)
index 449d4d0..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 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 GE_PIANO_ROLL_H
-#define GE_PIANO_ROLL_H
-
-
-#include <FL/fl_draw.H>
-#include "baseActionEditor.h"
-
-
-class gePianoRoll : public geBaseActionEditor
-{
-private:
-
-       enum class Notes
-       {
-               G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10,
-               A = 11, GS = 0
-       };
-
-       /* drawSurface*
-       Generates a complex drawing in memory first and copy it to the screen at a
-       later point in time. Fl_Offscreen surface holds the necessary data.     The first
-       call creates an offscreen surface of CELL_W pixel wide containing note values.
-       The second call creates another offscreen surface of CELL_W pixels wide
-       containing the rest of the piano roll. The latter will then be tiled during
-       the ::draw() call. */
-
-       void drawSurface1();
-       void drawSurface2();
-
-       int push_y;
-
-       Fl_Offscreen surface1;  // notes, no repeat
-       Fl_Offscreen surface2;  // lines, x-repeat
-
-public:
-
-       static const int MAX_KEYS    = 127;
-       static const int MAX_OCTAVES = 9;
-       static const int KEYS        = 12;
-       static const int CELL_H      = 18;
-       static const int CELL_W      = 40;
-
-       gePianoRoll(int x, int y, int w, class gdActionEditor *pParent);
-
-       void draw();
-       int  handle(int e);
-       void updateActions();
-
-       /* cursorOnItem
-       Defines wheter the cursor is over a piano item. This value is updated by each
-       gePianoItem sub-widget. */
-
-       bool cursorOnItem;
-};
-
-
-#endif
diff --git a/src/gui/elems/pluginBrowser.cpp b/src/gui/elems/pluginBrowser.cpp
new file mode 100644 (file)
index 0000000..058ae62
--- /dev/null
@@ -0,0 +1,124 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <FL/fl_draw.H>
+#include "../../core/plugin.h"
+#include "../../core/const.h"
+#include "../../core/pluginHost.h"
+#include "basics/boxtypes.h"
+#include "pluginBrowser.h"
+
+
+using std::vector;
+using std::string;
+using namespace giada::m;
+
+
+gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
+       : Fl_Browser(x, y, w, h)
+{
+       box(G_CUSTOM_BORDER_BOX);
+       textsize(GUI_FONT_SIZE_BASE);
+       textcolor(COLOR_TEXT_0);
+       selection_color(COLOR_BG_1);
+       color(COLOR_BG_0);
+
+       this->scrollbar.color(COLOR_BG_0);
+       this->scrollbar.selection_color(COLOR_BG_1);
+       this->scrollbar.labelcolor(COLOR_BD_1);
+       this->scrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+       this->hscrollbar.color(COLOR_BG_0);
+       this->hscrollbar.selection_color(COLOR_BG_1);
+       this->hscrollbar.labelcolor(COLOR_BD_1);
+       this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+       type(FL_HOLD_BROWSER);
+
+       computeWidths();
+
+  column_widths(widths);
+  column_char('\t');       // tabs as column delimiters
+
+       refresh();
+
+       end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginBrowser::refresh()
+{
+       clear();
+
+       add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
+       add("---\t---\t---\t---\t---");
+
+       for (int i=0; i<pluginHost::countAvailablePlugins(); i++) {
+               pluginHost::PluginInfo pi = pluginHost::getAvailablePluginInfo(i);
+               string m = pluginHost::doesPluginExist(pi.uid) ? "" : "@-";
+               string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
+                               pi.category +   "\t" + m + pi.format + "\t" + m + pi.uid;
+               add(s.c_str());
+       }
+
+       for (unsigned i=0; i<pluginHost::countUnknownPlugins(); i++) {
+               string s = "?\t?\t?\t?\t? " + pluginHost::getUnknownPluginInfo(i) + " ?";
+               add(s.c_str());
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginBrowser::computeWidths()
+{
+       int w0, w1, w3;
+       for (int i=0; i<pluginHost::countAvailablePlugins(); i++) {
+               pluginHost::PluginInfo pi = pluginHost::getAvailablePluginInfo(i);
+               w0 = (int) fl_width(pi.name.c_str());
+               w1 = (int) fl_width(pi.manufacturerName.c_str());
+               w3 = (int) fl_width(pi.format.c_str());
+               if (w0 > widths[0]) widths[0] = w0;
+               if (w1 > widths[1]) widths[1] = w1;
+               if (w3 > widths[3]) widths[3] = w3;
+       }
+       widths[0] += 60;
+       widths[1] += 60;
+       widths[2] = fl_width("CATEGORY") + 60;
+       widths[3] += 60;
+       widths[4] = 0;
+}
+
+
+#endif
diff --git a/src/gui/elems/pluginBrowser.h b/src/gui/elems/pluginBrowser.h
new file mode 100644 (file)
index 0000000..4e2287e
--- /dev/null
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef GE_PLUGIN_BROWSER_H
+#define GE_PLUGIN_BROWSER_H
+
+
+#include <FL/Fl_Browser.H>
+
+
+class gePluginBrowser : public Fl_Browser
+{
+private:
+
+       int widths[5] = {0};
+
+       void computeWidths();
+
+public:
+
+       gePluginBrowser(int x, int y, int w, int h);
+
+       void refresh();
+};
+
+#endif
+
+#endif
diff --git a/src/gui/elems/sampleEditor/boostTool.cpp b/src/gui/elems/sampleEditor/boostTool.cpp
new file mode 100644 (file)
index 0000000..9a72a4f
--- /dev/null
@@ -0,0 +1,123 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/waveFx.h"  
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "boostTool.h"
+
+
+geBoostTool::geBoostTool(int X, int Y, SampleChannel *ch)
+  : Fl_Group(X, Y, 220, 20),
+    ch      (ch)
+{
+  begin();
+    label     = new geBox(x(), y(), gu_getStringWidth("Boost"), 20, "Boost", FL_ALIGN_RIGHT);
+    dial      = new geDial(label->x()+label->w()+4, y(), 20, 20);
+    input     = new geInput(dial->x()+dial->w()+4, y(), 70, 20);
+    normalize = new geButton(input->x()+input->w()+4, y(), 70, 20, "Normalize");
+  end();
+
+  dial->range(1.0f, 10.0f);
+  dial->callback(cb_setBoost, (void*)this);
+  dial->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
+
+  input->callback(cb_setBoostNum, (void*)this);
+
+  normalize->callback(cb_normalize, (void*)this);
+
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::refresh()
+{
+  char buf[16];
+  float dB = gu_linearToDB(ch->getBoost());
+  sprintf(buf, "%.2f", dB);
+  input->value(buf);
+  // a dial > than it's max value goes crazy
+  dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::cb_setBoost   (Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_setBoost(); }
+void geBoostTool::cb_setBoostNum(Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_setBoostNum(); }
+void geBoostTool::cb_normalize  (Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_normalize(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_setBoost()
+{
+  if (Fl::event() == FL_DRAG)
+    glue_setBoost(ch, dial->value());
+  else 
+  if (Fl::event() == FL_RELEASE) {
+    glue_setBoost(ch, dial->value());
+    static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_setBoostNum()
+{
+  glue_setBoost(ch, gu_dBtoLinear(atof(input->value())));
+  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_normalize()
+{
+  float val = wfx_normalizeSoft(ch->wave);
+  glue_setBoost(ch, val); // it's like a fake user moving the dial 
+  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
diff --git a/src/gui/elems/sampleEditor/boostTool.h b/src/gui/elems/sampleEditor/boostTool.h
new file mode 100644 (file)
index 0000000..f1e7875
--- /dev/null
@@ -0,0 +1,68 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_BOOST_TOOL_H
+#define GE_BOOST_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class geBoostTool : public Fl_Group
+{
+private:
+
+  SampleChannel *ch;
+
+  geBox    *label;
+  geDial   *dial;
+  geInput  *input;
+  geButton *normalize;
+
+  static void cb_setBoost   (Fl_Widget *w, void *p);
+  static void cb_setBoostNum(Fl_Widget *w, void *p);
+  static void cb_normalize  (Fl_Widget *w, void *p);
+  inline void __cb_setBoost   ();
+  inline void __cb_setBoostNum();
+  inline void __cb_normalize  ();
+
+public:
+
+  geBoostTool(int x, int y, SampleChannel *ch);
+
+  void refresh();
+};
+
+
+#endif
diff --git a/src/gui/elems/sampleEditor/panTool.cpp b/src/gui/elems/sampleEditor/panTool.cpp
new file mode 100644 (file)
index 0000000..59eb792
--- /dev/null
@@ -0,0 +1,112 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/waveFx.h"  
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "panTool.h"
+
+
+gePanTool::gePanTool(int x, int y, SampleChannel *ch)
+  : Fl_Group(x, y, 200, 20),
+    ch      (ch)
+{
+  begin();
+    label = new geBox(x, y, gu_getStringWidth("Pan"), 20, "Pan", FL_ALIGN_RIGHT);
+    dial  = new geDial(label->x()+label->w()+4, y, 20, 20);
+    input = new geInput(dial->x()+dial->w()+4, y, 70, 20);
+    reset = new geButton(input->x()+input->w()+4, y, 70, 20, "Reset");
+  end();
+
+  dial->range(0.0f, 1.0f);
+  dial->callback(cb_panning, (void*)this);
+
+  input->align(FL_ALIGN_RIGHT);
+  input->readonly(1);
+  input->cursor_color(FL_WHITE);
+
+  reset->callback(cb_panReset, (void*)this);
+
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::refresh()
+{
+  dial->value(ch->getPan());
+  char buf[8];
+  if (ch->getPan() < 0.5f) {
+    sprintf(buf, "%d L", (int) ((-ch->getPan() * 200.0f) + 100.0f));
+    input->value(buf);
+  }
+  else 
+  if (ch->getPan() == 0.5)
+    input->value("C");
+  else {
+    sprintf(buf, "%d R", (int) ((ch->getPan() * 200.0f) - 100.0f));
+    input->value(buf);
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::cb_panning (Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panning(); }
+void gePanTool::cb_panReset(Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panReset(); }
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::__cb_panning()
+{
+  glue_setPanning(ch, dial->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::__cb_panReset()
+{
+  glue_setPanning(ch, 0.5f);
+}
\ No newline at end of file
diff --git a/src/gui/elems/sampleEditor/panTool.h b/src/gui/elems/sampleEditor/panTool.h
new file mode 100644 (file)
index 0000000..c90ca66
--- /dev/null
@@ -0,0 +1,66 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PAN_TOOL_H
+#define GE_PAN_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class gePanTool : public Fl_Group
+{
+private:
+
+  SampleChannel *ch;
+
+  geBox    *label;
+  geDial   *dial;
+  geInput  *input;
+  geButton *reset;
+
+  static void cb_panning (Fl_Widget *w, void *p);
+  static void cb_panReset(Fl_Widget *w, void *p);
+  inline void __cb_panning();
+  inline void __cb_panReset();
+
+public:
+
+  gePanTool(int x, int y, SampleChannel *ch);
+
+  void refresh();
+};
+
+
+#endif
diff --git a/src/gui/elems/sampleEditor/pitchTool.cpp b/src/gui/elems/sampleEditor/pitchTool.cpp
new file mode 100644 (file)
index 0000000..8e6f439
--- /dev/null
@@ -0,0 +1,163 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/graphics.h"  
+#include "../../../core/clock.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "pitchTool.h"
+
+
+using namespace giada::m;
+
+
+gePitchTool::gePitchTool(int x, int y, SampleChannel *ch)
+  : Fl_Group(x, y, 600, 20),
+    ch      (ch)
+{
+  begin();
+    label       = new geBox(x, y, gu_getStringWidth("Pitch"), 20, "Pitch", FL_ALIGN_RIGHT);
+    dial        = new geDial(label->x()+label->w()+4, y, 20, 20);
+    input       = new geInput(dial->x()+dial->w()+4, y, 70, 20);
+    pitchToBar  = new geButton(input->x()+input->w()+4, y, 70, 20, "To bar");
+    pitchToSong = new geButton(pitchToBar->x()+pitchToBar->w()+4, y, 70, 20, "To song");
+    pitchHalf   = new geButton(pitchToSong->x()+pitchToSong->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
+    pitchDouble = new geButton(pitchHalf->x()+pitchHalf->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
+    pitchReset  = new geButton(pitchDouble->x()+pitchDouble->w()+4, y, 70, 20, "Reset");
+  end();
+
+  dial->range(0.01f, 4.0f);
+  dial->callback(cb_setPitch, (void*)this);
+  dial->when(FL_WHEN_RELEASE);
+
+  input->align(FL_ALIGN_RIGHT);
+  input->callback(cb_setPitchNum, (void*)this);
+  input->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
+
+  pitchToBar->callback(cb_setPitchToBar, (void*)this);
+  pitchToSong->callback(cb_setPitchToSong, (void*)this);
+  pitchHalf->callback(cb_setPitchHalf, (void*)this);
+  pitchDouble->callback(cb_setPitchDouble, (void*)this);
+  pitchReset->callback(cb_resetPitch, (void*)this);
+
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::refresh()
+{
+  dial->value(ch->getPitch());
+  char buf[16];
+  sprintf(buf, "%.4f", ch->getPitch()); // 4 digits
+  input->value(buf);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::cb_setPitch      (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitch(); }
+void gePitchTool::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToBar(); }
+void gePitchTool::cb_setPitchToSong(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToSong(); }
+void gePitchTool::cb_setPitchHalf  (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchHalf(); }
+void gePitchTool::cb_setPitchDouble(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchDouble(); }
+void gePitchTool::cb_resetPitch    (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_resetPitch(); }
+void gePitchTool::cb_setPitchNum   (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchNum(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitch()
+{
+  glue_setPitch(ch, dial->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchNum()
+{
+  glue_setPitch(ch, atof(input->value()));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchHalf()
+{
+  glue_setPitch(ch, dial->value()/2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchDouble()
+{
+  glue_setPitch(ch, dial->value()*2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchToBar()
+{
+  glue_setPitch(ch, ch->end / (float) clock::getFramesPerBar());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchToSong()
+{
+  glue_setPitch(ch, ch->end / (float) clock::getTotalFrames());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_resetPitch()
+{
+  glue_setPitch(ch, G_DEFAULT_PITCH);
+}
diff --git a/src/gui/elems/sampleEditor/pitchTool.h b/src/gui/elems/sampleEditor/pitchTool.h
new file mode 100644 (file)
index 0000000..fd3f7ae
--- /dev/null
@@ -0,0 +1,80 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_PITCH_TOOL_H
+#define GE_PITCH_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class gePitchTool : public Fl_Group
+{
+private:
+
+  SampleChannel *ch;
+
+  geBox    *label;
+  geDial   *dial;
+  geInput  *input;
+  geButton *pitchToBar;
+  geButton *pitchToSong;
+  geButton *pitchHalf;
+  geButton *pitchDouble;
+  geButton *pitchReset;
+
+  static void cb_setPitch      (Fl_Widget *w, void *p);
+  static void cb_setPitchToBar (Fl_Widget *w, void *p);
+  static void cb_setPitchToSong(Fl_Widget *w, void *p);
+  static void cb_setPitchHalf  (Fl_Widget *w, void *p);
+  static void cb_setPitchDouble(Fl_Widget *w, void *p);
+  static void cb_resetPitch    (Fl_Widget *w, void *p);
+  static void cb_setPitchNum   (Fl_Widget *w, void *p);
+  inline void __cb_setPitch();
+  inline void __cb_setPitchToBar();
+  inline void __cb_setPitchToSong();
+  inline void __cb_setPitchHalf();
+  inline void __cb_setPitchDouble();
+  inline void __cb_resetPitch();
+  inline void __cb_setPitchNum();
+
+public:
+
+  gePitchTool(int x, int y, SampleChannel *ch);
+
+  void refresh();
+};
+
+
+#endif
diff --git a/src/gui/elems/sampleEditor/rangeTool.cpp b/src/gui/elems/sampleEditor/rangeTool.cpp
new file mode 100644 (file)
index 0000000..c7b34b1
--- /dev/null
@@ -0,0 +1,101 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/wave.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "rangeTool.h"
+
+
+geRangeTool::geRangeTool(int x, int y, SampleChannel *ch)
+  : Fl_Group(x, y, 300, 20),
+    ch      (ch)
+{
+  begin();
+    label  = new geBox  (x, y, gu_getStringWidth("Range"), 20, "Range", FL_ALIGN_RIGHT);
+    begin_ = new geInput(label->x()+label->w()+4, y, 70, 20);
+    end_   = new geInput(begin_->x()+begin_->w()+4, y, 70, 20);
+    reset  = new geButton(end_->x()+end_->w()+4, y, 70, 20, "Reset");
+  end();
+
+  begin_->type(FL_INT_INPUT);
+  begin_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+  begin_->callback(cb_setChanPos, this);
+  
+  end_->type(FL_INT_INPUT);
+  end_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+  end_->callback(cb_setChanPos, this);
+
+  reset->callback(cb_resetStartEnd, this);
+
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::refresh()
+{
+  begin_->value(gu_itoa(ch->begin / 2).c_str());
+  end_->value(gu_itoa(ch->end / 2).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::cb_setChanPos   (Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_setChanPos(); }
+void geRangeTool::cb_resetStartEnd(Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_resetStartEnd(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::__cb_setChanPos()
+{
+  glue_setBeginEndChannel(ch, atoi(begin_->value())*2, atoi(end_->value())*2);
+  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::__cb_resetStartEnd()
+{
+  glue_setBeginEndChannel(ch, 0, ch->wave->size);
+  static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
diff --git a/src/gui/elems/sampleEditor/rangeTool.h b/src/gui/elems/sampleEditor/rangeTool.h
new file mode 100644 (file)
index 0000000..909f0f3
--- /dev/null
@@ -0,0 +1,65 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_RANGE_TOOL_H
+#define GE_RANGE_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geInput;
+class geButton;
+class geBox;
+
+
+class geRangeTool : public Fl_Group
+{
+private:
+
+  SampleChannel *ch;
+
+  geBox    *label;
+  geInput  *begin_;
+  geInput  *end_;
+  geButton *reset;
+
+  static void cb_setChanPos   (Fl_Widget *w, void *p);
+  static void cb_resetStartEnd(Fl_Widget *w, void *p);
+  inline void __cb_setChanPos();
+  inline void __cb_resetStartEnd();
+
+public:
+
+  geRangeTool(int x, int y, SampleChannel *ch);
+
+  void refresh();
+};
+
+
+#endif
diff --git a/src/gui/elems/sampleEditor/volumeTool.cpp b/src/gui/elems/sampleEditor/volumeTool.cpp
new file mode 100644 (file)
index 0000000..3cd7048
--- /dev/null
@@ -0,0 +1,100 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cmath>
+#include <FL/Fl_Pack.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../mainWindow/keyboard/channel.h"
+#include "volumeTool.h"
+
+
+geVolumeTool::geVolumeTool(int X, int Y, SampleChannel *ch)
+  : Fl_Group(X, Y, 150, 20),
+    ch      (ch)
+{
+  begin();
+    label = new geBox  (x(), y(), gu_getStringWidth("Volume"), 20, "Volume", FL_ALIGN_RIGHT);
+    dial  = new geDial (label->x()+label->w()+4, y(), 20, 20);
+    input = new geInput(dial->x()+dial->w()+4, y(), 70, 20);
+  end();
+
+  dial->range(0.0f, 1.0f);
+  dial->callback(cb_setVolume, (void*)this);
+
+  input->callback(cb_setVolumeNum, (void*)this);
+
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::refresh()
+{
+  char buf[16];
+  float dB = gu_linearToDB(ch->volume);
+  if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
+  else                sprintf(buf, "-inf");
+  input->value(buf);
+  dial->value(ch->guiChannel->vol->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::cb_setVolume   (Fl_Widget *w, void *p) { ((geVolumeTool*)p)->__cb_setVolume(); }
+void geVolumeTool::cb_setVolumeNum(Fl_Widget *w, void *p) { ((geVolumeTool*)p)->__cb_setVolumeNum(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::__cb_setVolume()
+{
+  glue_setVolume(ch, dial->value(), false, true);
+  refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::__cb_setVolumeNum()
+{
+  float value = pow(10, (atof(input->value()) / 20)); // linear = 10^(dB/20)
+  glue_setVolume(ch, value, false, true);
+  dial->value(ch->guiChannel->vol->value());
+}
diff --git a/src/gui/elems/sampleEditor/volumeTool.h b/src/gui/elems/sampleEditor/volumeTool.h
new file mode 100644 (file)
index 0000000..e6f98c1
--- /dev/null
@@ -0,0 +1,64 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_VOLUME_TOOL_H
+#define GE_VOLUME_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geBox;
+
+
+class geVolumeTool : public Fl_Group
+{
+private:
+
+  SampleChannel *ch;
+
+  geBox   *label;
+  geDial  *dial;
+  geInput *input;
+
+  static void cb_setVolume   (Fl_Widget *w, void *p);
+       static void cb_setVolumeNum(Fl_Widget *w, void *p);
+  inline void __cb_setVolume   ();
+  inline void __cb_setVolumeNum();
+
+public:
+
+  geVolumeTool(int x, int y, SampleChannel *ch);
+
+  void refresh();
+};
+
+
+#endif
diff --git a/src/gui/elems/sampleEditor/waveTools.cpp b/src/gui/elems/sampleEditor/waveTools.cpp
new file mode 100644 (file)
index 0000000..52c2903
--- /dev/null
@@ -0,0 +1,101 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 "../../../core/const.h"
+#include "../basics/boxtypes.h"
+#include "waveform.h"
+#include "waveTools.h"
+
+
+geWaveTools::geWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l)
+       : Fl_Scroll(x, y, w, h, l)
+{
+       type(Fl_Scroll::HORIZONTAL_ALWAYS);
+       hscrollbar.color(COLOR_BG_0);
+       hscrollbar.selection_color(COLOR_BG_1);
+       hscrollbar.labelcolor(COLOR_BD_1);
+       hscrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+       waveform = new geWaveform(x, y, w, h-24, ch);
+
+
+       //resizable(waveform);
+}
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveTools::updateWaveform()
+{
+       waveform->alloc(w());
+       waveform->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveTools::resize(int x, int y, int w, int h)
+{
+       if (this->w() == w || (this->w() != w && this->h() != h)) {   // vertical or both resize
+               Fl_Widget::resize(x, y, w, h);
+               waveform->resize(x, y, waveform->w(), h-24);
+               updateWaveform();
+       }
+       else {                                                        // horizontal resize
+               Fl_Widget::resize(x, y, w, h);
+       }
+
+       if (this->w() > waveform->w())
+               waveform->stretchToWindow();
+
+       int offset = waveform->x() + waveform->w() - this->w() - this->x();
+       if (offset < 0)
+               waveform->position(waveform->x()-offset, this->y());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geWaveTools::handle(int e)
+{
+       int ret = Fl_Group::handle(e);
+       switch (e) {
+               case FL_MOUSEWHEEL: {
+                       waveform->setZoom(Fl::event_dy());
+                       redraw();
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
diff --git a/src/gui/elems/sampleEditor/waveTools.h b/src/gui/elems/sampleEditor/waveTools.h
new file mode 100644 (file)
index 0000000..303326b
--- /dev/null
@@ -0,0 +1,54 @@
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_WAVE_TOOLS_H
+#define GE_WAVE_TOOLS_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class SampleChannel;
+class geWaveform;
+
+
+class geWaveTools : public Fl_Scroll
+{
+public:
+
+       geWaveform *waveform;
+
+       geWaveTools(int X,int Y,int W, int H, SampleChannel *ch, const char *L=0);
+       void resize(int x, int y, int w, int h);
+       int  handle(int e);
+
+       void updateWaveform();
+};
+
+#endif
diff --git a/src/gui/elems/sampleEditor/waveform.cpp b/src/gui/elems/sampleEditor/waveform.cpp
new file mode 100644 (file)
index 0000000..860ad59
--- /dev/null
@@ -0,0 +1,813 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geWaveform
+ * An element which represents a waveform.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cmath>
+#include <FL/fl_draw.H>
+#include <FL/Fl_Menu_Button.H>
+#include "../../../core/wave.h"
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/waveFx.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../glue/channel.h"
+#include "../basics/boxtypes.h"
+#include "waveTools.h"
+#include "waveform.h"
+
+
+using namespace giada::m;
+
+
+geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel *ch, const char *l)
+: Fl_Widget(x, y, w, h, l),
+  chan(ch),
+  menuOpen(false),
+  chanStart(0),
+  chanStartLit(false),
+  chanEnd(0),
+  chanEndLit(false),
+  ratio(0.0f),
+  selectionA(0),
+  selectionB(0),
+  selectionA_abs(0),
+  selectionB_abs(0)
+{
+  data.sup  = nullptr;
+  data.inf  = nullptr;
+  data.size = 0;
+
+  grid.snap  = conf::sampleEditorGridOn;
+  grid.level = conf::sampleEditorGridVal;
+
+  stretchToWindow();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geWaveform::~geWaveform()
+{
+  freeData();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::freeData()
+{
+  if (data.sup != nullptr) {
+    free(data.sup);
+    free(data.inf);
+    data.sup  = nullptr;
+    data.inf  = nullptr;
+    data.size = 0;
+  }
+  grid.points.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::alloc(int datasize)
+{
+  ratio = chan->wave->size / (float) datasize;
+
+  if (ratio < 2)
+    return 0;
+
+  freeData();
+
+  data.size = datasize;
+  data.sup  = (int*) malloc(data.size * sizeof(int));
+  data.inf  = (int*) malloc(data.size * sizeof(int));
+
+  int offset = h() / 2;
+  int zero   = y() + offset; // center, zero amplitude (-inf dB)
+
+  /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
+   * even, as always */
+
+  int gridFreq = 0;
+  if (grid.level != 0) {
+    gridFreq = chan->wave->size / grid.level;
+    if (gridFreq % 2 != 0)
+      gridFreq--;
+  }
+
+  for (int i=0; i<data.size; i++) {
+
+    int pp;  // point prev
+    int pn;  // point next
+
+    /* resampling the waveform, hardcore way. Many thanks to
+     * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
+     * Note: we use
+     *   p = j * (m-1 / n)
+     * instead of
+     *   p = j * (m-1 / n-1)
+     * in order to obtain 'datasize' cells to parse (and not datasize-1) */
+
+    pp = i * ((chan->wave->size - 1) / (float) datasize);
+    pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
+
+    if (pp % 2 != 0) pp -= 1;
+    if (pn % 2 != 0) pn -= 1;
+
+    float peaksup = 0.0f;
+    float peakinf = 0.0f;
+
+    /* scan the original data in chunks */
+
+    int k = pp;
+    while (k < pn) {
+
+      if (chan->wave->data[k] > peaksup)
+        peaksup = chan->wave->data[k];    // FIXME - Left data only
+      else
+      if (chan->wave->data[k] <= peakinf)
+        peakinf = chan->wave->data[k];    // FIXME - Left data only
+
+      /* print grid */
+
+      if (gridFreq != 0)
+        if (k % gridFreq == 0 && k != 0)
+          grid.points.push_back(i);
+
+      k += 2;
+    }
+
+    data.sup[i] = zero - (peaksup * chan->getBoost() * offset);
+    data.inf[i] = zero - (peakinf * chan->getBoost() * offset);
+
+    // avoid window overflow
+
+    if (data.sup[i] < y())       data.sup[i] = y();
+    if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
+  }
+
+  recalcPoints();
+  return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::recalcPoints()
+{
+  selectionA = relativePoint(selectionA_abs);
+  selectionB = relativePoint(selectionB_abs);
+  chanStart  = relativePoint(chan->begin / 2);
+
+  /* fix the rounding error when chanEnd is set on the very end of the
+   * sample */
+
+  if (chan->end == chan->wave->size)
+    chanEnd = data.size - 2; // 2 px border
+  else
+    chanEnd = relativePoint(chan->end / 2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::draw()
+{
+  /* blank canvas */
+
+  fl_rectf(x(), y(), w(), h(), COLOR_BG_0);
+
+  /* draw selection (if any) */
+
+  if (selectionA != selectionB) {
+
+    int a_x = selectionA + x() - BORDER; // - start;
+    int b_x = selectionB + x() - BORDER; //  - start;
+
+    if (a_x < 0)
+      a_x = 0;
+    if (b_x >= w()-1)
+      b_x = w()-1;
+
+    if (selectionA < selectionB)
+      fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0);
+    else
+      fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0);
+  }
+
+  /* draw waveform from x1 (offset driven by the scrollbar) to x2
+   * (width of parent window). We don't draw the entire waveform,
+   * only the visibile part. */
+
+  int offset = h() / 2;
+  int zero   = y() + offset; // sample zero (-inf dB)
+
+  int wx1 = abs(x() - ((geWaveTools*)parent())->x());
+  int wx2 = wx1 + ((geWaveTools*)parent())->w();
+  if (x()+w() < ((geWaveTools*)parent())->w())
+    wx2 = x() + w() - BORDER;
+
+  fl_color(0, 0, 0);
+  for (int i=wx1; i<wx2; i++) {
+    fl_line(i+x(), zero, i+x(), data.sup[i]);
+    fl_line(i+x(), zero, i+x(), data.inf[i]);
+
+    /* print grid */
+
+    for (unsigned k=0; k<grid.points.size(); k++) {
+      if (grid.points.at(k) == i) {
+        //gu_log("draw grid line at %d\n", i);
+        fl_color(fl_rgb_color(54, 54, 54));
+        fl_line_style(FL_DASH, 0, nullptr);
+        fl_line(i+x(), y(), i+x(), y()+h());
+        fl_color(0, 0, 0);
+        fl_line_style(FL_SOLID, 0, nullptr);
+        break;
+      }
+    }
+  }
+
+  /* border box */
+
+  fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+  /* print chanStart */
+
+  int lineX = x()+chanStart+1;
+
+  if (chanStartLit) fl_color(COLOR_BD_1);
+  else              fl_color(COLOR_BD_0);
+
+  /* vertical line */
+
+  fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+  /* print flag and avoid overflow */
+
+  if (lineX+FLAG_WIDTH > w()+x()-2)
+    fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
+  else
+    fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
+
+  /* print chanEnd */
+
+  lineX = x()+chanEnd;
+  if (chanEndLit) fl_color(COLOR_BD_1);
+  else            fl_color(COLOR_BD_0);
+
+  fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+  if (lineX-FLAG_WIDTH < x())
+    fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
+  else
+    fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::handle(int e)
+{
+  int ret = 0;
+
+  switch (e) {
+
+    case FL_PUSH: {
+
+      mouseX = Fl::event_x();
+      pushed = true;
+
+      if (!mouseOnEnd() && !mouseOnStart()) {
+
+        /* right button? show the menu. Don't set selectionA,B,etc */
+
+        if (Fl::event_button3()) {
+          openEditMenu();
+        }
+        else
+        if (mouseOnSelectionA() || mouseOnSelectionB()) {
+          resized = true;
+        }
+        else {
+          dragged = true;
+          selectionA = Fl::event_x() - x();
+
+          if (selectionA >= data.size) selectionA = data.size;
+
+          selectionB = selectionA;
+          selectionA_abs = absolutePoint(selectionA);
+          selectionB_abs = selectionA_abs;
+        }
+      }
+
+      ret = 1;
+      break;
+    }
+
+    case FL_RELEASE: {
+
+      int realChanStart = chan->begin;
+      int realChanEnd   = chan->end;
+
+      if (chanStartLit)
+        realChanStart = absolutePoint(chanStart)*2;
+      else
+      if (chanEndLit)
+        realChanEnd = absolutePoint(chanEnd)*2;
+
+      glue_setBeginEndChannel(chan, realChanStart, realChanEnd);
+
+      pushed  = false;
+      dragged = false;
+
+      redraw();
+      ret = 1;
+      break;
+    }
+
+    case FL_ENTER: {  // enables FL_DRAG
+      ret = 1;
+      break;
+    }
+
+    case FL_LEAVE: {
+      if (chanStartLit || chanEndLit) {
+        chanStartLit = false;
+        chanEndLit   = false;
+        redraw();
+      }
+      ret = 1;
+      break;
+    }
+
+    case FL_MOVE: {
+      mouseX = Fl::event_x();
+      mouseY = Fl::event_y();
+
+      if (mouseOnStart()) {
+        chanStartLit = true;
+        redraw();
+      }
+      else
+      if (chanStartLit) {
+        chanStartLit = false;
+        redraw();
+      }
+
+      if (mouseOnEnd()) {
+        chanEndLit = true;
+        redraw();
+      }
+      else
+      if (chanEndLit) {
+        chanEndLit = false;
+        redraw();
+      }
+
+      if (mouseOnSelectionA())
+        fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+      else
+      if (mouseOnSelectionB())
+        fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+      else
+        fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+      ret = 1;
+      break;
+    }
+
+    case FL_DRAG: {
+
+      /* here the mouse is on the chanStart tool */
+
+      if (chanStartLit && pushed) {
+
+        chanStart = Fl::event_x() - x();
+
+        if (grid.snap)
+          chanStart = applySnap(chanStart);
+
+        if (chanStart < 0)
+          chanStart = 0;
+        else
+        if (chanStart >= chanEnd)
+          chanStart = chanEnd-2;
+
+        redraw();
+      }
+      else
+      if (chanEndLit && pushed) {
+
+        chanEnd = Fl::event_x() - x();
+
+        if (grid.snap)
+          chanEnd = applySnap(chanEnd);
+
+        if (chanEnd >= data.size - 2)
+          chanEnd = data.size - 2;
+        else
+        if (chanEnd <= chanStart)
+          chanEnd = chanStart + 2;
+
+        redraw();
+      }
+
+      /* here the mouse is on the waveform, i.e. a selection */
+
+      else
+      if (dragged) {
+
+        selectionB = Fl::event_x() - x();
+
+        if (selectionB >= data.size)
+          selectionB = data.size;
+
+        if (selectionB <= 0)
+          selectionB = 0;
+
+        if (grid.snap)
+          selectionB = applySnap(selectionB);
+
+        selectionB_abs = absolutePoint(selectionB);
+        redraw();
+      }
+
+      /* here the mouse is on a selection boundary i.e. resize */
+
+      else
+      if (resized) {
+        int pos = Fl::event_x() - x();
+        if (mouseOnSelectionA()) {
+          selectionA     = grid.snap ? applySnap(pos) : pos;
+          selectionA_abs = absolutePoint(selectionA);
+        }
+        else
+        if (mouseOnSelectionB()) {
+          selectionB     = grid.snap ? applySnap(pos) : pos;
+          selectionB_abs = absolutePoint(selectionB);
+        }
+        redraw();
+      }
+      mouseX = Fl::event_x();
+      ret = 1;
+      break;
+    }
+  }
+  return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* pixel snap disances (10px) must be equal to those defined in
+ * geWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */
+/* TODO - use constant for 10px */
+
+int geWaveform::applySnap(int pos)
+{
+  for (unsigned i=0; i<grid.points.size(); i++) {
+    if (pos >= grid.points.at(i) - SNAPPING &&
+        pos <= grid.points.at(i) + SNAPPING)
+    {
+      return grid.points.at(i);
+    }
+  }
+  return pos;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::mouseOnStart()
+{
+  return mouseX-10 >  chanStart + x() - BORDER              &&
+         mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH &&
+         mouseY    >  h() + y() - FLAG_HEIGHT;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::mouseOnEnd()
+{
+  return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH &&
+         mouseX-10 <= chanEnd + x() - BORDER              &&
+         mouseY    <= y() + FLAG_HEIGHT + 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* pixel boundaries (10px) must be equal to the snap factor distance
+ * defined in geWaveform::applySnap() */
+
+bool geWaveform::mouseOnSelectionA()
+{
+  if (selectionA == selectionB)
+    return false;
+  return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x();
+}
+
+
+bool geWaveform::mouseOnSelectionB()
+{
+  if (selectionA == selectionB)
+    return false;
+  return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::absolutePoint(int p)
+{
+  if (p <= 0)
+    return 0;
+
+  if (p > data.size)
+    return chan->wave->size / 2;
+
+  return (p * ratio) / 2;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::relativePoint(int p)
+{
+  return (ceil(p / ratio)) * 2;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::openEditMenu()
+{
+  if (selectionA == selectionB)
+    return;
+
+  menuOpen = true;
+
+  Fl_Menu_Item menu[] = {
+    {"Cut"},
+    {"Trim"},
+    {"Silence"},
+    {"Fade in"},
+    {"Fade out"},
+    {"Smooth edges"},
+    {"Set start/end here"},
+    {0}
+  };
+
+  if (chan->status == STATUS_PLAY) {
+    menu[0].deactivate();
+    menu[1].deactivate();
+  }
+
+  Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+  b->box(G_CUSTOM_BORDER_BOX);
+  b->textsize(GUI_FONT_SIZE_BASE);
+  b->textcolor(COLOR_TEXT_0);
+  b->color(COLOR_BG_0);
+
+  const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+  if (!m) {
+    menuOpen = false;
+    return;
+  }
+
+  /* straightSel() to ensure that point A is always lower than B */
+
+  straightSel();
+
+  if (strcmp(m->label(), "Silence") == 0) {
+    wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+    selectionA = 0;
+    selectionB = 0;
+
+    stretchToWindow();
+    redraw();
+    menuOpen = false;
+    return;
+  }
+
+  if (strcmp(m->label(), "Set start/end here") == 0) {
+
+    glue_setBeginEndChannel(chan, absolutePoint(selectionA) * 2, 
+      absolutePoint(selectionB) * 2); // stereo values
+
+    selectionA     = 0;
+    selectionB     = 0;
+    selectionA_abs = 0;
+    selectionB_abs = 0;
+
+    recalcPoints();
+    redraw();
+    menuOpen = false;
+    return;
+  }
+
+  if (strcmp(m->label(), "Cut") == 0) {
+    wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+    /* for convenience reset start/end points */
+
+    glue_setBeginEndChannel(chan, 0, chan->wave->size);
+
+    selectionA     = 0;
+    selectionB     = 0;
+    selectionA_abs = 0;
+    selectionB_abs = 0;
+
+    setZoom(0);
+
+    menuOpen = false;
+    return;
+  }
+
+  if (strcmp(m->label(), "Trim") == 0) {
+    wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+    glue_setBeginEndChannel(chan, 0, chan->wave->size);
+
+    selectionA     = 0;
+    selectionB     = 0;
+    selectionA_abs = 0;
+    selectionB_abs = 0;
+
+    stretchToWindow();
+    menuOpen = false;
+    redraw();
+    return;
+  }
+
+  if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) {
+
+    int type = !strcmp(m->label(), "Fade in") ? 0 : 1;
+    wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type);
+
+    selectionA = 0;
+    selectionB = 0;
+
+    stretchToWindow();
+    redraw();
+    menuOpen = false;
+    return;
+  }
+
+  if (!strcmp(m->label(), "Smooth edges")) {
+
+    wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+    selectionA = 0;
+    selectionB = 0;
+
+    stretchToWindow();
+    redraw();
+    menuOpen = false;
+    return;
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::straightSel()
+{
+  if (selectionA > selectionB) {
+    unsigned tmp = selectionB;
+    selectionB = selectionA;
+    selectionA = tmp;
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::setZoom(int type)
+{
+  int newSize;
+  if (type == -1) newSize = data.size*2;  // zoom in
+  else            newSize = data.size/2;  // zoom out
+
+  if (alloc(newSize)) {
+    size(data.size, h());
+
+    /* zoom to pointer */
+
+    int shift;
+    if (x() > 0)
+      shift = Fl::event_x() - x();
+    else
+    if (type == -1)
+      shift = Fl::event_x() + abs(x());
+    else
+      shift = (Fl::event_x() + abs(x())) / -2;
+
+    if (x() - shift > BORDER)
+      shift = 0;
+
+    position(x() - shift, y());
+
+
+    /* avoid overflow when zooming out with scrollbar like that:
+     * |----------[scrollbar]|
+     *
+     * offset vs smaller:
+     * |[wave------------| offset > 0  smaller = false
+     * |[wave----]       | offset < 0, smaller = true
+     * |-------------]   | offset < 0, smaller = false  */
+
+    int  parentW = ((geWaveTools*)parent())->w();
+    int  thisW   = x() + w() - BORDER;           // visible width, not full width
+
+    if (thisW < parentW)
+      position(x() + parentW - thisW, y());
+    if (smaller())
+      stretchToWindow();
+
+    redraw();
+  }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::stretchToWindow()
+{
+  int s = ((geWaveTools*)parent())->w();
+  alloc(s);
+  position(BORDER, y());
+  size(s, h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::smaller()
+{
+  return w() < ((geWaveTools*)parent())->w();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float geWaveform::displayRatio()
+{
+    return 1.0f / (data.size / (float) w());
+};
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::setGridLevel(int l)
+{
+  grid.points.clear();
+  grid.level = l;
+  alloc(data.size);
+  redraw();
+}
diff --git a/src/gui/elems/sampleEditor/waveform.h b/src/gui/elems/sampleEditor/waveform.h
new file mode 100644 (file)
index 0000000..f84ec87
--- /dev/null
@@ -0,0 +1,197 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_waveform
+ * an element which represents a waveform.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_WAVEFORM_H
+#define GE_WAVEFORM_H
+
+
+#include <vector>
+#include <FL/Fl_Widget.H>
+
+
+class SampleChannel;
+
+
+class geWaveform : public Fl_Widget
+{
+private:
+
+       static const int FLAG_WIDTH  = 20;
+       static const int FLAG_HEIGHT = 20;
+       static const int BORDER      = 8;                               // window border <-> widget border
+       static const int SNAPPING    = 10;
+
+       /* data
+        * real graphic stuff from the underlying waveform */
+
+       struct data
+  {
+               int *sup;
+               int *inf;
+               int  size;
+       } data;
+
+       /* grid */
+
+       struct grid
+  {
+               bool snap;
+               int level;
+               std::vector<int> points;
+       } grid;
+
+       /* chan
+        * chan in use. */
+
+       SampleChannel *chan;
+
+       /* menuOpen
+        * is the menu open? */
+
+       bool menuOpen;
+
+       /* mouseOnStart/end
+        * is mouse on start or end flag? */
+
+       bool mouseOnStart();
+       bool mouseOnEnd();
+
+       /* mouseOnSelectionA/B
+        * as above, for the selection */
+
+       bool mouseOnSelectionA();
+       bool mouseOnSelectionB();
+
+       /* absolutePoint
+        * from a relative 'p' point (zoom affected) returns the same point
+        * zoom 1:1 based */
+
+       int absolutePoint(int p);
+
+       /* relativePoint
+        * from an absolute 'p' point (1:1 zoom), returns the same point zoom
+        * affected */
+
+       int relativePoint(int p);
+
+       /* straightSel
+        * helper function which flattens the selection if it was made from
+        * right to left (inverse selection) */
+
+       void straightSel();
+
+       /* freeData
+        * destroy any graphical buffer */
+
+       void freeData();
+
+       /* smaller
+        * is the waveform smaller than the parent window? */
+
+       bool smaller();
+
+  /* applySnap
+   * snap a point at 'pos' pixel */
+
+  int applySnap(int pos);
+
+public:
+
+       geWaveform(int x, int y, int w, int h, SampleChannel *ch, const char *l=0);
+       ~geWaveform();
+       void draw();
+       int  handle(int e);
+
+       /* alloc
+        * allocate memory for the picture */
+
+       int alloc(int datasize=0);
+
+       /* recalcPoints
+        * re-calc chanStart, chanEnd, ... */
+
+       void recalcPoints();
+
+       /* openEditMenu
+        * show edit menu on right-click */
+
+       void openEditMenu();
+
+       /* displayRatio
+        * how much of the waveform is being displayed on screen */
+
+       float displayRatio();
+
+       /* zoom
+        * type == 1 : zoom out, type == -1: zoom in */
+
+       void setZoom(int type);
+
+       /* strecthToWindow
+        * shrink or enlarge the waveform to match parent's width (gWaveTools) */
+
+       void stretchToWindow();
+
+       /* setGridLevel
+        * set a new frequency level for the grid. 0 means disabled. */
+
+       void setGridLevel(int l);
+
+  void setSnap(bool v) { grid.snap = v; }
+  bool getSnap()       { return grid.snap; }
+
+       inline int getSize() { return data.size; }
+
+       int  chanStart;
+       bool chanStartLit;
+       int  chanEnd;
+       bool chanEndLit;
+       bool pushed;
+       bool dragged;
+       bool resized;
+
+       float ratio;
+
+  /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */
+       int  mouseX;                                     // mouse pos for drag.n.drop
+       int  mouseY;
+
+       /* selectionA/B  = portion of the selected wave
+        * " " "" " _abs = selectionA/B not affected by zoom */
+       /** TODO - change selectionA to selectionA_rel
+           TODO - change selectionB to selectionB_rel */
+       int selectionA;
+       int selectionB;
+       int selectionA_abs;
+       int selectionB_abs;
+};
+
+
+#endif
diff --git a/src/gui/elems/soundMeter.cpp b/src/gui/elems/soundMeter.cpp
new file mode 100644 (file)
index 0000000..d0b27fe
--- /dev/null
@@ -0,0 +1,87 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cmath>
+#include <FL/fl_draw.H>
+#include "../../core/const.h"
+#include "../../core/kernelAudio.h"
+#include "soundMeter.h"
+
+
+using namespace giada::m;
+
+
+geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char *L)
+  : Fl_Box    (x, y, w, h, L),
+    clip      (false),
+    mixerPeak (0.0f),
+    peak      (0.0f),
+    dbLevel   (0.0f),
+    dbLevelOld(0.0f)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSoundMeter::draw()
+{
+  fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+  /* peak = the highest value inside the frame */
+
+  peak = 0.0f;
+  float tmp_peak = 0.0f;
+
+  tmp_peak = fabs(mixerPeak);
+  if (tmp_peak > peak)
+    peak = tmp_peak;
+
+  clip = peak >= 1.0f ? true : false; // 1.0f is considered clip
+
+
+  /*  dBFS (full scale) calculation, plus decay of -2dB per frame */
+
+  dbLevel = 20 * log10(peak);
+  if (dbLevel < dbLevelOld)
+    if (dbLevelOld > -G_DB_MIN_SCALE)
+      dbLevel = dbLevelOld - 2.0f;
+
+  dbLevelOld = dbLevel;
+
+  /* graphical part */
+
+  float px_level = 0.0f;
+  if (dbLevel < 0.0f)
+    px_level = ((w()/G_DB_MIN_SCALE) * dbLevel) + w();
+  else
+    px_level = w();
+
+  fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);
+  fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !kernelAudio::getStatus() ? COLOR_ALERT : COLOR_BD_0);
+}
diff --git a/src/gui/elems/soundMeter.h b/src/gui/elems/soundMeter.h
new file mode 100644 (file)
index 0000000..0b3768b
--- /dev/null
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 GE_SOUND_METER_H
+#define GE_SOUND_METER_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geSoundMeter : public Fl_Box
+{
+public:
+
+  geSoundMeter(int X, int Y, int W, int H, const char *L=0);
+
+  void draw() override;
+
+  bool clip;
+       float mixerPeak;        // peak from mixer
+
+private:
+
+       float peak;
+       float dbLevel;
+       float dbLevelOld;
+};
+
+
+#endif
index a80e63be53c008d1d650981544493f5c68646326..f02b575efe155439a7560cbc8a9360ecc26fc348 100644 (file)
@@ -4,7 +4,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #if defined(__linux__) || defined(__APPLE__)
        #include <unistd.h>
 #endif
+#include <FL/Fl.H>
 #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"
 #include "core/mixer.h"
+#include "core/clock.h"
 #include "core/mixerHandler.h"
 #include "core/kernelAudio.h"
 #include "core/kernelMidi.h"
 #include "core/pluginHost.h"
 
 
-/* global variables. Yeah, we are nasty */
-
 pthread_t     G_videoThread;
-KernelAudio   G_KernelAudio;
-Mixer         G_Mixer;
-Recorder      G_Recorder;
-KernelMidi    G_KernelMidi;
 bool          G_quit;
-bool           G_audio_status;
-bool          G_midiStatus;
-Patch_DEPR_   G_Patch_DEPR_;
-Patch         G_Patch;
-Conf          G_Conf;
-MidiMapConf   G_MidiMap;
 gdMainWindow *G_MainWin;
 
-#ifdef WITH_VST
-PluginHost G_PluginHost;
-#endif
-
 
 void *videoThreadCb(void *arg);
 
@@ -78,8 +63,9 @@ int main(int argc, char **argv)
        init_prepareKernelAudio();
        init_prepareKernelMIDI();
        init_startGUI(argc, argv);
-       Fl::lock();
-       pthread_create(&G_videoThread, NULL, videoThreadCb, NULL);
+
+  Fl::lock();
+       pthread_create(&G_videoThread, nullptr, videoThreadCb, nullptr);
        init_startKernelAudio();
 
 #ifdef WITH_VST
@@ -92,14 +78,14 @@ int main(int argc, char **argv)
        juce::shutdownJuce_GUI();
 #endif
 
-       pthread_join(G_videoThread, NULL);
+       pthread_join(G_videoThread, nullptr);
        return ret;
 }
 
 
 void *videoThreadCb(void *arg)
 {
-       if (G_audio_status)
+       if (giada::m::kernelAudio::getStatus())
                while (!G_quit) {
                        gu_refreshUI();
 #ifdef _WIN32
@@ -108,6 +94,6 @@ void *videoThreadCb(void *arg)
                        usleep(GUI_SLEEP);
 #endif
                }
-       pthread_exit(NULL);
+       pthread_exit(nullptr);
        return 0;
 }
index 7c064d0f1e0f5aa2cb1ca09607588cb454ca4b58..99a6ad10bb7c495d8bac3587cf80ece9f81218e3 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#ifndef G_UTILS_COCOA_H
+#define G_UTILS_COCOA_H
+
+
 /* fl_xid() from FLTK returns a pointer to NSWindow, but plugins on OS X want a
  * pointer to NSView. The function does the hard conversion. */
 
@@ -34,5 +38,8 @@ void *cocoa_getViewFromWindow(void *p);
 
 /* A bug on on OS X seems to misalign plugins' UI. The function takes care of
  * fixing the positioning. */
+
 void cocoa_setWindowSize(void *p, int w, int h);
+
+
+#endif
index cdf3e76b1c057dc0f1574a2d15e115646b4b6209..6c6f4b56d3cfee9609939872f208ebbd905bbfc5 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
diff --git a/src/utils/deps.cpp b/src/utils/deps.cpp
new file mode 100644 (file)
index 0000000..a732be8
--- /dev/null
@@ -0,0 +1,66 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <rtmidi/RtMidi.h>
+#include <sndfile.h>
+#include "../deps/rtaudio-mod/RtAudio.h"
+#include "deps.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace u     {
+namespace deps  
+{
+string getLibsndfileVersion()
+{
+  char buffer[128];
+  sf_command(NULL, SFC_GET_LIB_VERSION, buffer, sizeof(buffer));
+  return string(buffer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtAudioVersion()
+{
+  return RtAudio::getVersion();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtMidiVersion()
+{
+  return RtMidi::getVersion();
+}
+
+}}};  // giada::u::deps::
\ No newline at end of file
diff --git a/src/utils/deps.h b/src/utils/deps.h
new file mode 100644 (file)
index 0000000..ff3f331
--- /dev/null
@@ -0,0 +1,44 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_UTILS_DEPS_H
+#define G_UTILS_DEPS_H
+
+
+#include <string>
+
+
+namespace giada {
+namespace u     {
+namespace deps 
+{
+std::string getLibsndfileVersion();
+std::string getRtAudioVersion();
+std::string getRtMidiVersion();
+}}};  // giada::u::deps::
+
+#endif
\ No newline at end of file
index 23b238474c01951dbab671caf13237c014ed46a9..5d9e8af95b7a0ff51c0da4cce941fcf202ccfa50 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
 #include <cstdarg>
 #include <sys/stat.h>   // stat (gu_dirExists)
 #include <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <cstdlib>
+#ifdef __APPLE__  // our Clans still doesn't know about cstdint (c++11 stuff)
+       #include <stdint.h>
+#else
+       #include <cstdint>
+#endif
 #include <string>
-#include <string.h>
-#include <limits.h>
-#if defined(__APPLE__)
+#include <cstring>
+#include <climits>
+#ifdef __APPLE__
        #include <libgen.h>     // basename unix
        #include <pwd.h>        // getpwuid
 #endif
@@ -170,9 +174,9 @@ string gu_getCurrentPath()
 {
  char buf[PATH_MAX];
 #if defined(__WIN32)
-       if (_getcwd(buf, PATH_MAX) != NULL)
+       if (_getcwd(buf, PATH_MAX) != nullptr)
 #else
-       if (getcwd(buf, PATH_MAX) != NULL)
+       if (getcwd(buf, PATH_MAX) != nullptr)
 #endif
                return buf;
        else
@@ -252,7 +256,7 @@ string gu_getHomePath()
 #elif defined(__APPLE__)
 
        struct passwd *p = getpwuid(getuid());
-       if (p == NULL) {
+       if (p == nullptr) {
                gu_log("[gu_getHomePath] unable to fetch user infos\n");
                return "";
        }
index abb98618806671a32b2120c6e508575526ca20a2..cee9c81f9306c6a4d6fff817ee09e38f3faa9ea2 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef UTILS_H
-#define UTILS_H
+#ifndef G_UTILS_FS_H
+#define G_UTILS_FS_H
 
 
 #include <string>
-#include <vector>
 
 
-using std::string;
-using std::vector;
-
-
-bool gu_fileExists(const string &path);
-
-bool gu_dirExists(const string &path);
-
-bool gu_isDir(const string &path);
-
-bool gu_isProject(const string &path);
-
-bool gu_mkdir(const string &path);
-
-string gu_getCurrentPath();
-
-string gu_getHomePath();
-
-string gu_basename(const string &s);
-
-string gu_dirname(const string &s);
-
-string gu_getExt(const string &s);
-
-string gu_stripExt(const string &s);
-
-string gu_stripFileUrl(const string &s);
+bool gu_fileExists(const std::string &path);
+bool gu_dirExists(const std::string &path);
+bool gu_isDir(const std::string &path);
+bool gu_isProject(const std::string &path);
+bool gu_mkdir(const std::string &path);
+std::string gu_getCurrentPath();
+std::string gu_getHomePath();
+std::string gu_basename(const std::string &s);
+std::string gu_dirname(const std::string &s);
+std::string gu_getExt(const std::string &s);
+std::string gu_stripExt(const std::string &s);
+std::string gu_stripFileUrl(const std::string &s);
 
 
 #endif
index 715126d524d6f9f6d50f057abe290278c67b1e15..1b5a0441f7fe53973c200ccf30714596ef8aa105 100644 (file)
@@ -2,11 +2,9 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * gui_utils
- *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
+#include <string>
+#include <FL/fl_draw.H>
+#if defined(_WIN32)
+       #include "../ext/resource.h"
+#elif defined(__linux__)
+       #include <X11/xpm.h>
+#endif
 #include "../core/mixer.h"
-#include "../core/patch_DEPR_.h"
 #include "../core/recorder.h"
 #include "../core/wave.h"
+#include "../core/clock.h"
 #include "../core/pluginHost.h"
 #include "../core/channel.h"
 #include "../core/conf.h"
@@ -38,7 +43,7 @@
 #include "../gui/dialogs/gd_warnings.h"
 #include "../gui/dialogs/gd_mainWindow.h"
 #include "../gui/dialogs/gd_actionEditor.h"
-#include "../gui/elems/ge_window.h"
+#include "../gui/dialogs/window.h"
 #include "../gui/elems/mainWindow/mainIO.h"
 #include "../gui/elems/mainWindow/mainTimer.h"
 #include "../gui/elems/mainWindow/mainTransport.h"
 #include "gui.h"
 
 
-extern Mixer          G_Mixer;
-extern unsigned      G_beats;
-extern bool                 G_audio_status;
-extern Patch_DEPR_   G_patch;
-extern Conf          G_conf;
-extern uint32_t      G_time;
 extern gdMainWindow *G_MainWin;
-#ifdef WITH_VST
-extern PluginHost    G_PluginHost;
-#endif
+
+
+using std::string;
+using namespace giada::m;
 
 
 static int blinker = 0;
@@ -103,21 +103,21 @@ int gu_getBlinker()
 
 void gu_updateControls()
 {
-       for (unsigned i=0; i<G_Mixer.channels.size(); i++)
-               G_Mixer.channels.at(i)->guiChannel->update();
+       for (unsigned i=0; i<mixer::channels.size(); i++)
+               mixer::channels.at(i)->guiChannel->update();
 
-       G_MainWin->mainIO->setOutVol(G_Mixer.outVol);
-       G_MainWin->mainIO->setInVol(G_Mixer.inVol);
+       G_MainWin->mainIO->setOutVol(mixer::outVol);
+       G_MainWin->mainIO->setInVol(mixer::inVol);
 #ifdef WITH_VST
-       G_MainWin->mainIO->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0);
-       G_MainWin->mainIO->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0);
+       G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::MASTER_OUT)->size() > 0);
+       G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::MASTER_IN)->size() > 0);
 #endif
 
-       G_MainWin->mainTimer->setMeter(G_Mixer.beats, G_Mixer.bars);
-       G_MainWin->mainTimer->setBpm(G_Mixer.bpm);
+       G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
+       G_MainWin->mainTimer->setBpm(clock::getBpm());
 
-       G_MainWin->mainTransport->updatePlay(G_Mixer.running);
-       G_MainWin->mainTransport->updateMetronome(G_Mixer.metronome);
+       G_MainWin->mainTransport->updatePlay(clock::isRunning());
+       G_MainWin->mainTransport->updateMetronome(mixer::metronome);
 }
 
 
@@ -141,7 +141,7 @@ void gu_setFavicon(Fl_Window *w)
        fl_open_display();
        Pixmap p, mask;
        XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
-               (char **) giada_icon, &p, &mask, NULL);
+               (char **) giada_icon, &p, &mask, nullptr);
        w->icon((char *)p);
 
 #elif defined(_WIN32)
@@ -155,7 +155,7 @@ void gu_setFavicon(Fl_Window *w)
 /* -------------------------------------------------------------------------- */
 
 
-void gu_openSubWindow(gWindow *parent, gWindow *child, int id)
+void gu_openSubWindow(gdWindow *parent, gdWindow *child, int id)
 {
        if (parent->hasWindow(id)) {
                gu_log("[GU] parent has subwindow with id=%d, deleting\n", id);
@@ -185,12 +185,12 @@ void gu_refreshActionEditor()
 /* -------------------------------------------------------------------------- */
 
 
-gWindow *gu_getSubwindow(gWindow *parent, int id)
+gdWindow *gu_getSubwindow(gdWindow *parent, int id)
 {
        if (parent->hasWindow(id))
                return parent->getChild(id);
        else
-               return NULL;
+               return nullptr;
 }
 
 
@@ -212,6 +212,18 @@ void gu_closeAllSubwindows()
 /* -------------------------------------------------------------------------- */
 
 
+int gu_getStringWidth(const std::string &s)
+{
+  int w = 0;
+  int h = 0;
+  fl_measure(s.c_str(), w, h);
+  return w;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 string gu_removeFltkChars(const string &s)
 {
        string out = gu_replace(s, "/", "-");
index 921c9eab74c84ce01eac7e1fc7c79d4a9ecf3363..00a79945a8c5a910ffb962ef12e9967f2fceec56 100644 (file)
@@ -4,7 +4,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  *
  * -------------------------------------------------------------------------- */
 
-#ifndef GUI_UTILS_H
-#define GUI_UTILS_H
 
-#include <dirent.h>
-#include <string>
-#include <FL/x.H>
-#include <FL/Fl.H>
-#ifdef __APPLE__
-       #include <libgen.h>     // in osx, for basename() (but linux?)
-#endif
+#ifndef G_UTILS_GUI_H
+#define G_UTILS_GUI_H
 
-/* including stuff for the favicon */
 
-#if defined(_WIN32)
-       #include "../ext/resource.h"
-#elif defined(__linux__)
-       #include <X11/xpm.h>
-#endif
+#include <string>
 
 
-using std::string;
+class Fl_Window;
+class gdWindow;
 
 
 /* refresh
@@ -66,11 +55,11 @@ void gu_updateControls();
 /* update_win_label
  * update the name of the main window */
 
-void gu_updateMainWinLabel(const string &s);
+void gu_updateMainWinLabel(const std::string &s);
 
 void gu_setFavicon(Fl_Window *w);
 
-void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
+void gu_openSubWindow(gdWindow *parent, gdWindow *child, int id);
 
 /* refreshActionEditor
  * reload the action editor window by closing and reopening it. It's used
@@ -79,21 +68,22 @@ void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
 
 void gu_refreshActionEditor();
 
-
 /* closeAllSubwindows
  * close all subwindows attached to mainWin. */
 
 void gu_closeAllSubwindows();
 
-
 /* getSubwindow
- * return a pointer to an open subwindow, otherwise NULL. */
+ * return a pointer to an open subwindow, otherwise nullptr. */
 
-gWindow *gu_getSubwindow(class gWindow *parent, int id);
+gdWindow *gu_getSubwindow(gdWindow *parent, int id);
 
 /* removeFltkChars
  * Strip special chars used by FLTK to split menus into sub-menus. */
 
-string gu_removeFltkChars(const string &s);
+std::string gu_removeFltkChars(const std::string &s);
+
+int gu_getStringWidth(const std::string &s);
+
 
 #endif
index 3724f03ee3da80923378999584aab3146a303855..067d8bd95a5e74702c79e021cc4a777c2cbfd72b 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
index 14ece2974c6c126ada1fb644a2fc1b10866ed6a7..c7e2afc233daaf66193a4503afd89888c65dea77 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
@@ -27,8 +27,8 @@
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __LOG_H__
-#define __LOG_H__
+#ifndef G_UTILS_LOG_H
+#define G_UTILS_LOG_H
 
 
 /* init
diff --git a/src/utils/math.cpp b/src/utils/math.cpp
new file mode 100644 (file)
index 0000000..e89a21c
--- /dev/null
@@ -0,0 +1,44 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 <cmath>
+#include "math.h"
+
+
+float gu_linearToDB(float f)
+{
+  return 20 * std::log10(f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float gu_dBtoLinear(float f)
+{
+  return std::pow(10, f/20.0f); 
+}
diff --git a/src/utils/math.h b/src/utils/math.h
new file mode 100644 (file)
index 0000000..890e3bc
--- /dev/null
@@ -0,0 +1,37 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2017 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 G_UTILS_MATH_H
+#define G_UTILS_MATH_H
+
+
+float gu_linearToDB(float f);
+
+float gu_dBtoLinear(float f);
+
+
+#endif
index 603693e11504b2215a7d2a9afeaeed187ae1c5a5..0e5cc9c385c43a6885aaf963220a6514e80882ca 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#include <limits.h>
+#include <climits>
 #include <sstream>
 #include "string.h"
 
 
 using std::string;
+using std::vector;
 
 
 string gu_getRealPath(const string &path)
@@ -41,11 +42,11 @@ string gu_getRealPath(const string &path)
 
 #if defined(__linux__) || defined(__APPLE__)
 
-       char *buf = realpath(path.c_str(), NULL);
+       char *buf = realpath(path.c_str(), nullptr);
 
 #else // Windows
 
-       char *buf = _fullpath(NULL, path.c_str(), PATH_MAX);
+       char *buf = _fullpath(nullptr, path.c_str(), PATH_MAX);
 
 #endif
 
@@ -62,6 +63,7 @@ string gu_getRealPath(const string &path)
 
 string gu_itoa(int i)
 {
+    // TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
        std::stringstream out;
        out << i;
        return out.str();
index 88df1af8bc7a3901e0c09d64796d2398e603bd2f..a460a352bbc8bf700d6335358de80e91951c22e7 100644 (file)
@@ -6,7 +6,7 @@
  *
  * -----------------------------------------------------------------------------
  *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
  *
  * This file is part of Giada - Your Hardcore Loopmachine.
  *
  * -------------------------------------------------------------------------- */
 
 
-#ifndef __UTILS_STRING_H__
-#define __UTILS_STRING_H__
+#ifndef G_UTILS_STRING_H
+#define G_UTILS_STRING_H
 
 
 #include <string>
 #include <vector>
 
 
-using std::string;
-using std::vector;
+std::string gu_getRealPath(const std::string &path);
 
+std::string gu_replace(std::string in, const std::string &search,
+  const std::string &replace);
 
-string gu_getRealPath(const string &path);
+std::string gu_trim(const std::string &s);
 
-string gu_replace(string in, const string &search, const string &replace);
+// TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
+std::string gu_itoa(int i);
 
-string gu_trim(const string &s);
-
-string gu_itoa(int i);
-
-void gu_split(string in, string sep, vector<string> *v);
+void gu_split(std::string in, std::string sep, std::vector<std::string> *v);
 
 
 #endif
diff --git a/tests/catch.hpp b/tests/catch.hpp
deleted file mode 100644 (file)
index de61226..0000000
+++ /dev/null
@@ -1,9416 +0,0 @@
-/*
- *  Catch v1.2.1
- *  Generated: 2015-06-30 18:23:27.961086
- *  ----------------------------------------------------------
- *  This file has been merged from multiple headers. Please don't edit it directly
- *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
- *
- *  Distributed under the Boost Software License, Version 1.0. (See accompanying
- *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
-#define TWOBLUECUBES_CATCH_HPP_INCLUDED
-
-#ifdef __clang__
-#    pragma clang system_header
-#elif defined __GNUC__
-#    pragma GCC system_header
-#endif
-
-// #included from: internal/catch_suppress_warnings.h
-
-#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-#   ifdef __ICC // icpc defines the __clang__ macro
-#       pragma warning(push)
-#       pragma warning(disable: 161 1682)
-#   else // __ICC
-#       pragma clang diagnostic ignored "-Wglobal-constructors"
-#       pragma clang diagnostic ignored "-Wvariadic-macros"
-#       pragma clang diagnostic ignored "-Wc99-extensions"
-#       pragma clang diagnostic ignored "-Wunused-variable"
-#       pragma clang diagnostic push
-#       pragma clang diagnostic ignored "-Wpadded"
-#       pragma clang diagnostic ignored "-Wc++98-compat"
-#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
-#       pragma clang diagnostic ignored "-Wswitch-enum"
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic ignored "-Wvariadic-macros"
-#    pragma GCC diagnostic ignored "-Wunused-variable"
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wpadded"
-#endif
-
-#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
-#  define CATCH_IMPL
-#endif
-
-#ifdef CATCH_IMPL
-#  ifndef CLARA_CONFIG_MAIN
-#    define CLARA_CONFIG_MAIN_NOT_DEFINED
-#    define CLARA_CONFIG_MAIN
-#  endif
-#endif
-
-// #included from: internal/catch_notimplemented_exception.h
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
-
-// #included from: catch_common.h
-#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
-
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-
-#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
-#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
-
-#include <sstream>
-#include <stdexcept>
-#include <algorithm>
-
-// #included from: catch_compiler_capabilities.h
-#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
-
-// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
-// The following features are defined:
-//
-// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
-// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
-// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
-// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
-// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
-
-// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
-
-// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
-
-// In general each macro has a _NO_<feature name> form
-// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
-
-// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
-
-#ifdef __clang__
-
-#  if __has_feature(cxx_nullptr)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#  endif
-
-#  if __has_feature(cxx_noexcept)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#  endif
-
-#endif // __clang__
-
-////////////////////////////////////////////////////////////////////////////////
-// Borland
-#ifdef __BORLANDC__
-
-#endif // __BORLANDC__
-
-////////////////////////////////////////////////////////////////////////////////
-// EDG
-#ifdef __EDG_VERSION__
-
-#endif // __EDG_VERSION__
-
-////////////////////////////////////////////////////////////////////////////////
-// Digital Mars
-#ifdef __DMC__
-
-#endif // __DMC__
-
-////////////////////////////////////////////////////////////////////////////////
-// GCC
-#ifdef __GNUC__
-
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
-#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
-
-#endif // __GNUC__
-
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#ifdef _MSC_VER
-
-#if (_MSC_VER >= 1600)
-#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
-
-#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
-#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#endif
-
-#endif // _MSC_VER
-
-// Use variadic macros if the compiler supports them
-#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
-    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
-    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
-    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
-
-#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// C++ language feature support
-
-// catch all support for C++11
-#if (__cplusplus >= 201103L)
-
-#  define CATCH_CPP11_OR_GREATER
-
-#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#  endif
-
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#  endif
-
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#  endif
-
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-#  endif
-
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-#  endif
-
-#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-#  endif
-
-#endif // __cplusplus >= 201103L
-
-// Now set the actual defines based on the above + anything the user has configured
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_NULLPTR
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_NOEXCEPT
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_IS_ENUM
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_TUPLE
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
-#define CATCH_CONFIG_VARIADIC_MACROS
-#endif
-
-// noexcept support:
-#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
-#  define CATCH_NOEXCEPT noexcept
-#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
-#else
-#  define CATCH_NOEXCEPT throw()
-#  define CATCH_NOEXCEPT_IS(x)
-#endif
-
-namespace Catch {
-
-    class NonCopyable {
-#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        NonCopyable( NonCopyable const& )              = delete;
-        NonCopyable( NonCopyable && )                  = delete;
-        NonCopyable& operator = ( NonCopyable const& ) = delete;
-        NonCopyable& operator = ( NonCopyable && )     = delete;
-#else
-        NonCopyable( NonCopyable const& info );
-        NonCopyable& operator = ( NonCopyable const& );
-#endif
-
-    protected:
-        NonCopyable() {}
-        virtual ~NonCopyable();
-    };
-
-    class SafeBool {
-    public:
-        typedef void (SafeBool::*type)() const;
-
-        static type makeSafe( bool value ) {
-            return value ? &SafeBool::trueValue : 0;
-        }
-    private:
-        void trueValue() const {}
-    };
-
-    template<typename ContainerT>
-    inline void deleteAll( ContainerT& container ) {
-        typename ContainerT::const_iterator it = container.begin();
-        typename ContainerT::const_iterator itEnd = container.end();
-        for(; it != itEnd; ++it )
-            delete *it;
-    }
-    template<typename AssociativeContainerT>
-    inline void deleteAllValues( AssociativeContainerT& container ) {
-        typename AssociativeContainerT::const_iterator it = container.begin();
-        typename AssociativeContainerT::const_iterator itEnd = container.end();
-        for(; it != itEnd; ++it )
-            delete it->second;
-    }
-
-    bool startsWith( std::string const& s, std::string const& prefix );
-    bool endsWith( std::string const& s, std::string const& suffix );
-    bool contains( std::string const& s, std::string const& infix );
-    void toLowerInPlace( std::string& s );
-    std::string toLower( std::string const& s );
-    std::string trim( std::string const& str );
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
-
-    struct pluralise {
-        pluralise( std::size_t count, std::string const& label );
-
-        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
-
-        std::size_t m_count;
-        std::string m_label;
-    };
-
-    struct SourceLineInfo {
-
-        SourceLineInfo();
-        SourceLineInfo( char const* _file, std::size_t _line );
-        SourceLineInfo( SourceLineInfo const& other );
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        SourceLineInfo( SourceLineInfo && )                  = default;
-        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
-        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
-#  endif
-        bool empty() const;
-        bool operator == ( SourceLineInfo const& other ) const;
-        bool operator < ( SourceLineInfo const& other ) const;
-
-        std::string file;
-        std::size_t line;
-    };
-
-    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
-
-    // This is just here to avoid compiler warnings with macro constants and boolean literals
-    inline bool isTrue( bool value ){ return value; }
-    inline bool alwaysTrue() { return true; }
-    inline bool alwaysFalse() { return false; }
-
-    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
-
-    // Use this in variadic streaming macros to allow
-    //    >> +StreamEndStop
-    // as well as
-    //    >> stuff +StreamEndStop
-    struct StreamEndStop {
-        std::string operator+() {
-            return std::string();
-        }
-    };
-    template<typename T>
-    T const& operator + ( T const& value, StreamEndStop ) {
-        return value;
-    }
-}
-
-#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
-#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
-
-#include <ostream>
-
-namespace Catch {
-
-    class NotImplementedException : public std::exception
-    {
-    public:
-        NotImplementedException( SourceLineInfo const& lineInfo );
-        NotImplementedException( NotImplementedException const& ) {}
-
-        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
-
-        virtual const char* what() const CATCH_NOEXCEPT;
-
-    private:
-        std::string m_what;
-        SourceLineInfo m_lineInfo;
-    };
-
-} // end namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
-
-// #included from: internal/catch_context.h
-#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
-
-// #included from: catch_interfaces_generators.h
-#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    struct IGeneratorInfo {
-        virtual ~IGeneratorInfo();
-        virtual bool moveNext() = 0;
-        virtual std::size_t getCurrentIndex() const = 0;
-    };
-
-    struct IGeneratorsForTest {
-        virtual ~IGeneratorsForTest();
-
-        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
-        virtual bool moveNext() = 0;
-    };
-
-    IGeneratorsForTest* createGeneratorsForTest();
-
-} // end namespace Catch
-
-// #included from: catch_ptr.hpp
-#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
-    // An intrusive reference counting smart pointer.
-    // T must implement addRef() and release() methods
-    // typically implementing the IShared interface
-    template<typename T>
-    class Ptr {
-    public:
-        Ptr() : m_p( NULL ){}
-        Ptr( T* p ) : m_p( p ){
-            if( m_p )
-                m_p->addRef();
-        }
-        Ptr( Ptr const& other ) : m_p( other.m_p ){
-            if( m_p )
-                m_p->addRef();
-        }
-        ~Ptr(){
-            if( m_p )
-                m_p->release();
-        }
-        void reset() {
-            if( m_p )
-                m_p->release();
-            m_p = NULL;
-        }
-        Ptr& operator = ( T* p ){
-            Ptr temp( p );
-            swap( temp );
-            return *this;
-        }
-        Ptr& operator = ( Ptr const& other ){
-            Ptr temp( other );
-            swap( temp );
-            return *this;
-        }
-        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
-        T* get() { return m_p; }
-        const T* get() const{ return m_p; }
-        T& operator*() const { return *m_p; }
-        T* operator->() const { return m_p; }
-        bool operator !() const { return m_p == NULL; }
-        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
-
-    private:
-        T* m_p;
-    };
-
-    struct IShared : NonCopyable {
-        virtual ~IShared();
-        virtual void addRef() const = 0;
-        virtual void release() const = 0;
-    };
-
-    template<typename T = IShared>
-    struct SharedImpl : T {
-
-        SharedImpl() : m_rc( 0 ){}
-
-        virtual void addRef() const {
-            ++m_rc;
-        }
-        virtual void release() const {
-            if( --m_rc == 0 )
-                delete this;
-        }
-
-        mutable unsigned int m_rc;
-    };
-
-} // end namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <vector>
-#include <stdlib.h>
-
-namespace Catch {
-
-    class TestCase;
-    class Stream;
-    struct IResultCapture;
-    struct IRunner;
-    struct IGeneratorsForTest;
-    struct IConfig;
-
-    struct IContext
-    {
-        virtual ~IContext();
-
-        virtual IResultCapture* getResultCapture() = 0;
-        virtual IRunner* getRunner() = 0;
-        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
-        virtual bool advanceGeneratorsForCurrentTest() = 0;
-        virtual Ptr<IConfig const> getConfig() const = 0;
-    };
-
-    struct IMutableContext : IContext
-    {
-        virtual ~IMutableContext();
-        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
-        virtual void setRunner( IRunner* runner ) = 0;
-        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
-    };
-
-    IContext& getCurrentContext();
-    IMutableContext& getCurrentMutableContext();
-    void cleanUpContext();
-    Stream createStream( std::string const& streamName );
-
-}
-
-// #included from: internal/catch_test_registry.hpp
-#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
-
-// #included from: catch_interfaces_testcase.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
-
-#include <vector>
-
-namespace Catch {
-
-    class TestSpec;
-
-    struct ITestCase : IShared {
-        virtual void invoke () const = 0;
-    protected:
-        virtual ~ITestCase();
-    };
-
-    class TestCase;
-    struct IConfig;
-
-    struct ITestCaseRegistry {
-        virtual ~ITestCaseRegistry();
-        virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
-
-    };
-}
-
-namespace Catch {
-
-template<typename C>
-class MethodTestCase : public SharedImpl<ITestCase> {
-
-public:
-    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
-
-    virtual void invoke() const {
-        C obj;
-        (obj.*m_method)();
-    }
-
-private:
-    virtual ~MethodTestCase() {}
-
-    void (C::*m_method)();
-};
-
-typedef void(*TestFunction)();
-
-struct NameAndDesc {
-    NameAndDesc( const char* _name = "", const char* _description= "" )
-    : name( _name ), description( _description )
-    {}
-
-    const char* name;
-    const char* description;
-};
-
-struct AutoReg {
-
-    AutoReg(    TestFunction function,
-                SourceLineInfo const& lineInfo,
-                NameAndDesc const& nameAndDesc );
-
-    template<typename C>
-    AutoReg(    void (C::*method)(),
-                char const* className,
-                NameAndDesc const& nameAndDesc,
-                SourceLineInfo const& lineInfo ) {
-        registerTestCase(   new MethodTestCase<C>( method ),
-                            className,
-                            nameAndDesc,
-                            lineInfo );
-    }
-
-    void registerTestCase(  ITestCase* testCase,
-                            char const* className,
-                            NameAndDesc const& nameAndDesc,
-                            SourceLineInfo const& lineInfo );
-
-    ~AutoReg();
-
-private:
-    AutoReg( AutoReg const& );
-    void operator= ( AutoReg const& );
-};
-
-} // end namespace Catch
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TESTCASE( ... ) \
-        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
-        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
-        namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
-                void test(); \
-            }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
-        } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
-
-#else
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
-        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
-        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
-        namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
-                void test(); \
-            }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
-        } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
-
-#endif
-
-// #included from: internal/catch_capture.hpp
-#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
-
-// #included from: catch_result_builder.h
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
-
-// #included from: catch_result_type.h
-#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
-
-namespace Catch {
-
-    // ResultWas::OfType enum
-    struct ResultWas { enum OfType {
-        Unknown = -1,
-        Ok = 0,
-        Info = 1,
-        Warning = 2,
-
-        FailureBit = 0x10,
-
-        ExpressionFailed = FailureBit | 1,
-        ExplicitFailure = FailureBit | 2,
-
-        Exception = 0x100 | FailureBit,
-
-        ThrewException = Exception | 1,
-        DidntThrowException = Exception | 2,
-
-        FatalErrorCondition = 0x200 | FailureBit
-
-    }; };
-
-    inline bool isOk( ResultWas::OfType resultType ) {
-        return ( resultType & ResultWas::FailureBit ) == 0;
-    }
-    inline bool isJustInfo( int flags ) {
-        return flags == ResultWas::Info;
-    }
-
-    // ResultDisposition::Flags enum
-    struct ResultDisposition { enum Flags {
-        Normal = 0x01,
-
-        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
-        FalseTest = 0x04,           // Prefix expression with !
-        SuppressFail = 0x08         // Failures are reported but do not fail the test
-    }; };
-
-    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
-        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
-    }
-
-    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
-    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
-    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
-
-} // end namespace Catch
-
-// #included from: catch_assertionresult.h
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    struct AssertionInfo
-    {
-        AssertionInfo() {}
-        AssertionInfo(  std::string const& _macroName,
-                        SourceLineInfo const& _lineInfo,
-                        std::string const& _capturedExpression,
-                        ResultDisposition::Flags _resultDisposition );
-
-        std::string macroName;
-        SourceLineInfo lineInfo;
-        std::string capturedExpression;
-        ResultDisposition::Flags resultDisposition;
-    };
-
-    struct AssertionResultData
-    {
-        AssertionResultData() : resultType( ResultWas::Unknown ) {}
-
-        std::string reconstructedExpression;
-        std::string message;
-        ResultWas::OfType resultType;
-    };
-
-    class AssertionResult {
-    public:
-        AssertionResult();
-        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
-        ~AssertionResult();
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-         AssertionResult( AssertionResult const& )              = default;
-         AssertionResult( AssertionResult && )                  = default;
-         AssertionResult& operator = ( AssertionResult const& ) = default;
-         AssertionResult& operator = ( AssertionResult && )     = default;
-#  endif
-
-        bool isOk() const;
-        bool succeeded() const;
-        ResultWas::OfType getResultType() const;
-        bool hasExpression() const;
-        bool hasMessage() const;
-        std::string getExpression() const;
-        std::string getExpressionInMacro() const;
-        bool hasExpandedExpression() const;
-        std::string getExpandedExpression() const;
-        std::string getMessage() const;
-        SourceLineInfo getSourceInfo() const;
-        std::string getTestMacroName() const;
-
-    protected:
-        AssertionInfo m_info;
-        AssertionResultData m_resultData;
-    };
-
-} // end namespace Catch
-
-namespace Catch {
-
-    struct TestFailureException{};
-
-    template<typename T> class ExpressionLhs;
-
-    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
-
-    struct CopyableStream {
-        CopyableStream() {}
-        CopyableStream( CopyableStream const& other ) {
-            oss << other.oss.str();
-        }
-        CopyableStream& operator=( CopyableStream const& other ) {
-            oss.str("");
-            oss << other.oss.str();
-            return *this;
-        }
-        std::ostringstream oss;
-    };
-
-    class ResultBuilder {
-    public:
-        ResultBuilder(  char const* macroName,
-                        SourceLineInfo const& lineInfo,
-                        char const* capturedExpression,
-                        ResultDisposition::Flags resultDisposition );
-
-        template<typename T>
-        ExpressionLhs<T const&> operator <= ( T const& operand );
-        ExpressionLhs<bool> operator <= ( bool value );
-
-        template<typename T>
-        ResultBuilder& operator << ( T const& value ) {
-            m_stream.oss << value;
-            return *this;
-        }
-
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
-        ResultBuilder& setResultType( ResultWas::OfType result );
-        ResultBuilder& setResultType( bool result );
-        ResultBuilder& setLhs( std::string const& lhs );
-        ResultBuilder& setRhs( std::string const& rhs );
-        ResultBuilder& setOp( std::string const& op );
-
-        void endExpression();
-
-        std::string reconstructExpression() const;
-        AssertionResult build() const;
-
-        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
-        void captureResult( ResultWas::OfType resultType );
-        void captureExpression();
-        void react();
-        bool shouldDebugBreak() const;
-        bool allowThrows() const;
-
-    private:
-        AssertionInfo m_assertionInfo;
-        AssertionResultData m_data;
-        struct ExprComponents {
-            ExprComponents() : testFalse( false ) {}
-            bool testFalse;
-            std::string lhs, rhs, op;
-        } m_exprComponents;
-        CopyableStream m_stream;
-
-        bool m_shouldDebugBreak;
-        bool m_shouldThrow;
-    };
-
-} // namespace Catch
-
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
-
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#endif
-
-#include <cstddef>
-
-namespace Catch {
-namespace Internal {
-
-    enum Operator {
-        IsEqualTo,
-        IsNotEqualTo,
-        IsLessThan,
-        IsGreaterThan,
-        IsLessThanOrEqualTo,
-        IsGreaterThanOrEqualTo
-    };
-
-    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
-    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
-    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
-    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
-    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
-    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
-    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
-
-    template<typename T>
-    inline T& opCast(T const& t) { return const_cast<T&>(t); }
-
-// nullptr_t support based on pull request #154 from Konstantin Baumann
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
-    // So the compare overloads can be operator agnostic we convey the operator as a template
-    // enum, which is used to specialise an Evaluator for doing the comparison.
-    template<typename T1, typename T2, Operator Op>
-    class Evaluator{};
-
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return opCast( lhs ) ==  opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsNotEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) != opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) < opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsGreaterThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) > opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) >= opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) <= opCast( rhs );
-        }
-    };
-
-    template<Operator Op, typename T1, typename T2>
-    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
-        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
-    }
-
-    // This level of indirection allows us to specialise for integer types
-    // to avoid signed/ unsigned warnings
-
-    // "base" overload
-    template<Operator Op, typename T1, typename T2>
-    bool compare( T1 const& lhs, T2 const& rhs ) {
-        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
-    }
-
-    // unsigned X to int
-    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-
-    // unsigned X to long
-    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-
-    // int to unsigned X
-    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-
-    // long to unsigned X
-    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-
-    // pointer to long (when comparing against NULL)
-    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
-    }
-
-    // pointer to int (when comparing against NULL)
-    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
-    }
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    // pointer to nullptr_t (when comparing against nullptr)
-    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
-    }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
-} // end of namespace Internal
-} // end of namespace Catch
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-// #included from: catch_tostring.h
-#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
-
-#include <sstream>
-#include <iomanip>
-#include <limits>
-#include <vector>
-#include <cstddef>
-
-#ifdef __OBJC__
-// #included from: catch_objc_arc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
-
-#import <Foundation/Foundation.h>
-
-#ifdef __has_feature
-#define CATCH_ARC_ENABLED __has_feature(objc_arc)
-#else
-#define CATCH_ARC_ENABLED 0
-#endif
-
-void arcSafeRelease( NSObject* obj );
-id performOptionalSelector( id obj, SEL sel );
-
-#if !CATCH_ARC_ENABLED
-inline void arcSafeRelease( NSObject* obj ) {
-    [obj release];
-}
-inline id performOptionalSelector( id obj, SEL sel ) {
-    if( [obj respondsToSelector: sel] )
-        return [obj performSelector: sel];
-    return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED
-#define CATCH_ARC_STRONG
-#else
-inline void arcSafeRelease( NSObject* ){}
-inline id performOptionalSelector( id obj, SEL sel ) {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-#endif
-    if( [obj respondsToSelector: sel] )
-        return [obj performSelector: sel];
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-    return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
-#define CATCH_ARC_STRONG __strong
-#endif
-
-#endif
-
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-#include <tuple>
-#endif
-
-#ifdef CATCH_CONFIG_CPP11_IS_ENUM
-#include <type_traits>
-#endif
-
-namespace Catch {
-
-// Why we're here.
-template<typename T>
-std::string toString( T const& value );
-
-// Built in overloads
-
-std::string toString( std::string const& value );
-std::string toString( std::wstring const& value );
-std::string toString( const char* const value );
-std::string toString( char* const value );
-std::string toString( const wchar_t* const value );
-std::string toString( wchar_t* const value );
-std::string toString( int value );
-std::string toString( unsigned long value );
-std::string toString( unsigned int value );
-std::string toString( const double value );
-std::string toString( const float value );
-std::string toString( bool value );
-std::string toString( char value );
-std::string toString( signed char value );
-std::string toString( unsigned char value );
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t );
-#endif
-
-#ifdef __OBJC__
-    std::string toString( NSString const * const& nsstring );
-    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
-    std::string toString( NSObject* const& nsObject );
-#endif
-
-namespace Detail {
-
-    extern std::string unprintableString;
-
-    struct BorgType {
-        template<typename T> BorgType( T const& );
-    };
-
-    struct TrueType { char sizer[1]; };
-    struct FalseType { char sizer[2]; };
-
-    TrueType& testStreamable( std::ostream& );
-    FalseType testStreamable( FalseType );
-
-    FalseType operator<<( std::ostream const&, BorgType const& );
-
-    template<typename T>
-    struct IsStreamInsertable {
-        static std::ostream &s;
-        static T  const&t;
-        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
-    };
-
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
-    template<typename T,
-             bool IsEnum = std::is_enum<T>::value
-             >
-    struct EnumStringMaker
-    {
-        static std::string convert( T const& ) { return unprintableString; }
-    };
-
-    template<typename T>
-    struct EnumStringMaker<T,true>
-    {
-        static std::string convert( T const& v )
-        {
-            return ::Catch::toString(
-                static_cast<typename std::underlying_type<T>::type>(v)
-                );
-        }
-    };
-#endif
-    template<bool C>
-    struct StringMakerBase {
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
-        template<typename T>
-        static std::string convert( T const& v )
-        {
-            return EnumStringMaker<T>::convert( v );
-        }
-#else
-        template<typename T>
-        static std::string convert( T const& ) { return unprintableString; }
-#endif
-    };
-
-    template<>
-    struct StringMakerBase<true> {
-        template<typename T>
-        static std::string convert( T const& _value ) {
-            std::ostringstream oss;
-            oss << _value;
-            return oss.str();
-        }
-    };
-
-    std::string rawMemoryToString( const void *object, std::size_t size );
-
-    template<typename T>
-    inline std::string rawMemoryToString( const T& object ) {
-      return rawMemoryToString( &object, sizeof(object) );
-    }
-
-} // end namespace Detail
-
-template<typename T>
-struct StringMaker :
-    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
-
-template<typename T>
-struct StringMaker<T*> {
-    template<typename U>
-    static std::string convert( U* p ) {
-        if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
-        else
-            return Detail::rawMemoryToString( p );
-    }
-};
-
-template<typename R, typename C>
-struct StringMaker<R C::*> {
-    static std::string convert( R C::* p ) {
-        if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
-        else
-            return Detail::rawMemoryToString( p );
-    }
-};
-
-namespace Detail {
-    template<typename InputIterator>
-    std::string rangeToString( InputIterator first, InputIterator last );
-}
-
-//template<typename T, typename Allocator>
-//struct StringMaker<std::vector<T, Allocator> > {
-//    static std::string convert( std::vector<T,Allocator> const& v ) {
-//        return Detail::rangeToString( v.begin(), v.end() );
-//    }
-//};
-
-template<typename T, typename Allocator>
-std::string toString( std::vector<T,Allocator> const& v ) {
-    return Detail::rangeToString( v.begin(), v.end() );
-}
-
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-
-// toString for tuples
-namespace TupleDetail {
-  template<
-      typename Tuple,
-      std::size_t N = 0,
-      bool = (N < std::tuple_size<Tuple>::value)
-      >
-  struct ElementPrinter {
-      static void print( const Tuple& tuple, std::ostream& os )
-      {
-          os << ( N ? ", " : " " )
-             << Catch::toString(std::get<N>(tuple));
-          ElementPrinter<Tuple,N+1>::print(tuple,os);
-      }
-  };
-
-  template<
-      typename Tuple,
-      std::size_t N
-      >
-  struct ElementPrinter<Tuple,N,false> {
-      static void print( const Tuple&, std::ostream& ) {}
-  };
-
-}
-
-template<typename ...Types>
-struct StringMaker<std::tuple<Types...>> {
-
-    static std::string convert( const std::tuple<Types...>& tuple )
-    {
-        std::ostringstream os;
-        os << '{';
-        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
-        os << " }";
-        return os.str();
-    }
-};
-#endif // CATCH_CONFIG_CPP11_TUPLE
-
-namespace Detail {
-    template<typename T>
-    std::string makeString( T const& value ) {
-        return StringMaker<T>::convert( value );
-    }
-} // end namespace Detail
-
-/// \brief converts any type to a string
-///
-/// The default template forwards on to ostringstream - except when an
-/// ostringstream overload does not exist - in which case it attempts to detect
-/// that and writes {?}.
-/// Overload (not specialise) this template for custom typs that you don't want
-/// to provide an ostream overload for.
-template<typename T>
-std::string toString( T const& value ) {
-    return StringMaker<T>::convert( value );
-}
-
-    namespace Detail {
-    template<typename InputIterator>
-    std::string rangeToString( InputIterator first, InputIterator last ) {
-        std::ostringstream oss;
-        oss << "{ ";
-        if( first != last ) {
-            oss << Catch::toString( *first );
-            for( ++first ; first != last ; ++first )
-                oss << ", " << Catch::toString( *first );
-        }
-        oss << " }";
-        return oss.str();
-    }
-}
-
-} // end namespace Catch
-
-namespace Catch {
-
-// Wraps the LHS of an expression and captures the operator and RHS (if any) -
-// wrapping them all in a ResultBuilder object
-template<typename T>
-class ExpressionLhs {
-    ExpressionLhs& operator = ( ExpressionLhs const& );
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-    ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
-#  endif
-
-public:
-    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-    ExpressionLhs( ExpressionLhs const& ) = default;
-    ExpressionLhs( ExpressionLhs && )     = default;
-#  endif
-
-    template<typename RhsT>
-    ResultBuilder& operator == ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsEqualTo>( rhs );
-    }
-
-    template<typename RhsT>
-    ResultBuilder& operator != ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsNotEqualTo>( rhs );
-    }
-
-    template<typename RhsT>
-    ResultBuilder& operator < ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsLessThan>( rhs );
-    }
-
-    template<typename RhsT>
-    ResultBuilder& operator > ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsGreaterThan>( rhs );
-    }
-
-    template<typename RhsT>
-    ResultBuilder& operator <= ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
-    }
-
-    template<typename RhsT>
-    ResultBuilder& operator >= ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
-    }
-
-    ResultBuilder& operator == ( bool rhs ) {
-        return captureExpression<Internal::IsEqualTo>( rhs );
-    }
-
-    ResultBuilder& operator != ( bool rhs ) {
-        return captureExpression<Internal::IsNotEqualTo>( rhs );
-    }
-
-    void endExpression() {
-        bool value = m_lhs ? true : false;
-        m_rb
-            .setLhs( Catch::toString( value ) )
-            .setResultType( value )
-            .endExpression();
-    }
-
-    // Only simple binary expressions are allowed on the LHS.
-    // If more complex compositions are required then place the sub expression in parentheses
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
-    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
-private:
-    template<Internal::Operator Op, typename RhsT>
-    ResultBuilder& captureExpression( RhsT const& rhs ) {
-        return m_rb
-            .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
-            .setLhs( Catch::toString( m_lhs ) )
-            .setRhs( Catch::toString( rhs ) )
-            .setOp( Internal::OperatorTraits<Op>::getName() );
-    }
-
-private:
-    ResultBuilder& m_rb;
-    T m_lhs;
-};
-
-} // end namespace Catch
-
-
-namespace Catch {
-
-    template<typename T>
-    inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
-        return ExpressionLhs<T const&>( *this, operand );
-    }
-
-    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
-        return ExpressionLhs<bool>( *this, value );
-    }
-
-} // namespace Catch
-
-// #included from: catch_message.h
-#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    struct MessageInfo {
-        MessageInfo(    std::string const& _macroName,
-                        SourceLineInfo const& _lineInfo,
-                        ResultWas::OfType _type );
-
-        std::string macroName;
-        SourceLineInfo lineInfo;
-        ResultWas::OfType type;
-        std::string message;
-        unsigned int sequence;
-
-        bool operator == ( MessageInfo const& other ) const {
-            return sequence == other.sequence;
-        }
-        bool operator < ( MessageInfo const& other ) const {
-            return sequence < other.sequence;
-        }
-    private:
-        static unsigned int globalCount;
-    };
-
-    struct MessageBuilder {
-        MessageBuilder( std::string const& macroName,
-                        SourceLineInfo const& lineInfo,
-                        ResultWas::OfType type )
-        : m_info( macroName, lineInfo, type )
-        {}
-
-        template<typename T>
-        MessageBuilder& operator << ( T const& value ) {
-            m_stream << value;
-            return *this;
-        }
-
-        MessageInfo m_info;
-        std::ostringstream m_stream;
-    };
-
-    class ScopedMessage {
-    public:
-        ScopedMessage( MessageBuilder const& builder );
-        ScopedMessage( ScopedMessage const& other );
-        ~ScopedMessage();
-
-        MessageInfo m_info;
-    };
-
-} // end namespace Catch
-
-// #included from: catch_interfaces_capture.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    class TestCase;
-    class AssertionResult;
-    struct AssertionInfo;
-    struct SectionInfo;
-    struct MessageInfo;
-    class ScopedMessageBuilder;
-    struct Counts;
-
-    struct IResultCapture {
-
-        virtual ~IResultCapture();
-
-        virtual void assertionEnded( AssertionResult const& result ) = 0;
-        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
-                                        Counts& assertions ) = 0;
-        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
-        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
-        virtual void popScopedMessage( MessageInfo const& message ) = 0;
-
-        virtual std::string getCurrentTestName() const = 0;
-        virtual const AssertionResult* getLastResult() const = 0;
-
-        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
-    };
-
-    IResultCapture& getResultCapture();
-}
-
-// #included from: catch_debugger.h
-#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
-
-// #included from: catch_platform.h
-#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
-
-#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_MAC
-#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_IPHONE
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
-#define CATCH_PLATFORM_WINDOWS
-#endif
-
-#include <string>
-
-namespace Catch{
-
-    bool isDebuggerActive();
-    void writeToDebugConsole( std::string const& text );
-}
-
-#ifdef CATCH_PLATFORM_MAC
-
-    // The following code snippet based on:
-    // http://cocoawithlove.com/2008/03/break-into-debugger.html
-    #ifdef DEBUG
-        #if defined(__ppc64__) || defined(__ppc__)
-            #define CATCH_BREAK_INTO_DEBUGGER() \
-                if( Catch::isDebuggerActive() ) { \
-                    __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
-                    : : : "memory","r0","r3","r4" ); \
-                }
-        #else
-            #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
-        #endif
-    #endif
-
-#elif defined(_MSC_VER)
-    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
-    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
-#endif
-
-#ifndef CATCH_BREAK_INTO_DEBUGGER
-#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
-#endif
-
-// #included from: catch_interfaces_runner.h
-#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
-
-namespace Catch {
-    class TestCase;
-
-    struct IRunner {
-        virtual ~IRunner();
-        virtual bool aborting() const = 0;
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// In the event of a failure works out if the debugger needs to be invoked
-// and/or an exception thrown and takes appropriate action.
-// This needs to be done as a macro so the debugger will stop in the user
-// source code rather than in Catch library code
-#define INTERNAL_CATCH_REACT( resultBuilder ) \
-    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
-    resultBuilder.react();
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
-        try { \
-            ( __catchResult <= expr ).endExpression(); \
-        } \
-        catch( ... ) { \
-            __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
-        } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
-    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
-    if( Catch::getResultCapture().getLastResult()->succeeded() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
-    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
-    if( !Catch::getResultCapture().getLastResult()->succeeded() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
-        try { \
-            expr; \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
-        } \
-        catch( ... ) { \
-            __catchResult.useActiveException( resultDisposition ); \
-        } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
-        if( __catchResult.allowThrows() ) \
-            try { \
-                expr; \
-                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
-            } \
-            catch( ... ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
-            } \
-        else \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
-        if( __catchResult.allowThrows() ) \
-            try { \
-                expr; \
-                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
-            } \
-            catch( exceptionType ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
-            } \
-            catch( ... ) { \
-                __catchResult.useActiveException( resultDisposition ); \
-            } \
-        else \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
-        do { \
-            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
-            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
-            __catchResult.captureResult( messageType ); \
-            INTERNAL_CATCH_REACT( __catchResult ) \
-        } while( Catch::alwaysFalse() )
-#else
-    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
-        do { \
-            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
-            __catchResult << log + ::Catch::StreamEndStop(); \
-            __catchResult.captureResult( messageType ); \
-            INTERNAL_CATCH_REACT( __catchResult ) \
-        } while( Catch::alwaysFalse() )
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_INFO( log, macroName ) \
-    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
-        try { \
-            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
-            __catchResult \
-                .setLhs( Catch::toString( arg ) ) \
-                .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
-                .setOp( "matches" ) \
-                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
-            __catchResult.captureExpression(); \
-        } catch( ... ) { \
-            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
-        } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
-
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
-
-namespace Catch {
-
-    struct SectionInfo {
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name,
-                std::string const& _description = std::string() );
-
-        std::string name;
-        std::string description;
-        SourceLineInfo lineInfo;
-    };
-
-} // end namespace Catch
-
-// #included from: catch_totals.hpp
-#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
-
-#include <cstddef>
-
-namespace Catch {
-
-    struct Counts {
-        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
-
-        Counts operator - ( Counts const& other ) const {
-            Counts diff;
-            diff.passed = passed - other.passed;
-            diff.failed = failed - other.failed;
-            diff.failedButOk = failedButOk - other.failedButOk;
-            return diff;
-        }
-        Counts& operator += ( Counts const& other ) {
-            passed += other.passed;
-            failed += other.failed;
-            failedButOk += other.failedButOk;
-            return *this;
-        }
-
-        std::size_t total() const {
-            return passed + failed + failedButOk;
-        }
-        bool allPassed() const {
-            return failed == 0 && failedButOk == 0;
-        }
-        bool allOk() const {
-            return failed == 0;
-        }
-
-        std::size_t passed;
-        std::size_t failed;
-        std::size_t failedButOk;
-    };
-
-    struct Totals {
-
-        Totals operator - ( Totals const& other ) const {
-            Totals diff;
-            diff.assertions = assertions - other.assertions;
-            diff.testCases = testCases - other.testCases;
-            return diff;
-        }
-
-        Totals delta( Totals const& prevTotals ) const {
-            Totals diff = *this - prevTotals;
-            if( diff.assertions.failed > 0 )
-                ++diff.testCases.failed;
-            else if( diff.assertions.failedButOk > 0 )
-                ++diff.testCases.failedButOk;
-            else
-                ++diff.testCases.passed;
-            return diff;
-        }
-
-        Totals& operator += ( Totals const& other ) {
-            assertions += other.assertions;
-            testCases += other.testCases;
-            return *this;
-        }
-
-        Counts assertions;
-        Counts testCases;
-    };
-}
-
-// #included from: catch_timer.h
-#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
-
-#ifdef CATCH_PLATFORM_WINDOWS
-typedef unsigned long long uint64_t;
-#else
-#include <stdint.h>
-#endif
-
-namespace Catch {
-
-    class Timer {
-    public:
-        Timer() : m_ticks( 0 ) {}
-        void start();
-        unsigned int getElapsedMicroseconds() const;
-        unsigned int getElapsedMilliseconds() const;
-        double getElapsedSeconds() const;
-
-    private:
-        uint64_t m_ticks;
-    };
-
-} // namespace Catch
-
-#include <string>
-
-namespace Catch {
-
-    class Section : NonCopyable {
-    public:
-        Section( SectionInfo const& info );
-        ~Section();
-
-        // This indicates whether the section should be executed or not
-        operator bool() const;
-
-    private:
-        SectionInfo m_info;
-
-        std::string m_name;
-        Counts m_assertions;
-        bool m_sectionIncluded;
-        Timer m_timer;
-    };
-
-} // end namespace Catch
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define INTERNAL_CATCH_SECTION( ... ) \
-        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
-#else
-    #define INTERNAL_CATCH_SECTION( name, desc ) \
-        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
-#endif
-
-// #included from: internal/catch_generators.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
-
-#include <iterator>
-#include <vector>
-#include <string>
-#include <stdlib.h>
-
-namespace Catch {
-
-template<typename T>
-struct IGenerator {
-    virtual ~IGenerator() {}
-    virtual T getValue( std::size_t index ) const = 0;
-    virtual std::size_t size () const = 0;
-};
-
-template<typename T>
-class BetweenGenerator : public IGenerator<T> {
-public:
-    BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
-
-    virtual T getValue( std::size_t index ) const {
-        return m_from+static_cast<int>( index );
-    }
-
-    virtual std::size_t size() const {
-        return static_cast<std::size_t>( 1+m_to-m_from );
-    }
-
-private:
-
-    T m_from;
-    T m_to;
-};
-
-template<typename T>
-class ValuesGenerator : public IGenerator<T> {
-public:
-    ValuesGenerator(){}
-
-    void add( T value ) {
-        m_values.push_back( value );
-    }
-
-    virtual T getValue( std::size_t index ) const {
-        return m_values[index];
-    }
-
-    virtual std::size_t size() const {
-        return m_values.size();
-    }
-
-private:
-    std::vector<T> m_values;
-};
-
-template<typename T>
-class CompositeGenerator {
-public:
-    CompositeGenerator() : m_totalSize( 0 ) {}
-
-    // *** Move semantics, similar to auto_ptr ***
-    CompositeGenerator( CompositeGenerator& other )
-    :   m_fileInfo( other.m_fileInfo ),
-        m_totalSize( 0 )
-    {
-        move( other );
-    }
-
-    CompositeGenerator& setFileInfo( const char* fileInfo ) {
-        m_fileInfo = fileInfo;
-        return *this;
-    }
-
-    ~CompositeGenerator() {
-        deleteAll( m_composed );
-    }
-
-    operator T () const {
-        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
-
-        typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
-        typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
-        for( size_t index = 0; it != itEnd; ++it )
-        {
-            const IGenerator<T>* generator = *it;
-            if( overallIndex >= index && overallIndex < index + generator->size() )
-            {
-                return generator->getValue( overallIndex-index );
-            }
-            index += generator->size();
-        }
-        CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
-        return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
-    }
-
-    void add( const IGenerator<T>* generator ) {
-        m_totalSize += generator->size();
-        m_composed.push_back( generator );
-    }
-
-    CompositeGenerator& then( CompositeGenerator& other ) {
-        move( other );
-        return *this;
-    }
-
-    CompositeGenerator& then( T value ) {
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( value );
-        add( valuesGen );
-        return *this;
-    }
-
-private:
-
-    void move( CompositeGenerator& other ) {
-        std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
-        m_totalSize += other.m_totalSize;
-        other.m_composed.clear();
-    }
-
-    std::vector<const IGenerator<T>*> m_composed;
-    std::string m_fileInfo;
-    size_t m_totalSize;
-};
-
-namespace Generators
-{
-    template<typename T>
-    CompositeGenerator<T> between( T from, T to ) {
-        CompositeGenerator<T> generators;
-        generators.add( new BetweenGenerator<T>( from, to ) );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2 ) {
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2, T val3 ){
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        valuesGen->add( val3 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        valuesGen->add( val3 );
-        valuesGen->add( val4 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-} // end namespace Generators
-
-using namespace Generators;
-
-} // end namespace Catch
-
-#define INTERNAL_CATCH_LINESTR2( line ) #line
-#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
-
-#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
-
-// #included from: internal/catch_interfaces_exception.h
-#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
-
-#include <string>
-// #included from: catch_interfaces_registry_hub.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    class TestCase;
-    struct ITestCaseRegistry;
-    struct IExceptionTranslatorRegistry;
-    struct IExceptionTranslator;
-    struct IReporterRegistry;
-    struct IReporterFactory;
-
-    struct IRegistryHub {
-        virtual ~IRegistryHub();
-
-        virtual IReporterRegistry const& getReporterRegistry() const = 0;
-        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
-        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
-    };
-
-    struct IMutableRegistryHub {
-        virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
-        virtual void registerTest( TestCase const& testInfo ) = 0;
-        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
-    };
-
-    IRegistryHub& getRegistryHub();
-    IMutableRegistryHub& getMutableRegistryHub();
-    void cleanUp();
-    std::string translateActiveException();
-
-}
-
-
-namespace Catch {
-
-    typedef std::string(*exceptionTranslateFunction)();
-
-    struct IExceptionTranslator {
-        virtual ~IExceptionTranslator();
-        virtual std::string translate() const = 0;
-    };
-
-    struct IExceptionTranslatorRegistry {
-        virtual ~IExceptionTranslatorRegistry();
-
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
-
-            ExceptionTranslator( std::string(*translateFunction)( T& ) )
-            : m_translateFunction( translateFunction )
-            {}
-
-            virtual std::string translate() const {
-                try {
-                    throw;
-                }
-                catch( T& ex ) {
-                    return m_translateFunction( ex );
-                }
-            }
-
-        protected:
-            std::string(*m_translateFunction)( T& );
-        };
-
-    public:
-        template<typename T>
-        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
-    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
-    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
-
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
-
-#include <cmath>
-#include <limits>
-
-namespace Catch {
-namespace Detail {
-
-    class Approx {
-    public:
-        explicit Approx ( double value )
-        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-            m_scale( 1.0 ),
-            m_value( value )
-        {}
-
-        Approx( Approx const& other )
-        :   m_epsilon( other.m_epsilon ),
-            m_scale( other.m_scale ),
-            m_value( other.m_value )
-        {}
-
-        static Approx custom() {
-            return Approx( 0 );
-        }
-
-        Approx operator()( double value ) {
-            Approx approx( value );
-            approx.epsilon( m_epsilon );
-            approx.scale( m_scale );
-            return approx;
-        }
-
-        friend bool operator == ( double lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
-        }
-
-        friend bool operator == ( Approx const& lhs, double rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        friend bool operator != ( double lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        friend bool operator != ( Approx const& lhs, double rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        Approx& epsilon( double newEpsilon ) {
-            m_epsilon = newEpsilon;
-            return *this;
-        }
-
-        Approx& scale( double newScale ) {
-            m_scale = newScale;
-            return *this;
-        }
-
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << "Approx( " << Catch::toString( m_value ) << " )";
-            return oss.str();
-        }
-
-    private:
-        double m_epsilon;
-        double m_scale;
-        double m_value;
-    };
-}
-
-template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
-    return value.toString();
-}
-
-} // end namespace Catch
-
-// #included from: internal/catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-    template<typename ExpressionT>
-    struct Matcher : SharedImpl<IShared>
-    {
-        typedef ExpressionT ExpressionType;
-
-        virtual ~Matcher() {}
-        virtual Ptr<Matcher> clone() const = 0;
-        virtual bool match( ExpressionT const& expr ) const = 0;
-        virtual std::string toString() const = 0;
-    };
-
-    template<typename DerivedT, typename ExpressionT>
-    struct MatcherImpl : Matcher<ExpressionT> {
-
-        virtual Ptr<Matcher<ExpressionT> > clone() const {
-            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
-        }
-    };
-
-    namespace Generic {
-
-        template<typename ExpressionT>
-        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AllOf() {}
-            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AllOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( !m_matchers[i]->match( expr ) )
-                        return false;
-                return true;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " and ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-        template<typename ExpressionT>
-        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AnyOf() {}
-            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( m_matchers[i]->match( expr ) )
-                        return true;
-                return false;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " or ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-    }
-
-    namespace StdString {
-
-        inline std::string makeString( std::string const& str ) { return str; }
-        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
-
-        struct Equals : MatcherImpl<Equals, std::string> {
-            Equals( std::string const& str ) : m_str( str ){}
-            Equals( Equals const& other ) : m_str( other.m_str ){}
-
-            virtual ~Equals();
-
-            virtual bool match( std::string const& expr ) const {
-                return m_str == expr;
-            }
-            virtual std::string toString() const {
-                return "equals: \"" + m_str + "\"";
-            }
-
-            std::string m_str;
-        };
-
-        struct Contains : MatcherImpl<Contains, std::string> {
-            Contains( std::string const& substr ) : m_substr( substr ){}
-            Contains( Contains const& other ) : m_substr( other.m_substr ){}
-
-            virtual ~Contains();
-
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) != std::string::npos;
-            }
-            virtual std::string toString() const {
-                return "contains: \"" + m_substr + "\"";
-            }
-
-            std::string m_substr;
-        };
-
-        struct StartsWith : MatcherImpl<StartsWith, std::string> {
-            StartsWith( std::string const& substr ) : m_substr( substr ){}
-            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
-
-            virtual ~StartsWith();
-
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == 0;
-            }
-            virtual std::string toString() const {
-                return "starts with: \"" + m_substr + "\"";
-            }
-
-            std::string m_substr;
-        };
-
-        struct EndsWith : MatcherImpl<EndsWith, std::string> {
-            EndsWith( std::string const& substr ) : m_substr( substr ){}
-            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
-
-            virtual ~EndsWith();
-
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == expr.size() - m_substr.size();
-            }
-            virtual std::string toString() const {
-                return "ends with: \"" + m_substr + "\"";
-            }
-
-            std::string m_substr;
-        };
-    } // namespace StdString
-    } // namespace Impl
-
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-
-    inline Impl::StdString::Equals      Equals( std::string const& str ) {
-        return Impl::StdString::Equals( str );
-    }
-    inline Impl::StdString::Equals      Equals( const char* str ) {
-        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
-    }
-    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
-        return Impl::StdString::Contains( substr );
-    }
-    inline Impl::StdString::Contains    Contains( const char* substr ) {
-        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
-        return Impl::StdString::StartsWith( substr );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
-        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
-        return Impl::StdString::EndsWith( substr );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
-        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
-    }
-
-} // namespace Matchers
-
-using namespace Matchers;
-
-} // namespace Catch
-
-// #included from: internal/catch_interfaces_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
-
-// #included from: catch_tag_alias.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    struct TagAlias {
-        TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
-
-        std::string tag;
-        SourceLineInfo lineInfo;
-    };
-
-    struct RegistrarForTagAliases {
-        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
-    };
-
-} // end namespace Catch
-
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
-// #included from: catch_option.hpp
-#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
-
-namespace Catch {
-
-    // An optional type
-    template<typename T>
-    class Option {
-    public:
-        Option() : nullableValue( NULL ) {}
-        Option( T const& _value )
-        : nullableValue( new( storage ) T( _value ) )
-        {}
-        Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
-        {}
-
-        ~Option() {
-            reset();
-        }
-
-        Option& operator= ( Option const& _other ) {
-            if( &_other != this ) {
-                reset();
-                if( _other )
-                    nullableValue = new( storage ) T( *_other );
-            }
-            return *this;
-        }
-        Option& operator = ( T const& _value ) {
-            reset();
-            nullableValue = new( storage ) T( _value );
-            return *this;
-        }
-
-        void reset() {
-            if( nullableValue )
-                nullableValue->~T();
-            nullableValue = NULL;
-        }
-
-        T& operator*() { return *nullableValue; }
-        T const& operator*() const { return *nullableValue; }
-        T* operator->() { return nullableValue; }
-        const T* operator->() const { return nullableValue; }
-
-        T valueOr( T const& defaultValue ) const {
-            return nullableValue ? *nullableValue : defaultValue;
-        }
-
-        bool some() const { return nullableValue != NULL; }
-        bool none() const { return nullableValue == NULL; }
-
-        bool operator !() const { return nullableValue == NULL; }
-        operator SafeBool::type() const {
-            return SafeBool::makeSafe( some() );
-        }
-
-    private:
-        T* nullableValue;
-        char storage[sizeof(T)];
-    };
-
-} // end namespace Catch
-
-namespace Catch {
-
-    struct ITagAliasRegistry {
-        virtual ~ITagAliasRegistry();
-        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
-        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
-
-        static ITagAliasRegistry const& get();
-    };
-
-} // end namespace Catch
-
-// These files are included here so the single_include script doesn't put them
-// in the conditionally compiled sections
-// #included from: internal/catch_test_case_info.h
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
-
-#include <string>
-#include <set>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
-    struct ITestCase;
-
-    struct TestCaseInfo {
-        enum SpecialProperties{
-            None = 0,
-            IsHidden = 1 << 1,
-            ShouldFail = 1 << 2,
-            MayFail = 1 << 3,
-            Throws = 1 << 4
-        };
-
-        TestCaseInfo(   std::string const& _name,
-                        std::string const& _className,
-                        std::string const& _description,
-                        std::set<std::string> const& _tags,
-                        SourceLineInfo const& _lineInfo );
-
-        TestCaseInfo( TestCaseInfo const& other );
-
-        bool isHidden() const;
-        bool throws() const;
-        bool okToFail() const;
-        bool expectedToFail() const;
-
-        std::string name;
-        std::string className;
-        std::string description;
-        std::set<std::string> tags;
-        std::set<std::string> lcaseTags;
-        std::string tagsAsString;
-        SourceLineInfo lineInfo;
-        SpecialProperties properties;
-    };
-
-    class TestCase : public TestCaseInfo {
-    public:
-
-        TestCase( ITestCase* testCase, TestCaseInfo const& info );
-        TestCase( TestCase const& other );
-
-        TestCase withName( std::string const& _newName ) const;
-
-        void invoke() const;
-
-        TestCaseInfo const& getTestCaseInfo() const;
-
-        void swap( TestCase& other );
-        bool operator == ( TestCase const& other ) const;
-        bool operator < ( TestCase const& other ) const;
-        TestCase& operator = ( TestCase const& other );
-
-    private:
-        Ptr<ITestCase> test;
-    };
-
-    TestCase makeTestCase(  ITestCase* testCase,
-                            std::string const& className,
-                            std::string const& name,
-                            std::string const& description,
-                            SourceLineInfo const& lineInfo );
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-
-#ifdef __OBJC__
-// #included from: internal/catch_objc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
-
-#import <objc/runtime.h>
-
-#include <string>
-
-// NB. Any general catch headers included here must be included
-// in catch.hpp first to make sure they are included by the single
-// header for non obj-usage
-
-///////////////////////////////////////////////////////////////////////////////
-// This protocol is really only here for (self) documenting purposes, since
-// all its methods are optional.
-@protocol OcFixture
-
-@optional
-
--(void) setUp;
--(void) tearDown;
-
-@end
-
-namespace Catch {
-
-    class OcMethod : public SharedImpl<ITestCase> {
-
-    public:
-        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
-
-        virtual void invoke() const {
-            id obj = [[m_cls alloc] init];
-
-            performOptionalSelector( obj, @selector(setUp)  );
-            performOptionalSelector( obj, m_sel );
-            performOptionalSelector( obj, @selector(tearDown)  );
-
-            arcSafeRelease( obj );
-        }
-    private:
-        virtual ~OcMethod() {}
-
-        Class m_cls;
-        SEL m_sel;
-    };
-
-    namespace Detail{
-
-        inline std::string getAnnotation(   Class cls,
-                                            std::string const& annotationName,
-                                            std::string const& testCaseName ) {
-            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
-            SEL sel = NSSelectorFromString( selStr );
-            arcSafeRelease( selStr );
-            id value = performOptionalSelector( cls, sel );
-            if( value )
-                return [(NSString*)value UTF8String];
-            return "";
-        }
-    }
-
-    inline size_t registerTestMethods() {
-        size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( NULL, 0 );
-
-        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
-        objc_getClassList( classes, noClasses );
-
-        for( int c = 0; c < noClasses; c++ ) {
-            Class cls = classes[c];
-            {
-                u_int count;
-                Method* methods = class_copyMethodList( cls, &count );
-                for( u_int m = 0; m < count ; m++ ) {
-                    SEL selector = method_getName(methods[m]);
-                    std::string methodName = sel_getName(selector);
-                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
-                        std::string testCaseName = methodName.substr( 15 );
-                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
-                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
-                        const char* className = class_getName( cls );
-
-                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
-                        noTestMethods++;
-                    }
-                }
-                free(methods);
-            }
-        }
-        return noTestMethods;
-    }
-
-    namespace Matchers {
-        namespace Impl {
-        namespace NSStringMatchers {
-
-            template<typename MatcherT>
-            struct StringHolder : MatcherImpl<MatcherT, NSString*>{
-                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
-                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
-                StringHolder() {
-                    arcSafeRelease( m_substr );
-                }
-
-                NSString* m_substr;
-            };
-
-            struct Equals : StringHolder<Equals> {
-                Equals( NSString* substr ) : StringHolder( substr ){}
-
-                virtual bool match( ExpressionType const& str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str isEqualToString:m_substr];
-                }
-
-                virtual std::string toString() const {
-                    return "equals string: " + Catch::toString( m_substr );
-                }
-            };
-
-            struct Contains : StringHolder<Contains> {
-                Contains( NSString* substr ) : StringHolder( substr ){}
-
-                virtual bool match( ExpressionType const& str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location != NSNotFound;
-                }
-
-                virtual std::string toString() const {
-                    return "contains string: " + Catch::toString( m_substr );
-                }
-            };
-
-            struct StartsWith : StringHolder<StartsWith> {
-                StartsWith( NSString* substr ) : StringHolder( substr ){}
-
-                virtual bool match( ExpressionType const& str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == 0;
-                }
-
-                virtual std::string toString() const {
-                    return "starts with: " + Catch::toString( m_substr );
-                }
-            };
-            struct EndsWith : StringHolder<EndsWith> {
-                EndsWith( NSString* substr ) : StringHolder( substr ){}
-
-                virtual bool match( ExpressionType const& str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
-                }
-
-                virtual std::string toString() const {
-                    return "ends with: " + Catch::toString( m_substr );
-                }
-            };
-
-        } // namespace NSStringMatchers
-        } // namespace Impl
-
-        inline Impl::NSStringMatchers::Equals
-            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
-
-        inline Impl::NSStringMatchers::Contains
-            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
-
-        inline Impl::NSStringMatchers::StartsWith
-            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
-
-        inline Impl::NSStringMatchers::EndsWith
-            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
-
-    } // namespace Matchers
-
-    using namespace Matchers;
-
-} // namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define OC_TEST_CASE( name, desc )\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
-{\
-return @ name; \
-}\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
-{ \
-return @ desc; \
-} \
--(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
-
-#endif
-
-#ifdef CATCH_IMPL
-// #included from: internal/catch_impl.hpp
-#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
-
-// Collect all the implementation files together here
-// These are the equivalent of what would usually be cpp files
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#endif
-
-// #included from: ../catch_runner.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
-
-// #included from: internal/catch_commandline.hpp
-#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
-
-// #included from: catch_config.hpp
-#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
-
-// #included from: catch_test_spec_parser.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-// #included from: catch_test_spec.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-#include <string>
-#include <vector>
-
-namespace Catch {
-
-    class TestSpec {
-        struct Pattern : SharedImpl<> {
-            virtual ~Pattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
-        };
-        class NamePattern : public Pattern {
-            enum WildcardPosition {
-                NoWildcard = 0,
-                WildcardAtStart = 1,
-                WildcardAtEnd = 2,
-                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-            };
-
-        public:
-            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
-                if( startsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 1 );
-                    m_wildcard = WildcardAtStart;
-                }
-                if( endsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 0, m_name.size()-1 );
-                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-                }
-            }
-            virtual ~NamePattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const {
-                switch( m_wildcard ) {
-                    case NoWildcard:
-                        return m_name == toLower( testCase.name );
-                    case WildcardAtStart:
-                        return endsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtEnd:
-                        return startsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtBothEnds:
-                        return contains( toLower( testCase.name ), m_name );
-                }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-                throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-            }
-        private:
-            std::string m_name;
-            WildcardPosition m_wildcard;
-        };
-        class TagPattern : public Pattern {
-        public:
-            TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
-            virtual ~TagPattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const {
-                return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
-            }
-        private:
-            std::string m_tag;
-        };
-        class ExcludedPattern : public Pattern {
-        public:
-            ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
-            virtual ~ExcludedPattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
-        private:
-            Ptr<Pattern> m_underlyingPattern;
-        };
-
-        struct Filter {
-            std::vector<Ptr<Pattern> > m_patterns;
-
-            bool matches( TestCaseInfo const& testCase ) const {
-                // All patterns in a filter must match for the filter to be a match
-                for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
-                    if( !(*it)->matches( testCase ) )
-                        return false;
-                    return true;
-            }
-        };
-
-    public:
-        bool hasFilters() const {
-            return !m_filters.empty();
-        }
-        bool matches( TestCaseInfo const& testCase ) const {
-            // A TestSpec matches if any filter matches
-            for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
-                if( it->matches( testCase ) )
-                    return true;
-            return false;
-        }
-
-    private:
-        std::vector<Filter> m_filters;
-
-        friend class TestSpecParser;
-    };
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-namespace Catch {
-
-    class TestSpecParser {
-        enum Mode{ None, Name, QuotedName, Tag };
-        Mode m_mode;
-        bool m_exclusion;
-        std::size_t m_start, m_pos;
-        std::string m_arg;
-        TestSpec::Filter m_currentFilter;
-        TestSpec m_testSpec;
-        ITagAliasRegistry const* m_tagAliases;
-
-    public:
-        TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
-
-        TestSpecParser& parse( std::string const& arg ) {
-            m_mode = None;
-            m_exclusion = false;
-            m_start = std::string::npos;
-            m_arg = m_tagAliases->expandAliases( arg );
-            for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
-                visitChar( m_arg[m_pos] );
-            if( m_mode == Name )
-                addPattern<TestSpec::NamePattern>();
-            return *this;
-        }
-        TestSpec testSpec() {
-            addFilter();
-            return m_testSpec;
-        }
-    private:
-        void visitChar( char c ) {
-            if( m_mode == None ) {
-                switch( c ) {
-                case ' ': return;
-                case '~': m_exclusion = true; return;
-                case '[': return startNewMode( Tag, ++m_pos );
-                case '"': return startNewMode( QuotedName, ++m_pos );
-                default: startNewMode( Name, m_pos ); break;
-                }
-            }
-            if( m_mode == Name ) {
-                if( c == ',' ) {
-                    addPattern<TestSpec::NamePattern>();
-                    addFilter();
-                }
-                else if( c == '[' ) {
-                    if( subString() == "exclude:" )
-                        m_exclusion = true;
-                    else
-                        addPattern<TestSpec::NamePattern>();
-                    startNewMode( Tag, ++m_pos );
-                }
-            }
-            else if( m_mode == QuotedName && c == '"' )
-                addPattern<TestSpec::NamePattern>();
-            else if( m_mode == Tag && c == ']' )
-                addPattern<TestSpec::TagPattern>();
-        }
-        void startNewMode( Mode mode, std::size_t start ) {
-            m_mode = mode;
-            m_start = start;
-        }
-        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
-        template<typename T>
-        void addPattern() {
-            std::string token = subString();
-            if( startsWith( token, "exclude:" ) ) {
-                m_exclusion = true;
-                token = token.substr( 8 );
-            }
-            if( !token.empty() ) {
-                Ptr<TestSpec::Pattern> pattern = new T( token );
-                if( m_exclusion )
-                    pattern = new TestSpec::ExcludedPattern( pattern );
-                m_currentFilter.m_patterns.push_back( pattern );
-            }
-            m_exclusion = false;
-            m_mode = None;
-        }
-        void addFilter() {
-            if( !m_currentFilter.m_patterns.empty() ) {
-                m_testSpec.m_filters.push_back( m_currentFilter );
-                m_currentFilter = TestSpec::Filter();
-            }
-        }
-    };
-    inline TestSpec parseTestSpec( std::string const& arg ) {
-        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
-    }
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// #included from: catch_interfaces_config.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
-
-#include <iostream>
-#include <string>
-#include <vector>
-
-namespace Catch {
-
-    struct Verbosity { enum Level {
-        NoOutput = 0,
-        Quiet,
-        Normal
-    }; };
-
-    struct WarnAbout { enum What {
-        Nothing = 0x00,
-        NoAssertions = 0x01
-    }; };
-
-    struct ShowDurations { enum OrNot {
-        DefaultForReporter,
-        Always,
-        Never
-    }; };
-    struct RunTests { enum InWhatOrder {
-        InDeclarationOrder,
-        InLexicographicalOrder,
-        InRandomOrder
-    }; };
-
-    class TestSpec;
-
-    struct IConfig : IShared {
-
-        virtual ~IConfig();
-
-        virtual bool allowThrows() const = 0;
-        virtual std::ostream& stream() const = 0;
-        virtual std::string name() const = 0;
-        virtual bool includeSuccessfulResults() const = 0;
-        virtual bool shouldDebugBreak() const = 0;
-        virtual bool warnAboutMissingAssertions() const = 0;
-        virtual int abortAfter() const = 0;
-        virtual bool showInvisibles() const = 0;
-        virtual ShowDurations::OrNot showDurations() const = 0;
-        virtual TestSpec const& testSpec() const = 0;
-        virtual RunTests::InWhatOrder runOrder() const = 0;
-        virtual unsigned int rngSeed() const = 0;
-        virtual bool forceColour() const = 0;
-    };
-}
-
-// #included from: catch_stream.h
-#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
-
-#include <streambuf>
-
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
-    class Stream {
-    public:
-        Stream();
-        Stream( std::streambuf* _streamBuf, bool _isOwned );
-        void release();
-
-        std::streambuf* streamBuf;
-
-    private:
-        bool isOwned;
-    };
-
-    std::ostream& cout();
-    std::ostream& cerr();
-}
-
-#include <memory>
-#include <vector>
-#include <string>
-#include <iostream>
-#include <ctime>
-
-#ifndef CATCH_CONFIG_CONSOLE_WIDTH
-#define CATCH_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-namespace Catch {
-
-    struct ConfigData {
-
-        ConfigData()
-        :   listTests( false ),
-            listTags( false ),
-            listReporters( false ),
-            listTestNamesOnly( false ),
-            showSuccessfulTests( false ),
-            shouldDebugBreak( false ),
-            noThrow( false ),
-            showHelp( false ),
-            showInvisibles( false ),
-            forceColour( false ),
-            abortAfter( -1 ),
-            rngSeed( 0 ),
-            verbosity( Verbosity::Normal ),
-            warnings( WarnAbout::Nothing ),
-            showDurations( ShowDurations::DefaultForReporter ),
-            runOrder( RunTests::InDeclarationOrder )
-        {}
-
-        bool listTests;
-        bool listTags;
-        bool listReporters;
-        bool listTestNamesOnly;
-
-        bool showSuccessfulTests;
-        bool shouldDebugBreak;
-        bool noThrow;
-        bool showHelp;
-        bool showInvisibles;
-        bool forceColour;
-
-        int abortAfter;
-        unsigned int rngSeed;
-
-        Verbosity::Level verbosity;
-        WarnAbout::What warnings;
-        ShowDurations::OrNot showDurations;
-        RunTests::InWhatOrder runOrder;
-
-        std::string reporterName;
-        std::string outputFilename;
-        std::string name;
-        std::string processName;
-
-        std::vector<std::string> testsOrTags;
-    };
-
-    class Config : public SharedImpl<IConfig> {
-    private:
-        Config( Config const& other );
-        Config& operator = ( Config const& other );
-        virtual void dummy();
-    public:
-
-        Config()
-        :   m_os( Catch::cout().rdbuf() )
-        {}
-
-        Config( ConfigData const& data )
-        :   m_data( data ),
-            m_os( Catch::cout().rdbuf() )
-        {
-            if( !data.testsOrTags.empty() ) {
-                TestSpecParser parser( ITagAliasRegistry::get() );
-                for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
-                    parser.parse( data.testsOrTags[i] );
-                m_testSpec = parser.testSpec();
-            }
-        }
-
-        virtual ~Config() {
-            m_os.rdbuf( Catch::cout().rdbuf() );
-            m_stream.release();
-        }
-
-        void setFilename( std::string const& filename ) {
-            m_data.outputFilename = filename;
-        }
-
-        std::string const& getFilename() const {
-            return m_data.outputFilename ;
-        }
-
-        bool listTests() const { return m_data.listTests; }
-        bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
-        bool listTags() const { return m_data.listTags; }
-        bool listReporters() const { return m_data.listReporters; }
-
-        std::string getProcessName() const { return m_data.processName; }
-
-        bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
-
-        void setStreamBuf( std::streambuf* buf ) {
-            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
-        }
-
-        void useStream( std::string const& streamName ) {
-            Stream stream = createStream( streamName );
-            setStreamBuf( stream.streamBuf );
-            m_stream.release();
-            m_stream = stream;
-        }
-
-        std::string getReporterName() const { return m_data.reporterName; }
-
-        int abortAfter() const { return m_data.abortAfter; }
-
-        TestSpec const& testSpec() const { return m_testSpec; }
-
-        bool showHelp() const { return m_data.showHelp; }
-        bool showInvisibles() const { return m_data.showInvisibles; }
-
-        // IConfig interface
-        virtual bool allowThrows() const        { return !m_data.noThrow; }
-        virtual std::ostream& stream() const    { return m_os; }
-        virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
-        virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
-        virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
-        virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
-        virtual RunTests::InWhatOrder runOrder() const  { return m_data.runOrder; }
-        virtual unsigned int rngSeed() const    { return m_data.rngSeed; }
-        virtual bool forceColour() const { return m_data.forceColour; }
-
-    private:
-        ConfigData m_data;
-
-        Stream m_stream;
-        mutable std::ostream m_os;
-        TestSpec m_testSpec;
-    };
-
-} // end namespace Catch
-
-// #included from: catch_clara.h
-#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
-
-// Use Catch's value for console width (store Clara's off to the side, if present)
-#ifdef CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
-#undef CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
-
-// Declare Clara inside the Catch namespace
-#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
-// #included from: ../external/clara.h
-
-// Only use header guard if we are not using an outer namespace
-#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
-
-#ifndef STITCH_CLARA_OPEN_NAMESPACE
-#define TWOBLUECUBES_CLARA_H_INCLUDED
-#define STITCH_CLARA_OPEN_NAMESPACE
-#define STITCH_CLARA_CLOSE_NAMESPACE
-#else
-#define STITCH_CLARA_CLOSE_NAMESPACE }
-#endif
-
-#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
-
-// ----------- #included from tbc_text_format.h -----------
-
-// Only use header guard if we are not using an outer namespace
-#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
-#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-#define TBC_TEXT_FORMAT_H_INCLUDED
-#endif
-
-#include <string>
-#include <vector>
-#include <sstream>
-
-// Use optional outer namespace
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
-#endif
-
-namespace Tbc {
-
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
-    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
-
-    struct TextAttributes {
-        TextAttributes()
-        :   initialIndent( std::string::npos ),
-            indent( 0 ),
-            width( consoleWidth-1 ),
-            tabChar( '\t' )
-        {}
-
-        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
-        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
-        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
-        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
-
-        std::size_t initialIndent;  // indent of first line, or npos
-        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
-        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
-        char tabChar;               // If this char is seen the indent is changed to current pos
-    };
-
-    class Text {
-    public:
-        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
-        : attr( _attr )
-        {
-            std::string wrappableChars = " [({.,/|\\-";
-            std::size_t indent = _attr.initialIndent != std::string::npos
-                ? _attr.initialIndent
-                : _attr.indent;
-            std::string remainder = _str;
-
-            while( !remainder.empty() ) {
-                if( lines.size() >= 1000 ) {
-                    lines.push_back( "... message truncated due to excessive size" );
-                    return;
-                }
-                std::size_t tabPos = std::string::npos;
-                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
-                std::size_t pos = remainder.find_first_of( '\n' );
-                if( pos <= width ) {
-                    width = pos;
-                }
-                pos = remainder.find_last_of( _attr.tabChar, width );
-                if( pos != std::string::npos ) {
-                    tabPos = pos;
-                    if( remainder[width] == '\n' )
-                        width--;
-                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
-                }
-
-                if( width == remainder.size() ) {
-                    spliceLine( indent, remainder, width );
-                }
-                else if( remainder[width] == '\n' ) {
-                    spliceLine( indent, remainder, width );
-                    if( width <= 1 || remainder.size() != 1 )
-                        remainder = remainder.substr( 1 );
-                    indent = _attr.indent;
-                }
-                else {
-                    pos = remainder.find_last_of( wrappableChars, width );
-                    if( pos != std::string::npos && pos > 0 ) {
-                        spliceLine( indent, remainder, pos );
-                        if( remainder[0] == ' ' )
-                            remainder = remainder.substr( 1 );
-                    }
-                    else {
-                        spliceLine( indent, remainder, width-1 );
-                        lines.back() += "-";
-                    }
-                    if( lines.size() == 1 )
-                        indent = _attr.indent;
-                    if( tabPos != std::string::npos )
-                        indent += tabPos;
-                }
-            }
-        }
-
-        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
-            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
-            _remainder = _remainder.substr( _pos );
-        }
-
-        typedef std::vector<std::string>::const_iterator const_iterator;
-
-        const_iterator begin() const { return lines.begin(); }
-        const_iterator end() const { return lines.end(); }
-        std::string const& last() const { return lines.back(); }
-        std::size_t size() const { return lines.size(); }
-        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << *this;
-            return oss.str();
-        }
-
-        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
-            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
-                it != itEnd; ++it ) {
-                if( it != _text.begin() )
-                    _stream << "\n";
-                _stream << *it;
-            }
-            return _stream;
-        }
-
-    private:
-        std::string str;
-        TextAttributes attr;
-        std::vector<std::string> lines;
-    };
-
-} // end namespace Tbc
-
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
-
-#endif // TBC_TEXT_FORMAT_H_INCLUDED
-
-// ----------- end of #include from tbc_text_format.h -----------
-// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
-
-#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
-
-#include <map>
-#include <algorithm>
-#include <stdexcept>
-#include <memory>
-
-// Use optional outer namespace
-#ifdef STITCH_CLARA_OPEN_NAMESPACE
-STITCH_CLARA_OPEN_NAMESPACE
-#endif
-
-namespace Clara {
-
-    struct UnpositionalTag {};
-
-    extern UnpositionalTag _;
-
-#ifdef CLARA_CONFIG_MAIN
-    UnpositionalTag _;
-#endif
-
-    namespace Detail {
-
-#ifdef CLARA_CONSOLE_WIDTH
-    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
-
-        using namespace Tbc;
-
-        inline bool startsWith( std::string const& str, std::string const& prefix ) {
-            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
-        }
-
-        template<typename T> struct RemoveConstRef{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
-
-        template<typename T>    struct IsBool       { static const bool value = false; };
-        template<>              struct IsBool<bool> { static const bool value = true; };
-
-        template<typename T>
-        void convertInto( std::string const& _source, T& _dest ) {
-            std::stringstream ss;
-            ss << _source;
-            ss >> _dest;
-            if( ss.fail() )
-                throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
-        }
-        inline void convertInto( std::string const& _source, std::string& _dest ) {
-            _dest = _source;
-        }
-        inline void convertInto( std::string const& _source, bool& _dest ) {
-            std::string sourceLC = _source;
-            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
-            if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
-                _dest = true;
-            else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
-                _dest = false;
-            else
-                throw std::runtime_error( "Expected a boolean value but did not recognise:\n  '" + _source + "'" );
-        }
-        inline void convertInto( bool _source, bool& _dest ) {
-            _dest = _source;
-        }
-        template<typename T>
-        inline void convertInto( bool, T& ) {
-            throw std::runtime_error( "Invalid conversion" );
-        }
-
-        template<typename ConfigT>
-        struct IArgFunction {
-            virtual ~IArgFunction() {}
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-            IArgFunction()                      = default;
-            IArgFunction( IArgFunction const& ) = default;
-#  endif
-            virtual void set( ConfigT& config, std::string const& value ) const = 0;
-            virtual void setFlag( ConfigT& config ) const = 0;
-            virtual bool takesArg() const = 0;
-            virtual IArgFunction* clone() const = 0;
-        };
-
-        template<typename ConfigT>
-        class BoundArgFunction {
-        public:
-            BoundArgFunction() : functionObj( NULL ) {}
-            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
-            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
-                delete functionObj;
-                functionObj = newFunctionObj;
-                return *this;
-            }
-            ~BoundArgFunction() { delete functionObj; }
-
-            void set( ConfigT& config, std::string const& value ) const {
-                functionObj->set( config, value );
-            }
-            void setFlag( ConfigT& config ) const {
-                functionObj->setFlag( config );
-            }
-            bool takesArg() const { return functionObj->takesArg(); }
-
-            bool isSet() const {
-                return functionObj != NULL;
-            }
-        private:
-            IArgFunction<ConfigT>* functionObj;
-        };
-
-        template<typename C>
-        struct NullBinder : IArgFunction<C>{
-            virtual void set( C&, std::string const& ) const {}
-            virtual void setFlag( C& ) const {}
-            virtual bool takesArg() const { return true; }
-            virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
-        };
-
-        template<typename C, typename M>
-        struct BoundDataMember : IArgFunction<C>{
-            BoundDataMember( M C::* _member ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                convertInto( stringValue, p.*member );
-            }
-            virtual void setFlag( C& p ) const {
-                convertInto( true, p.*member );
-            }
-            virtual bool takesArg() const { return !IsBool<M>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
-            M C::* member;
-        };
-        template<typename C, typename M>
-        struct BoundUnaryMethod : IArgFunction<C>{
-            BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                typename RemoveConstRef<M>::type value;
-                convertInto( stringValue, value );
-                (p.*member)( value );
-            }
-            virtual void setFlag( C& p ) const {
-                typename RemoveConstRef<M>::type value;
-                convertInto( true, value );
-                (p.*member)( value );
-            }
-            virtual bool takesArg() const { return !IsBool<M>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
-            void (C::*member)( M );
-        };
-        template<typename C>
-        struct BoundNullaryMethod : IArgFunction<C>{
-            BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                bool value;
-                convertInto( stringValue, value );
-                if( value )
-                    (p.*member)();
-            }
-            virtual void setFlag( C& p ) const {
-                (p.*member)();
-            }
-            virtual bool takesArg() const { return false; }
-            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
-            void (C::*member)();
-        };
-
-        template<typename C>
-        struct BoundUnaryFunction : IArgFunction<C>{
-            BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
-            virtual void set( C& obj, std::string const& stringValue ) const {
-                bool value;
-                convertInto( stringValue, value );
-                if( value )
-                    function( obj );
-            }
-            virtual void setFlag( C& p ) const {
-                function( p );
-            }
-            virtual bool takesArg() const { return false; }
-            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
-            void (*function)( C& );
-        };
-
-        template<typename C, typename T>
-        struct BoundBinaryFunction : IArgFunction<C>{
-            BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
-            virtual void set( C& obj, std::string const& stringValue ) const {
-                typename RemoveConstRef<T>::type value;
-                convertInto( stringValue, value );
-                function( obj, value );
-            }
-            virtual void setFlag( C& obj ) const {
-                typename RemoveConstRef<T>::type value;
-                convertInto( true, value );
-                function( obj, value );
-            }
-            virtual bool takesArg() const { return !IsBool<T>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
-            void (*function)( C&, T );
-        };
-
-    } // namespace Detail
-
-    struct Parser {
-        Parser() : separators( " \t=:" ) {}
-
-        struct Token {
-            enum Type { Positional, ShortOpt, LongOpt };
-            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
-            Type type;
-            std::string data;
-        };
-
-        void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
-            const std::string doubleDash = "--";
-            for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
-                parseIntoTokens( argv[i] , tokens);
-        }
-        void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
-            while( !arg.empty() ) {
-                Parser::Token token( Parser::Token::Positional, arg );
-                arg = "";
-                if( token.data[0] == '-' ) {
-                    if( token.data.size() > 1 && token.data[1] == '-' ) {
-                        token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
-                    }
-                    else {
-                        token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
-                        if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
-                            arg = "-" + token.data.substr( 1 );
-                            token.data = token.data.substr( 0, 1 );
-                        }
-                    }
-                }
-                if( token.type != Parser::Token::Positional ) {
-                    std::size_t pos = token.data.find_first_of( separators );
-                    if( pos != std::string::npos ) {
-                        arg = token.data.substr( pos+1 );
-                        token.data = token.data.substr( 0, pos );
-                    }
-                }
-                tokens.push_back( token );
-            }
-        }
-        std::string separators;
-    };
-
-    template<typename ConfigT>
-    struct CommonArgProperties {
-        CommonArgProperties() {}
-        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
-
-        Detail::BoundArgFunction<ConfigT> boundField;
-        std::string description;
-        std::string detail;
-        std::string placeholder; // Only value if boundField takes an arg
-
-        bool takesArg() const {
-            return !placeholder.empty();
-        }
-        void validate() const {
-            if( !boundField.isSet() )
-                throw std::logic_error( "option not bound" );
-        }
-    };
-    struct OptionArgProperties {
-        std::vector<std::string> shortNames;
-        std::string longName;
-
-        bool hasShortName( std::string const& shortName ) const {
-            return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
-        }
-        bool hasLongName( std::string const& _longName ) const {
-            return _longName == longName;
-        }
-    };
-    struct PositionalArgProperties {
-        PositionalArgProperties() : position( -1 ) {}
-        int position; // -1 means non-positional (floating)
-
-        bool isFixedPositional() const {
-            return position != -1;
-        }
-    };
-
-    template<typename ConfigT>
-    class CommandLine {
-
-        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
-            Arg() {}
-            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
-
-            using CommonArgProperties<ConfigT>::placeholder; // !TBD
-
-            std::string dbgName() const {
-                if( !longName.empty() )
-                    return "--" + longName;
-                if( !shortNames.empty() )
-                    return "-" + shortNames[0];
-                return "positional args";
-            }
-            std::string commands() const {
-                std::ostringstream oss;
-                bool first = true;
-                std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
-                for(; it != itEnd; ++it ) {
-                    if( first )
-                        first = false;
-                    else
-                        oss << ", ";
-                    oss << "-" << *it;
-                }
-                if( !longName.empty() ) {
-                    if( !first )
-                        oss << ", ";
-                    oss << "--" << longName;
-                }
-                if( !placeholder.empty() )
-                    oss << " <" << placeholder << ">";
-                return oss.str();
-            }
-        };
-
-        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
-#if defined(__cplusplus) && __cplusplus > 199711L
-        typedef std::unique_ptr<Arg> ArgAutoPtr;
-#else
-        typedef std::auto_ptr<Arg> ArgAutoPtr;
-#endif
-
-        friend void addOptName( Arg& arg, std::string const& optName )
-        {
-            if( optName.empty() )
-                return;
-            if( Detail::startsWith( optName, "--" ) ) {
-                if( !arg.longName.empty() )
-                    throw std::logic_error( "Only one long opt may be specified. '"
-                        + arg.longName
-                        + "' already specified, now attempting to add '"
-                        + optName + "'" );
-                arg.longName = optName.substr( 2 );
-            }
-            else if( Detail::startsWith( optName, "-" ) )
-                arg.shortNames.push_back( optName.substr( 1 ) );
-            else
-                throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
-        }
-        friend void setPositionalArg( Arg& arg, int position )
-        {
-            arg.position = position;
-        }
-
-        class ArgBuilder {
-        public:
-            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
-
-            // Bind a non-boolean data member (requires placeholder string)
-            template<typename C, typename M>
-            void bind( M C::* field, std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
-                m_arg->placeholder = placeholder;
-            }
-            // Bind a boolean data member (no placeholder required)
-            template<typename C>
-            void bind( bool C::* field ) {
-                m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
-            }
-
-            // Bind a method taking a single, non-boolean argument (requires a placeholder string)
-            template<typename C, typename M>
-            void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
-                m_arg->placeholder = placeholder;
-            }
-
-            // Bind a method taking a single, boolean argument (no placeholder string required)
-            template<typename C>
-            void bind( void (C::* unaryMethod)( bool ) ) {
-                m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
-            }
-
-            // Bind a method that takes no arguments (will be called if opt is present)
-            template<typename C>
-            void bind( void (C::* nullaryMethod)() ) {
-                m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
-            }
-
-            // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
-            template<typename C>
-            void bind( void (* unaryFunction)( C& ) ) {
-                m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
-            }
-
-            // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
-            template<typename C, typename T>
-            void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
-                m_arg->placeholder = placeholder;
-            }
-
-            ArgBuilder& describe( std::string const& description ) {
-                m_arg->description = description;
-                return *this;
-            }
-            ArgBuilder& detail( std::string const& detail ) {
-                m_arg->detail = detail;
-                return *this;
-            }
-
-        protected:
-            Arg* m_arg;
-        };
-
-        class OptBuilder : public ArgBuilder {
-        public:
-            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
-            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
-
-            OptBuilder& operator[]( std::string const& optName ) {
-                addOptName( *ArgBuilder::m_arg, optName );
-                return *this;
-            }
-        };
-
-    public:
-
-        CommandLine()
-        :   m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
-            m_highestSpecifiedArgPosition( 0 ),
-            m_throwOnUnrecognisedTokens( false )
-        {}
-        CommandLine( CommandLine const& other )
-        :   m_boundProcessName( other.m_boundProcessName ),
-            m_options ( other.m_options ),
-            m_positionalArgs( other.m_positionalArgs ),
-            m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
-            m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
-        {
-            if( other.m_floatingArg.get() )
-                m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
-        }
-
-        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
-            m_throwOnUnrecognisedTokens = shouldThrow;
-            return *this;
-        }
-
-        OptBuilder operator[]( std::string const& optName ) {
-            m_options.push_back( Arg() );
-            addOptName( m_options.back(), optName );
-            OptBuilder builder( &m_options.back() );
-            return builder;
-        }
-
-        ArgBuilder operator[]( int position ) {
-            m_positionalArgs.insert( std::make_pair( position, Arg() ) );
-            if( position > m_highestSpecifiedArgPosition )
-                m_highestSpecifiedArgPosition = position;
-            setPositionalArg( m_positionalArgs[position], position );
-            ArgBuilder builder( &m_positionalArgs[position] );
-            return builder;
-        }
-
-        // Invoke this with the _ instance
-        ArgBuilder operator[]( UnpositionalTag ) {
-            if( m_floatingArg.get() )
-                throw std::logic_error( "Only one unpositional argument can be added" );
-            m_floatingArg.reset( new Arg() );
-            ArgBuilder builder( m_floatingArg.get() );
-            return builder;
-        }
-
-        template<typename C, typename M>
-        void bindProcessName( M C::* field ) {
-            m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
-        }
-        template<typename C, typename M>
-        void bindProcessName( void (C::*_unaryMethod)( M ) ) {
-            m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
-        }
-
-        void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
-            typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
-            std::size_t maxWidth = 0;
-            for( it = itBegin; it != itEnd; ++it )
-                maxWidth = (std::max)( maxWidth, it->commands().size() );
-
-            for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usage( it->commands(), Detail::TextAttributes()
-                                                        .setWidth( maxWidth+indent )
-                                                        .setIndent( indent ) );
-                Detail::Text desc( it->description, Detail::TextAttributes()
-                                                        .setWidth( width - maxWidth - 3 ) );
-
-                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usage.size() ? usage[i] : "";
-                    os << usageCol;
-
-                    if( i < desc.size() && !desc[i].empty() )
-                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
-                            << desc[i];
-                    os << "\n";
-                }
-            }
-        }
-        std::string optUsage() const {
-            std::ostringstream oss;
-            optUsage( oss );
-            return oss.str();
-        }
-
-        void argSynopsis( std::ostream& os ) const {
-            for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
-                if( i > 1 )
-                    os << " ";
-                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
-                if( it != m_positionalArgs.end() )
-                    os << "<" << it->second.placeholder << ">";
-                else if( m_floatingArg.get() )
-                    os << "<" << m_floatingArg->placeholder << ">";
-                else
-                    throw std::logic_error( "non consecutive positional arguments with no floating args" );
-            }
-            // !TBD No indication of mandatory args
-            if( m_floatingArg.get() ) {
-                if( m_highestSpecifiedArgPosition > 1 )
-                    os << " ";
-                os << "[<" << m_floatingArg->placeholder << "> ...]";
-            }
-        }
-        std::string argSynopsis() const {
-            std::ostringstream oss;
-            argSynopsis( oss );
-            return oss.str();
-        }
-
-        void usage( std::ostream& os, std::string const& procName ) const {
-            validate();
-            os << "usage:\n  " << procName << " ";
-            argSynopsis( os );
-            if( !m_options.empty() ) {
-                os << " [options]\n\nwhere options are: \n";
-                optUsage( os, 2 );
-            }
-            os << "\n";
-        }
-        std::string usage( std::string const& procName ) const {
-            std::ostringstream oss;
-            usage( oss, procName );
-            return oss.str();
-        }
-
-        ConfigT parse( int argc, char const * const * argv ) const {
-            ConfigT config;
-            parseInto( argc, argv, config );
-            return config;
-        }
-
-        std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
-            std::string processName = argv[0];
-            std::size_t lastSlash = processName.find_last_of( "/\\" );
-            if( lastSlash != std::string::npos )
-                processName = processName.substr( lastSlash+1 );
-            m_boundProcessName.set( config, processName );
-            std::vector<Parser::Token> tokens;
-            Parser parser;
-            parser.parseIntoTokens( argc, argv, tokens );
-            return populate( tokens, config );
-        }
-
-        std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            validate();
-            std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
-            unusedTokens = populateFixedArgs( unusedTokens, config );
-            unusedTokens = populateFloatingArgs( unusedTokens, config );
-            return unusedTokens;
-        }
-
-        std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            std::vector<Parser::Token> unusedTokens;
-            std::vector<std::string> errors;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
-                for(; it != itEnd; ++it ) {
-                    Arg const& arg = *it;
-
-                    try {
-                        if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
-                            ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
-                            if( arg.takesArg() ) {
-                                if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
-                                    errors.push_back( "Expected argument to option: " + token.data );
-                                else
-                                    arg.boundField.set( config, tokens[++i].data );
-                            }
-                            else {
-                                arg.boundField.setFlag( config );
-                            }
-                            break;
-                        }
-                    }
-                    catch( std::exception& ex ) {
-                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
-                    }
-                }
-                if( it == itEnd ) {
-                    if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
-                        unusedTokens.push_back( token );
-                    else if( errors.empty() && m_throwOnUnrecognisedTokens )
-                        errors.push_back( "unrecognised option: " + token.data );
-                }
-            }
-            if( !errors.empty() ) {
-                std::ostringstream oss;
-                for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
-                        it != itEnd;
-                        ++it ) {
-                    if( it != errors.begin() )
-                        oss << "\n";
-                    oss << *it;
-                }
-                throw std::runtime_error( oss.str() );
-            }
-            return unusedTokens;
-        }
-        std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            std::vector<Parser::Token> unusedTokens;
-            int position = 1;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
-                if( it != m_positionalArgs.end() )
-                    it->second.boundField.set( config, token.data );
-                else
-                    unusedTokens.push_back( token );
-                if( token.type == Parser::Token::Positional )
-                    position++;
-            }
-            return unusedTokens;
-        }
-        std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            if( !m_floatingArg.get() )
-                return tokens;
-            std::vector<Parser::Token> unusedTokens;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                if( token.type == Parser::Token::Positional )
-                    m_floatingArg->boundField.set( config, token.data );
-                else
-                    unusedTokens.push_back( token );
-            }
-            return unusedTokens;
-        }
-
-        void validate() const
-        {
-            if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
-                throw std::logic_error( "No options or arguments specified" );
-
-            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
-                                                            itEnd = m_options.end();
-                    it != itEnd; ++it )
-                it->validate();
-        }
-
-    private:
-        Detail::BoundArgFunction<ConfigT> m_boundProcessName;
-        std::vector<Arg> m_options;
-        std::map<int, Arg> m_positionalArgs;
-        ArgAutoPtr m_floatingArg;
-        int m_highestSpecifiedArgPosition;
-        bool m_throwOnUnrecognisedTokens;
-    };
-
-} // end namespace Clara
-
-STITCH_CLARA_CLOSE_NAMESPACE
-#undef STITCH_CLARA_OPEN_NAMESPACE
-#undef STITCH_CLARA_CLOSE_NAMESPACE
-
-#endif // TWOBLUECUBES_CLARA_H_INCLUDED
-#undef STITCH_CLARA_OPEN_NAMESPACE
-
-// Restore Clara's value for console width, if present
-#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-
-#include <fstream>
-
-namespace Catch {
-
-    inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
-    inline void abortAfterX( ConfigData& config, int x ) {
-        if( x < 1 )
-            throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
-        config.abortAfter = x;
-    }
-    inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
-
-    inline void addWarning( ConfigData& config, std::string const& _warning ) {
-        if( _warning == "NoAssertions" )
-            config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
-        else
-            throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
-    }
-    inline void setOrder( ConfigData& config, std::string const& order ) {
-        if( startsWith( "declared", order ) )
-            config.runOrder = RunTests::InDeclarationOrder;
-        else if( startsWith( "lexical", order ) )
-            config.runOrder = RunTests::InLexicographicalOrder;
-        else if( startsWith( "random", order ) )
-            config.runOrder = RunTests::InRandomOrder;
-        else
-            throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
-    }
-    inline void setRngSeed( ConfigData& config, std::string const& seed ) {
-        if( seed == "time" ) {
-            config.rngSeed = static_cast<unsigned int>( std::time(0) );
-        }
-        else {
-            std::stringstream ss;
-            ss << seed;
-            ss >> config.rngSeed;
-            if( ss.fail() )
-                throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
-        }
-    }
-    inline void setVerbosity( ConfigData& config, int level ) {
-        // !TBD: accept strings?
-        config.verbosity = static_cast<Verbosity::Level>( level );
-    }
-    inline void setShowDurations( ConfigData& config, bool _showDurations ) {
-        config.showDurations = _showDurations
-            ? ShowDurations::Always
-            : ShowDurations::Never;
-    }
-    inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
-        std::ifstream f( _filename.c_str() );
-        if( !f.is_open() )
-            throw std::domain_error( "Unable to load input file: " + _filename );
-
-        std::string line;
-        while( std::getline( f, line ) ) {
-            line = trim(line);
-            if( !line.empty() && !startsWith( line, "#" ) )
-                addTestOrTags( config, "\"" + line + "\"," );
-        }
-    }
-
-    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
-
-        using namespace Clara;
-        CommandLine<ConfigData> cli;
-
-        cli.bindProcessName( &ConfigData::processName );
-
-        cli["-?"]["-h"]["--help"]
-            .describe( "display usage information" )
-            .bind( &ConfigData::showHelp );
-
-        cli["-l"]["--list-tests"]
-            .describe( "list all/matching test cases" )
-            .bind( &ConfigData::listTests );
-
-        cli["-t"]["--list-tags"]
-            .describe( "list all/matching tags" )
-            .bind( &ConfigData::listTags );
-
-        cli["-s"]["--success"]
-            .describe( "include successful tests in output" )
-            .bind( &ConfigData::showSuccessfulTests );
-
-        cli["-b"]["--break"]
-            .describe( "break into debugger on failure" )
-            .bind( &ConfigData::shouldDebugBreak );
-
-        cli["-e"]["--nothrow"]
-            .describe( "skip exception tests" )
-            .bind( &ConfigData::noThrow );
-
-        cli["-i"]["--invisibles"]
-            .describe( "show invisibles (tabs, newlines)" )
-            .bind( &ConfigData::showInvisibles );
-
-        cli["-o"]["--out"]
-            .describe( "output filename" )
-            .bind( &ConfigData::outputFilename, "filename" );
-
-        cli["-r"]["--reporter"]
-//            .placeholder( "name[:filename]" )
-            .describe( "reporter to use (defaults to console)" )
-            .bind( &ConfigData::reporterName, "name" );
-
-        cli["-n"]["--name"]
-            .describe( "suite name" )
-            .bind( &ConfigData::name, "name" );
-
-        cli["-a"]["--abort"]
-            .describe( "abort at first failure" )
-            .bind( &abortAfterFirst );
-
-        cli["-x"]["--abortx"]
-            .describe( "abort after x failures" )
-            .bind( &abortAfterX, "no. failures" );
-
-        cli["-w"]["--warn"]
-            .describe( "enable warnings" )
-            .bind( &addWarning, "warning name" );
-
-// - needs updating if reinstated
-//        cli.into( &setVerbosity )
-//            .describe( "level of verbosity (0=no output)" )
-//            .shortOpt( "v")
-//            .longOpt( "verbosity" )
-//            .placeholder( "level" );
-
-        cli[_]
-            .describe( "which test or tests to use" )
-            .bind( &addTestOrTags, "test name, pattern or tags" );
-
-        cli["-d"]["--durations"]
-            .describe( "show test durations" )
-            .bind( &setShowDurations, "yes/no" );
-
-        cli["-f"]["--input-file"]
-            .describe( "load test names to run from a file" )
-            .bind( &loadTestNamesFromFile, "filename" );
-
-        // Less common commands which don't have a short form
-        cli["--list-test-names-only"]
-            .describe( "list all/matching test cases names only" )
-            .bind( &ConfigData::listTestNamesOnly );
-
-        cli["--list-reporters"]
-            .describe( "list all reporters" )
-            .bind( &ConfigData::listReporters );
-
-        cli["--order"]
-            .describe( "test case order (defaults to decl)" )
-            .bind( &setOrder, "decl|lex|rand" );
-
-        cli["--rng-seed"]
-            .describe( "set a specific seed for random numbers" )
-            .bind( &setRngSeed, "'time'|number" );
-
-        cli["--force-colour"]
-            .describe( "force colourised output" )
-            .bind( &ConfigData::forceColour );
-
-        return cli;
-    }
-
-} // end namespace Catch
-
-// #included from: internal/catch_list.hpp
-#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
-
-// #included from: catch_text.h
-#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
-
-#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
-
-#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
-// #included from: ../external/tbc_text_format.h
-// Only use header guard if we are not using an outer namespace
-#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-#  ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#   define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#  endif
-# else
-#  define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-# endif
-#endif
-#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#include <string>
-#include <vector>
-#include <sstream>
-
-// Use optional outer namespace
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
-#endif
-
-namespace Tbc {
-
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
-    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
-
-    struct TextAttributes {
-        TextAttributes()
-        :   initialIndent( std::string::npos ),
-            indent( 0 ),
-            width( consoleWidth-1 ),
-            tabChar( '\t' )
-        {}
-
-        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
-        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
-        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
-        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
-
-        std::size_t initialIndent;  // indent of first line, or npos
-        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
-        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
-        char tabChar;               // If this char is seen the indent is changed to current pos
-    };
-
-    class Text {
-    public:
-        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
-        : attr( _attr )
-        {
-            std::string wrappableChars = " [({.,/|\\-";
-            std::size_t indent = _attr.initialIndent != std::string::npos
-                ? _attr.initialIndent
-                : _attr.indent;
-            std::string remainder = _str;
-
-            while( !remainder.empty() ) {
-                if( lines.size() >= 1000 ) {
-                    lines.push_back( "... message truncated due to excessive size" );
-                    return;
-                }
-                std::size_t tabPos = std::string::npos;
-                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
-                std::size_t pos = remainder.find_first_of( '\n' );
-                if( pos <= width ) {
-                    width = pos;
-                }
-                pos = remainder.find_last_of( _attr.tabChar, width );
-                if( pos != std::string::npos ) {
-                    tabPos = pos;
-                    if( remainder[width] == '\n' )
-                        width--;
-                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
-                }
-
-                if( width == remainder.size() ) {
-                    spliceLine( indent, remainder, width );
-                }
-                else if( remainder[width] == '\n' ) {
-                    spliceLine( indent, remainder, width );
-                    if( width <= 1 || remainder.size() != 1 )
-                        remainder = remainder.substr( 1 );
-                    indent = _attr.indent;
-                }
-                else {
-                    pos = remainder.find_last_of( wrappableChars, width );
-                    if( pos != std::string::npos && pos > 0 ) {
-                        spliceLine( indent, remainder, pos );
-                        if( remainder[0] == ' ' )
-                            remainder = remainder.substr( 1 );
-                    }
-                    else {
-                        spliceLine( indent, remainder, width-1 );
-                        lines.back() += "-";
-                    }
-                    if( lines.size() == 1 )
-                        indent = _attr.indent;
-                    if( tabPos != std::string::npos )
-                        indent += tabPos;
-                }
-            }
-        }
-
-        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
-            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
-            _remainder = _remainder.substr( _pos );
-        }
-
-        typedef std::vector<std::string>::const_iterator const_iterator;
-
-        const_iterator begin() const { return lines.begin(); }
-        const_iterator end() const { return lines.end(); }
-        std::string const& last() const { return lines.back(); }
-        std::size_t size() const { return lines.size(); }
-        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << *this;
-            return oss.str();
-        }
-
-        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
-            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
-                it != itEnd; ++it ) {
-                if( it != _text.begin() )
-                    _stream << "\n";
-                _stream << *it;
-            }
-            return _stream;
-        }
-
-    private:
-        std::string str;
-        TextAttributes attr;
-        std::vector<std::string> lines;
-    };
-
-} // end namespace Tbc
-
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
-
-#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-
-namespace Catch {
-    using Tbc::Text;
-    using Tbc::TextAttributes;
-}
-
-// #included from: catch_console_colour.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
-
-namespace Catch {
-
-    struct Colour {
-        enum Code {
-            None = 0,
-
-            White,
-            Red,
-            Green,
-            Blue,
-            Cyan,
-            Yellow,
-            Grey,
-
-            Bright = 0x10,
-
-            BrightRed = Bright | Red,
-            BrightGreen = Bright | Green,
-            LightGrey = Bright | Grey,
-            BrightWhite = Bright | White,
-
-            // By intention
-            FileName = LightGrey,
-            Warning = Yellow,
-            ResultError = BrightRed,
-            ResultSuccess = BrightGreen,
-            ResultExpectedFailure = Warning,
-
-            Error = BrightRed,
-            Success = Green,
-
-            OriginalExpression = Cyan,
-            ReconstructedExpression = Yellow,
-
-            SecondaryText = LightGrey,
-            Headers = White
-        };
-
-        // Use constructed object for RAII guard
-        Colour( Code _colourCode );
-        Colour( Colour const& other );
-        ~Colour();
-
-        // Use static method for one-shot changes
-        static void use( Code _colourCode );
-
-    private:
-        bool m_moved;
-    };
-
-    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
-
-} // end namespace Catch
-
-// #included from: catch_interfaces_reporter.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
-
-#include <string>
-#include <ostream>
-#include <map>
-#include <assert.h>
-
-namespace Catch
-{
-    struct ReporterConfig {
-        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
-        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
-
-        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
-        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
-
-        std::ostream& stream() const    { return *m_stream; }
-        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
-
-    private:
-        std::ostream* m_stream;
-        Ptr<IConfig> m_fullConfig;
-    };
-
-    struct ReporterPreferences {
-        ReporterPreferences()
-        : shouldRedirectStdOut( false )
-        {}
-
-        bool shouldRedirectStdOut;
-    };
-
-    template<typename T>
-    struct LazyStat : Option<T> {
-        LazyStat() : used( false ) {}
-        LazyStat& operator=( T const& _value ) {
-            Option<T>::operator=( _value );
-            used = false;
-            return *this;
-        }
-        void reset() {
-            Option<T>::reset();
-            used = false;
-        }
-        bool used;
-    };
-
-    struct TestRunInfo {
-        TestRunInfo( std::string const& _name ) : name( _name ) {}
-        std::string name;
-    };
-    struct GroupInfo {
-        GroupInfo(  std::string const& _name,
-                    std::size_t _groupIndex,
-                    std::size_t _groupsCount )
-        :   name( _name ),
-            groupIndex( _groupIndex ),
-            groupsCounts( _groupsCount )
-        {}
-
-        std::string name;
-        std::size_t groupIndex;
-        std::size_t groupsCounts;
-    };
-
-    struct AssertionStats {
-        AssertionStats( AssertionResult const& _assertionResult,
-                        std::vector<MessageInfo> const& _infoMessages,
-                        Totals const& _totals )
-        :   assertionResult( _assertionResult ),
-            infoMessages( _infoMessages ),
-            totals( _totals )
-        {
-            if( assertionResult.hasMessage() ) {
-                // Copy message into messages list.
-                // !TBD This should have been done earlier, somewhere
-                MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
-                builder << assertionResult.getMessage();
-                builder.m_info.message = builder.m_stream.str();
-
-                infoMessages.push_back( builder.m_info );
-            }
-        }
-        virtual ~AssertionStats();
-
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        AssertionStats( AssertionStats const& )              = default;
-        AssertionStats( AssertionStats && )                  = default;
-        AssertionStats& operator = ( AssertionStats const& ) = default;
-        AssertionStats& operator = ( AssertionStats && )     = default;
-#  endif
-
-        AssertionResult assertionResult;
-        std::vector<MessageInfo> infoMessages;
-        Totals totals;
-    };
-
-    struct SectionStats {
-        SectionStats(   SectionInfo const& _sectionInfo,
-                        Counts const& _assertions,
-                        double _durationInSeconds,
-                        bool _missingAssertions )
-        :   sectionInfo( _sectionInfo ),
-            assertions( _assertions ),
-            durationInSeconds( _durationInSeconds ),
-            missingAssertions( _missingAssertions )
-        {}
-        virtual ~SectionStats();
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        SectionStats( SectionStats const& )              = default;
-        SectionStats( SectionStats && )                  = default;
-        SectionStats& operator = ( SectionStats const& ) = default;
-        SectionStats& operator = ( SectionStats && )     = default;
-#  endif
-
-        SectionInfo sectionInfo;
-        Counts assertions;
-        double durationInSeconds;
-        bool missingAssertions;
-    };
-
-    struct TestCaseStats {
-        TestCaseStats(  TestCaseInfo const& _testInfo,
-                        Totals const& _totals,
-                        std::string const& _stdOut,
-                        std::string const& _stdErr,
-                        bool _aborting )
-        : testInfo( _testInfo ),
-            totals( _totals ),
-            stdOut( _stdOut ),
-            stdErr( _stdErr ),
-            aborting( _aborting )
-        {}
-        virtual ~TestCaseStats();
-
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        TestCaseStats( TestCaseStats const& )              = default;
-        TestCaseStats( TestCaseStats && )                  = default;
-        TestCaseStats& operator = ( TestCaseStats const& ) = default;
-        TestCaseStats& operator = ( TestCaseStats && )     = default;
-#  endif
-
-        TestCaseInfo testInfo;
-        Totals totals;
-        std::string stdOut;
-        std::string stdErr;
-        bool aborting;
-    };
-
-    struct TestGroupStats {
-        TestGroupStats( GroupInfo const& _groupInfo,
-                        Totals const& _totals,
-                        bool _aborting )
-        :   groupInfo( _groupInfo ),
-            totals( _totals ),
-            aborting( _aborting )
-        {}
-        TestGroupStats( GroupInfo const& _groupInfo )
-        :   groupInfo( _groupInfo ),
-            aborting( false )
-        {}
-        virtual ~TestGroupStats();
-
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        TestGroupStats( TestGroupStats const& )              = default;
-        TestGroupStats( TestGroupStats && )                  = default;
-        TestGroupStats& operator = ( TestGroupStats const& ) = default;
-        TestGroupStats& operator = ( TestGroupStats && )     = default;
-#  endif
-
-        GroupInfo groupInfo;
-        Totals totals;
-        bool aborting;
-    };
-
-    struct TestRunStats {
-        TestRunStats(   TestRunInfo const& _runInfo,
-                        Totals const& _totals,
-                        bool _aborting )
-        :   runInfo( _runInfo ),
-            totals( _totals ),
-            aborting( _aborting )
-        {}
-        virtual ~TestRunStats();
-
-#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        TestRunStats( TestRunStats const& _other )
-        :   runInfo( _other.runInfo ),
-            totals( _other.totals ),
-            aborting( _other.aborting )
-        {}
-#  else
-        TestRunStats( TestRunStats const& )              = default;
-        TestRunStats( TestRunStats && )                  = default;
-        TestRunStats& operator = ( TestRunStats const& ) = default;
-        TestRunStats& operator = ( TestRunStats && )     = default;
-#  endif
-
-        TestRunInfo runInfo;
-        Totals totals;
-        bool aborting;
-    };
-
-    struct IStreamingReporter : IShared {
-        virtual ~IStreamingReporter();
-
-        // Implementing class must also provide the following static method:
-        // static std::string getDescription();
-
-        virtual ReporterPreferences getPreferences() const = 0;
-
-        virtual void noMatchingTestCases( std::string const& spec ) = 0;
-
-        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
-
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
-
-        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
-
-        // The return value indicates if the messages buffer should be cleared:
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
-        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
-        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
-
-        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
-    };
-
-    struct IReporterFactory {
-        virtual ~IReporterFactory();
-        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
-        virtual std::string getDescription() const = 0;
-    };
-
-    struct IReporterRegistry {
-        typedef std::map<std::string, IReporterFactory*> FactoryMap;
-
-        virtual ~IReporterRegistry();
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
-        virtual FactoryMap const& getFactories() const = 0;
-    };
-
-}
-
-#include <limits>
-#include <algorithm>
-
-namespace Catch {
-
-    inline std::size_t listTests( Config const& config ) {
-
-        TestSpec testSpec = config.testSpec();
-        if( config.testSpec().hasFilters() )
-            Catch::cout() << "Matching test cases:\n";
-        else {
-            Catch::cout() << "All available test cases:\n";
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        }
-
-        std::size_t matchedTests = 0;
-        TextAttributes nameAttr, tagsAttr;
-        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
-        tagsAttr.setIndent( 6 );
-
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            matchedTests++;
-            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
-            Colour::Code colour = testCaseInfo.isHidden()
-                ? Colour::SecondaryText
-                : Colour::None;
-            Colour colourGuard( colour );
-
-            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
-            if( !testCaseInfo.tags.empty() )
-                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
-        }
-
-        if( !config.testSpec().hasFilters() )
-            Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
-        else
-            Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
-        return matchedTests;
-    }
-
-    inline std::size_t listTestsNamesOnly( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( !config.testSpec().hasFilters() )
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            matchedTests++;
-            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
-            Catch::cout() << testCaseInfo.name << std::endl;
-        }
-        return matchedTests;
-    }
-
-    struct TagInfo {
-        TagInfo() : count ( 0 ) {}
-        void add( std::string const& spelling ) {
-            ++count;
-            spellings.insert( spelling );
-        }
-        std::string all() const {
-            std::string out;
-            for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
-                        it != itEnd;
-                        ++it )
-                out += "[" + *it + "]";
-            return out;
-        }
-        std::set<std::string> spellings;
-        std::size_t count;
-    };
-
-    inline std::size_t listTags( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( config.testSpec().hasFilters() )
-            Catch::cout() << "Tags for matching test cases:\n";
-        else {
-            Catch::cout() << "All available tags:\n";
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        }
-
-        std::map<std::string, TagInfo> tagCounts;
-
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            for( std::set<std::string>::const_iterator  tagIt = it->getTestCaseInfo().tags.begin(),
-                                                        tagItEnd = it->getTestCaseInfo().tags.end();
-                    tagIt != tagItEnd;
-                    ++tagIt ) {
-                std::string tagName = *tagIt;
-                std::string lcaseTagName = toLower( tagName );
-                std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
-                if( countIt == tagCounts.end() )
-                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
-                countIt->second.add( tagName );
-            }
-        }
-
-        for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
-                                                            countItEnd = tagCounts.end();
-                countIt != countItEnd;
-                ++countIt ) {
-            std::ostringstream oss;
-            oss << "  " << std::setw(2) << countIt->second.count << "  ";
-            Text wrapper( countIt->second.all(), TextAttributes()
-                                                    .setInitialIndent( 0 )
-                                                    .setIndent( oss.str().size() )
-                                                    .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
-            Catch::cout() << oss.str() << wrapper << "\n";
-        }
-        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
-        return tagCounts.size();
-    }
-
-    inline std::size_t listReporters( Config const& /*config*/ ) {
-        Catch::cout() << "Available reporters:\n";
-        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
-        IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
-        std::size_t maxNameLen = 0;
-        for(it = itBegin; it != itEnd; ++it )
-            maxNameLen = (std::max)( maxNameLen, it->first.size() );
-
-        for(it = itBegin; it != itEnd; ++it ) {
-            Text wrapper( it->second->getDescription(), TextAttributes()
-                                                        .setInitialIndent( 0 )
-                                                        .setIndent( 7+maxNameLen )
-                                                        .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
-            Catch::cout() << "  "
-                    << it->first
-                    << ":"
-                    << std::string( maxNameLen - it->first.size() + 2, ' ' )
-                    << wrapper << "\n";
-        }
-        Catch::cout() << std::endl;
-        return factories.size();
-    }
-
-    inline Option<std::size_t> list( Config const& config ) {
-        Option<std::size_t> listedCount;
-        if( config.listTests() )
-            listedCount = listedCount.valueOr(0) + listTests( config );
-        if( config.listTestNamesOnly() )
-            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
-        if( config.listTags() )
-            listedCount = listedCount.valueOr(0) + listTags( config );
-        if( config.listReporters() )
-            listedCount = listedCount.valueOr(0) + listReporters( config );
-        return listedCount;
-    }
-
-} // end namespace Catch
-
-// #included from: internal/catch_runner_impl.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
-
-// #included from: catch_test_case_tracker.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
-
-#include <map>
-#include <string>
-#include <assert.h>
-
-namespace Catch {
-namespace SectionTracking {
-
-    class TrackedSection {
-
-        typedef std::map<std::string, TrackedSection> TrackedSections;
-
-    public:
-        enum RunState {
-            NotStarted,
-            Executing,
-            ExecutingChildren,
-            Completed
-        };
-
-        TrackedSection( std::string const& name, TrackedSection* parent )
-        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
-        {}
-
-        RunState runState() const { return m_runState; }
-
-        TrackedSection* findChild( std::string const& childName );
-        TrackedSection* acquireChild( std::string const& childName );
-
-        void enter() {
-            if( m_runState == NotStarted )
-                m_runState = Executing;
-        }
-        void leave();
-
-        TrackedSection* getParent() {
-            return m_parent;
-        }
-        bool hasChildren() const {
-            return !m_children.empty();
-        }
-
-    private:
-        std::string m_name;
-        RunState m_runState;
-        TrackedSections m_children;
-        TrackedSection* m_parent;
-    };
-
-    inline TrackedSection* TrackedSection::findChild( std::string const& childName ) {
-        TrackedSections::iterator it = m_children.find( childName );
-        return it != m_children.end()
-            ? &it->second
-            : NULL;
-    }
-    inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) {
-        if( TrackedSection* child = findChild( childName ) )
-            return child;
-        m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
-        return findChild( childName );
-    }
-    inline void TrackedSection::leave() {
-        for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
-                it != itEnd;
-                ++it )
-            if( it->second.runState() != Completed ) {
-                m_runState = ExecutingChildren;
-                return;
-            }
-        m_runState = Completed;
-    }
-
-    class TestCaseTracker {
-    public:
-        TestCaseTracker( std::string const& testCaseName )
-        :   m_testCase( testCaseName, NULL ),
-            m_currentSection( &m_testCase ),
-            m_completedASectionThisRun( false )
-        {}
-
-        bool enterSection( std::string const& name ) {
-            TrackedSection* child = m_currentSection->acquireChild( name );
-            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
-                return false;
-
-            m_currentSection = child;
-            m_currentSection->enter();
-            return true;
-        }
-        void leaveSection() {
-            m_currentSection->leave();
-            m_currentSection = m_currentSection->getParent();
-            assert( m_currentSection != NULL );
-            m_completedASectionThisRun = true;
-        }
-
-        bool currentSectionHasChildren() const {
-            return m_currentSection->hasChildren();
-        }
-        bool isCompleted() const {
-            return m_testCase.runState() == TrackedSection::Completed;
-        }
-
-        class Guard {
-        public:
-            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
-                m_tracker.enterTestCase();
-            }
-            ~Guard() {
-                m_tracker.leaveTestCase();
-            }
-        private:
-            Guard( Guard const& );
-            void operator = ( Guard const& );
-            TestCaseTracker& m_tracker;
-        };
-
-    private:
-        void enterTestCase() {
-            m_currentSection = &m_testCase;
-            m_completedASectionThisRun = false;
-            m_testCase.enter();
-        }
-        void leaveTestCase() {
-            m_testCase.leave();
-        }
-
-        TrackedSection m_testCase;
-        TrackedSection* m_currentSection;
-        bool m_completedASectionThisRun;
-    };
-
-} // namespace SectionTracking
-
-using SectionTracking::TestCaseTracker;
-
-} // namespace Catch
-
-// #included from: catch_fatal_condition.hpp
-#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
-
-namespace Catch {
-
-    // Report the error condition then exit the process
-    inline void fatal( std::string const& message, int exitCode ) {
-        IContext& context = Catch::getCurrentContext();
-        IResultCapture* resultCapture = context.getResultCapture();
-        resultCapture->handleFatalErrorCondition( message );
-
-               if( Catch::alwaysTrue() ) // avoids "no return" warnings
-            exit( exitCode );
-    }
-
-} // namespace Catch
-
-#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
-
-namespace Catch {
-
-    struct FatalConditionHandler {
-               void reset() {}
-       };
-
-} // namespace Catch
-
-#else // Not Windows - assumed to be POSIX compatible //////////////////////////
-
-#include <signal.h>
-
-namespace Catch {
-
-    struct SignalDefs { int id; const char* name; };
-    extern SignalDefs signalDefs[];
-    SignalDefs signalDefs[] = {
-            { SIGINT,  "SIGINT - Terminal interrupt signal" },
-            { SIGILL,  "SIGILL - Illegal instruction signal" },
-            { SIGFPE,  "SIGFPE - Floating point error signal" },
-            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
-            { SIGTERM, "SIGTERM - Termination request signal" },
-            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
-        };
-
-    struct FatalConditionHandler {
-
-        static void handleSignal( int sig ) {
-            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
-                if( sig == signalDefs[i].id )
-                    fatal( signalDefs[i].name, -sig );
-            fatal( "<unknown signal>", -sig );
-        }
-
-        FatalConditionHandler() : m_isSet( true ) {
-            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
-                signal( signalDefs[i].id, handleSignal );
-        }
-        ~FatalConditionHandler() {
-            reset();
-        }
-        void reset() {
-            if( m_isSet ) {
-                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
-                    signal( signalDefs[i].id, SIG_DFL );
-                m_isSet = false;
-            }
-        }
-
-        bool m_isSet;
-    };
-
-} // namespace Catch
-
-#endif // not Windows
-
-#include <set>
-#include <string>
-
-namespace Catch {
-
-    class StreamRedirect {
-
-    public:
-        StreamRedirect( std::ostream& stream, std::string& targetString )
-        :   m_stream( stream ),
-            m_prevBuf( stream.rdbuf() ),
-            m_targetString( targetString )
-        {
-            stream.rdbuf( m_oss.rdbuf() );
-        }
-
-        ~StreamRedirect() {
-            m_targetString += m_oss.str();
-            m_stream.rdbuf( m_prevBuf );
-        }
-
-    private:
-        std::ostream& m_stream;
-        std::streambuf* m_prevBuf;
-        std::ostringstream m_oss;
-        std::string& m_targetString;
-    };
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    class RunContext : public IResultCapture, public IRunner {
-
-        RunContext( RunContext const& );
-        void operator =( RunContext const& );
-
-    public:
-
-        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
-        :   m_runInfo( config->name() ),
-            m_context( getCurrentMutableContext() ),
-            m_activeTestCase( NULL ),
-            m_config( config ),
-            m_reporter( reporter ),
-            m_prevRunner( m_context.getRunner() ),
-            m_prevResultCapture( m_context.getResultCapture() ),
-            m_prevConfig( m_context.getConfig() )
-        {
-            m_context.setRunner( this );
-            m_context.setConfig( m_config );
-            m_context.setResultCapture( this );
-            m_reporter->testRunStarting( m_runInfo );
-        }
-
-        virtual ~RunContext() {
-            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
-            m_context.setRunner( m_prevRunner );
-            m_context.setConfig( NULL );
-            m_context.setResultCapture( m_prevResultCapture );
-            m_context.setConfig( m_prevConfig );
-        }
-
-        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
-            m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
-        }
-        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
-            m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
-        }
-
-        Totals runTest( TestCase const& testCase ) {
-            Totals prevTotals = m_totals;
-
-            std::string redirectedCout;
-            std::string redirectedCerr;
-
-            TestCaseInfo testInfo = testCase.getTestCaseInfo();
-
-            m_reporter->testCaseStarting( testInfo );
-
-            m_activeTestCase = &testCase;
-            m_testCaseTracker = TestCaseTracker( testInfo.name );
-
-            do {
-                do {
-                    runCurrentTest( redirectedCout, redirectedCerr );
-                }
-                while( !m_testCaseTracker->isCompleted() && !aborting() );
-            }
-            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
-
-            Totals deltaTotals = m_totals.delta( prevTotals );
-            m_totals.testCases += deltaTotals.testCases;
-            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
-                                                        deltaTotals,
-                                                        redirectedCout,
-                                                        redirectedCerr,
-                                                        aborting() ) );
-
-            m_activeTestCase = NULL;
-            m_testCaseTracker.reset();
-
-            return deltaTotals;
-        }
-
-        Ptr<IConfig const> config() const {
-            return m_config;
-        }
-
-    private: // IResultCapture
-
-        virtual void assertionEnded( AssertionResult const& result ) {
-            if( result.getResultType() == ResultWas::Ok ) {
-                m_totals.assertions.passed++;
-            }
-            else if( !result.isOk() ) {
-                m_totals.assertions.failed++;
-            }
-
-            if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
-                m_messages.clear();
-
-            // Reset working state
-            m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
-            m_lastResult = result;
-        }
-
-        virtual bool sectionStarted (
-            SectionInfo const& sectionInfo,
-            Counts& assertions
-        )
-        {
-            std::ostringstream oss;
-            oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
-
-            if( !m_testCaseTracker->enterSection( oss.str() ) )
-                return false;
-
-            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
-
-            m_reporter->sectionStarting( sectionInfo );
-
-            assertions = m_totals.assertions;
-
-            return true;
-        }
-        bool testForMissingAssertions( Counts& assertions ) {
-            if( assertions.total() != 0 ||
-                    !m_config->warnAboutMissingAssertions() ||
-                    m_testCaseTracker->currentSectionHasChildren() )
-                return false;
-            m_totals.assertions.failed++;
-            assertions.failed++;
-            return true;
-        }
-
-        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
-            if( std::uncaught_exception() ) {
-                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
-                return;
-            }
-
-            Counts assertions = m_totals.assertions - prevAssertions;
-            bool missingAssertions = testForMissingAssertions( assertions );
-
-            m_testCaseTracker->leaveSection();
-
-            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
-            m_messages.clear();
-        }
-
-        virtual void pushScopedMessage( MessageInfo const& message ) {
-            m_messages.push_back( message );
-        }
-
-        virtual void popScopedMessage( MessageInfo const& message ) {
-            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
-        }
-
-        virtual std::string getCurrentTestName() const {
-            return m_activeTestCase
-                ? m_activeTestCase->getTestCaseInfo().name
-                : "";
-        }
-
-        virtual const AssertionResult* getLastResult() const {
-            return &m_lastResult;
-        }
-
-        virtual void handleFatalErrorCondition( std::string const& message ) {
-            ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
-            resultBuilder.setResultType( ResultWas::FatalErrorCondition );
-            resultBuilder << message;
-            resultBuilder.captureExpression();
-
-            handleUnfinishedSections();
-
-            // Recreate section for test case (as we will lose the one that was in scope)
-            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
-
-            Counts assertions;
-            assertions.failed = 1;
-            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
-            m_reporter->sectionEnded( testCaseSectionStats );
-
-            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
-
-            Totals deltaTotals;
-            deltaTotals.testCases.failed = 1;
-            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
-                                                        deltaTotals,
-                                                        "",
-                                                        "",
-                                                        false ) );
-            m_totals.testCases.failed++;
-            testGroupEnded( "", m_totals, 1, 1 );
-            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
-        }
-
-    public:
-        // !TBD We need to do this another way!
-        bool aborting() const {
-            return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
-        }
-
-    private:
-
-        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
-            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
-            m_reporter->sectionStarting( testCaseSection );
-            Counts prevAssertions = m_totals.assertions;
-            double duration = 0;
-            try {
-                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
-                TestCaseTracker::Guard guard( *m_testCaseTracker );
-
-                Timer timer;
-                timer.start();
-                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
-                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
-                    StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
-                    invokeActiveTestCase();
-                }
-                else {
-                    invokeActiveTestCase();
-                }
-                duration = timer.getElapsedSeconds();
-            }
-            catch( TestFailureException& ) {
-                // This just means the test was aborted due to failure
-            }
-            catch(...) {
-                makeUnexpectedResultBuilder().useActiveException();
-            }
-            handleUnfinishedSections();
-            m_messages.clear();
-
-            Counts assertions = m_totals.assertions - prevAssertions;
-            bool missingAssertions = testForMissingAssertions( assertions );
-
-            if( testCaseInfo.okToFail() ) {
-                std::swap( assertions.failedButOk, assertions.failed );
-                m_totals.assertions.failed -= assertions.failedButOk;
-                m_totals.assertions.failedButOk += assertions.failedButOk;
-            }
-
-            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
-            m_reporter->sectionEnded( testCaseSectionStats );
-        }
-
-        void invokeActiveTestCase() {
-            FatalConditionHandler fatalConditionHandler; // Handle signals
-            m_activeTestCase->invoke();
-            fatalConditionHandler.reset();
-        }
-
-    private:
-
-        ResultBuilder makeUnexpectedResultBuilder() const {
-            return ResultBuilder(   m_lastAssertionInfo.macroName.c_str(),
-                                    m_lastAssertionInfo.lineInfo,
-                                    m_lastAssertionInfo.capturedExpression.c_str(),
-                                    m_lastAssertionInfo.resultDisposition );
-        }
-
-        void handleUnfinishedSections() {
-            // If sections ended prematurely due to an exception we stored their
-            // infos here so we can tear them down outside the unwind process.
-            for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
-                        itEnd = m_unfinishedSections.rend();
-                    it != itEnd;
-                    ++it )
-                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
-            m_unfinishedSections.clear();
-        }
-
-        struct UnfinishedSections {
-            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
-            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
-            {}
-
-            SectionInfo info;
-            Counts prevAssertions;
-            double durationInSeconds;
-        };
-
-        TestRunInfo m_runInfo;
-        IMutableContext& m_context;
-        TestCase const* m_activeTestCase;
-        Option<TestCaseTracker> m_testCaseTracker;
-        AssertionResult m_lastResult;
-
-        Ptr<IConfig const> m_config;
-        Totals m_totals;
-        Ptr<IStreamingReporter> m_reporter;
-        std::vector<MessageInfo> m_messages;
-        IRunner* m_prevRunner;
-        IResultCapture* m_prevResultCapture;
-        Ptr<IConfig const> m_prevConfig;
-        AssertionInfo m_lastAssertionInfo;
-        std::vector<UnfinishedSections> m_unfinishedSections;
-    };
-
-    IResultCapture& getResultCapture() {
-        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
-            return *capture;
-        else
-            throw std::logic_error( "No result capture instance" );
-    }
-
-} // end namespace Catch
-
-// #included from: internal/catch_version.h
-#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
-
-namespace Catch {
-
-    // Versioning information
-    struct Version {
-        Version(    unsigned int _majorVersion,
-                    unsigned int _minorVersion,
-                    unsigned int _patchNumber,
-                    std::string const& _branchName,
-                    unsigned int _buildNumber );
-
-        unsigned int const majorVersion;
-        unsigned int const minorVersion;
-        unsigned int const patchNumber;
-
-        // buildNumber is only used if branchName is not null
-        std::string const branchName;
-        unsigned int const buildNumber;
-
-        friend std::ostream& operator << ( std::ostream& os, Version const& version );
-
-    private:
-        void operator=( Version const& );
-    };
-
-    extern Version libraryVersion;
-}
-
-#include <fstream>
-#include <stdlib.h>
-#include <limits>
-
-namespace Catch {
-
-    class Runner {
-
-    public:
-        Runner( Ptr<Config> const& config )
-        :   m_config( config )
-        {
-            openStream();
-            makeReporter();
-        }
-
-        Totals runTests() {
-
-            RunContext context( m_config.get(), m_reporter );
-
-            Totals totals;
-
-            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
-
-            TestSpec testSpec = m_config->testSpec();
-            if( !testSpec.hasFilters() )
-                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
-
-            std::vector<TestCase> testCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
-
-            int testsRunForGroup = 0;
-            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
-                    it != itEnd;
-                    ++it ) {
-                testsRunForGroup++;
-                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
-
-                    if( context.aborting() )
-                        break;
-
-                    totals += context.runTest( *it );
-                    m_testsAlreadyRun.insert( *it );
-                }
-            }
-            std::vector<TestCase> skippedTestCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
-
-            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
-                    it != itEnd;
-                    ++it )
-                m_reporter->skipTest( *it );
-
-            context.testGroupEnded( "all tests", totals, 1, 1 );
-            return totals;
-        }
-
-    private:
-        void openStream() {
-            // Open output file, if specified
-            if( !m_config->getFilename().empty() ) {
-                m_ofs.open( m_config->getFilename().c_str() );
-                if( m_ofs.fail() ) {
-                    std::ostringstream oss;
-                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
-                    throw std::domain_error( oss.str() );
-                }
-                m_config->setStreamBuf( m_ofs.rdbuf() );
-            }
-        }
-        void makeReporter() {
-            std::string reporterName = m_config->getReporterName().empty()
-                ? "console"
-                : m_config->getReporterName();
-
-            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
-            if( !m_reporter ) {
-                std::ostringstream oss;
-                oss << "No reporter registered with name: '" << reporterName << "'";
-                throw std::domain_error( oss.str() );
-            }
-        }
-
-    private:
-        Ptr<Config> m_config;
-        std::ofstream m_ofs;
-        Ptr<IStreamingReporter> m_reporter;
-        std::set<TestCase> m_testsAlreadyRun;
-    };
-
-    class Session : NonCopyable {
-        static bool alreadyInstantiated;
-
-    public:
-
-        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
-
-        Session()
-        : m_cli( makeCommandLineParser() ) {
-            if( alreadyInstantiated ) {
-                std::string msg = "Only one instance of Catch::Session can ever be used";
-                Catch::cerr() << msg << std::endl;
-                throw std::logic_error( msg );
-            }
-            alreadyInstantiated = true;
-        }
-        ~Session() {
-            Catch::cleanUp();
-        }
-
-        void showHelp( std::string const& processName ) {
-            Catch::cout() << "\nCatch v" << libraryVersion << "\n";
-
-            m_cli.usage( Catch::cout(), processName );
-            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
-        }
-
-        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
-            try {
-                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
-                m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
-                if( m_configData.showHelp )
-                    showHelp( m_configData.processName );
-                m_config.reset();
-            }
-            catch( std::exception& ex ) {
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()
-                        << "\nError(s) in input:\n"
-                        << Text( ex.what(), TextAttributes().setIndent(2) )
-                        << "\n\n";
-                }
-                m_cli.usage( Catch::cout(), m_configData.processName );
-                return (std::numeric_limits<int>::max)();
-            }
-            return 0;
-        }
-
-        void useConfigData( ConfigData const& _configData ) {
-            m_configData = _configData;
-            m_config.reset();
-        }
-
-        int run( int argc, char* const argv[] ) {
-
-            int returnCode = applyCommandLine( argc, argv );
-            if( returnCode == 0 )
-                returnCode = run();
-            return returnCode;
-        }
-
-        int run() {
-            if( m_configData.showHelp )
-                return 0;
-
-            try
-            {
-                config(); // Force config to be constructed
-
-                std::srand( m_configData.rngSeed );
-
-                Runner runner( m_config );
-
-                // Handle list request
-                if( Option<std::size_t> listed = list( config() ) )
-                    return static_cast<int>( *listed );
-
-                return static_cast<int>( runner.runTests().assertions.failed );
-            }
-            catch( std::exception& ex ) {
-                Catch::cerr() << ex.what() << std::endl;
-                return (std::numeric_limits<int>::max)();
-            }
-        }
-
-        Clara::CommandLine<ConfigData> const& cli() const {
-            return m_cli;
-        }
-        std::vector<Clara::Parser::Token> const& unusedTokens() const {
-            return m_unusedTokens;
-        }
-        ConfigData& configData() {
-            return m_configData;
-        }
-        Config& config() {
-            if( !m_config )
-                m_config = new Config( m_configData );
-            return *m_config;
-        }
-
-    private:
-        Clara::CommandLine<ConfigData> m_cli;
-        std::vector<Clara::Parser::Token> m_unusedTokens;
-        ConfigData m_configData;
-        Ptr<Config> m_config;
-    };
-
-    bool Session::alreadyInstantiated = false;
-
-} // end namespace Catch
-
-// #included from: catch_registry_hub.hpp
-#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
-
-// #included from: catch_test_case_registry_impl.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
-
-#include <vector>
-#include <set>
-#include <sstream>
-#include <iostream>
-#include <algorithm>
-
-namespace Catch {
-
-    class TestRegistry : public ITestCaseRegistry {
-        struct LexSort {
-            bool operator() (TestCase i,TestCase j) const { return (i<j);}
-        };
-        struct RandomNumberGenerator {
-            int operator()( int n ) const { return std::rand() % n; }
-        };
-
-    public:
-        TestRegistry() : m_unnamedCount( 0 ) {}
-        virtual ~TestRegistry();
-
-        virtual void registerTest( TestCase const& testCase ) {
-            std::string name = testCase.getTestCaseInfo().name;
-            if( name == "" ) {
-                std::ostringstream oss;
-                oss << "Anonymous test case " << ++m_unnamedCount;
-                return registerTest( testCase.withName( oss.str() ) );
-            }
-
-            if( m_functions.find( testCase ) == m_functions.end() ) {
-                m_functions.insert( testCase );
-                m_functionsInOrder.push_back( testCase );
-                if( !testCase.isHidden() )
-                    m_nonHiddenFunctions.push_back( testCase );
-            }
-            else {
-                TestCase const& prev = *m_functions.find( testCase );
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
-                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
-                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
-                }
-                exit(1);
-            }
-        }
-
-        virtual std::vector<TestCase> const& getAllTests() const {
-            return m_functionsInOrder;
-        }
-
-        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
-            return m_nonHiddenFunctions;
-        }
-
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
-
-            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
-                                                        itEnd = m_functionsInOrder.end();
-                    it != itEnd;
-                    ++it ) {
-                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
-                if( includeTest != negated )
-                    matchingTestCases.push_back( *it );
-            }
-            sortTests( config, matchingTestCases );
-        }
-
-    private:
-
-        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
-
-            switch( config.runOrder() ) {
-                case RunTests::InLexicographicalOrder:
-                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
-                    break;
-                case RunTests::InRandomOrder:
-                {
-                    RandomNumberGenerator rng;
-                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
-                }
-                    break;
-                case RunTests::InDeclarationOrder:
-                    // already in declaration order
-                    break;
-            }
-        }
-        std::set<TestCase> m_functions;
-        std::vector<TestCase> m_functionsInOrder;
-        std::vector<TestCase> m_nonHiddenFunctions;
-        size_t m_unnamedCount;
-    };
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
-    public:
-
-        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
-
-        virtual void invoke() const {
-            m_fun();
-        }
-
-    private:
-        virtual ~FreeFunctionTestCase();
-
-        TestFunction m_fun;
-    };
-
-    inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
-        std::string className = classOrQualifiedMethodName;
-        if( startsWith( className, "&" ) )
-        {
-            std::size_t lastColons = className.rfind( "::" );
-            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
-            if( penultimateColons == std::string::npos )
-                penultimateColons = 1;
-            className = className.substr( penultimateColons, lastColons-penultimateColons );
-        }
-        return className;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    AutoReg::AutoReg(   TestFunction function,
-                        SourceLineInfo const& lineInfo,
-                        NameAndDesc const& nameAndDesc ) {
-        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
-    }
-
-    AutoReg::~AutoReg() {}
-
-    void AutoReg::registerTestCase( ITestCase* testCase,
-                                    char const* classOrQualifiedMethodName,
-                                    NameAndDesc const& nameAndDesc,
-                                    SourceLineInfo const& lineInfo ) {
-
-        getMutableRegistryHub().registerTest
-            ( makeTestCase( testCase,
-                            extractClassName( classOrQualifiedMethodName ),
-                            nameAndDesc.name,
-                            nameAndDesc.description,
-                            lineInfo ) );
-    }
-
-} // end namespace Catch
-
-// #included from: catch_reporter_registry.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
-
-#include <map>
-
-namespace Catch {
-
-    class ReporterRegistry : public IReporterRegistry {
-
-    public:
-
-        virtual ~ReporterRegistry() {
-            deleteAllValues( m_factories );
-        }
-
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
-            FactoryMap::const_iterator it =  m_factories.find( name );
-            if( it == m_factories.end() )
-                return NULL;
-            return it->second->create( ReporterConfig( config ) );
-        }
-
-        void registerReporter( std::string const& name, IReporterFactory* factory ) {
-            m_factories.insert( std::make_pair( name, factory ) );
-        }
-
-        FactoryMap const& getFactories() const {
-            return m_factories;
-        }
-
-    private:
-        FactoryMap m_factories;
-    };
-}
-
-// #included from: catch_exception_translator_registry.hpp
-#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
-
-#ifdef __OBJC__
-#import "Foundation/Foundation.h"
-#endif
-
-namespace Catch {
-
-    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
-    public:
-        ~ExceptionTranslatorRegistry() {
-            deleteAll( m_translators );
-        }
-
-        virtual void registerTranslator( const IExceptionTranslator* translator ) {
-            m_translators.push_back( translator );
-        }
-
-        virtual std::string translateActiveException() const {
-            try {
-#ifdef __OBJC__
-                // In Objective-C try objective-c exceptions first
-                @try {
-                    throw;
-                }
-                @catch (NSException *exception) {
-                    return Catch::toString( [exception description] );
-                }
-#else
-                throw;
-#endif
-            }
-            catch( TestFailureException& ) {
-                throw;
-            }
-            catch( std::exception& ex ) {
-                return ex.what();
-            }
-            catch( std::string& msg ) {
-                return msg;
-            }
-            catch( const char* msg ) {
-                return msg;
-            }
-            catch(...) {
-                return tryTranslators( m_translators.begin() );
-            }
-        }
-
-        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
-            if( it == m_translators.end() )
-                return "Unknown exception";
-
-            try {
-                return (*it)->translate();
-            }
-            catch(...) {
-                return tryTranslators( it+1 );
-            }
-        }
-
-    private:
-        std::vector<const IExceptionTranslator*> m_translators;
-    };
-}
-
-namespace Catch {
-
-    namespace {
-
-        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
-
-            RegistryHub( RegistryHub const& );
-            void operator=( RegistryHub const& );
-
-        public: // IRegistryHub
-            RegistryHub() {
-            }
-            virtual IReporterRegistry const& getReporterRegistry() const {
-                return m_reporterRegistry;
-            }
-            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
-                return m_testCaseRegistry;
-            }
-            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
-                return m_exceptionTranslatorRegistry;
-            }
-
-        public: // IMutableRegistryHub
-            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
-                m_reporterRegistry.registerReporter( name, factory );
-            }
-            virtual void registerTest( TestCase const& testInfo ) {
-                m_testCaseRegistry.registerTest( testInfo );
-            }
-            virtual void registerTranslator( const IExceptionTranslator* translator ) {
-                m_exceptionTranslatorRegistry.registerTranslator( translator );
-            }
-
-        private:
-            TestRegistry m_testCaseRegistry;
-            ReporterRegistry m_reporterRegistry;
-            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
-        };
-
-        // Single, global, instance
-        inline RegistryHub*& getTheRegistryHub() {
-            static RegistryHub* theRegistryHub = NULL;
-            if( !theRegistryHub )
-                theRegistryHub = new RegistryHub();
-            return theRegistryHub;
-        }
-    }
-
-    IRegistryHub& getRegistryHub() {
-        return *getTheRegistryHub();
-    }
-    IMutableRegistryHub& getMutableRegistryHub() {
-        return *getTheRegistryHub();
-    }
-    void cleanUp() {
-        delete getTheRegistryHub();
-        getTheRegistryHub() = NULL;
-        cleanUpContext();
-    }
-    std::string translateActiveException() {
-        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
-    }
-
-} // end namespace Catch
-
-// #included from: catch_notimplemented_exception.hpp
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
-
-#include <ostream>
-
-namespace Catch {
-
-    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
-    :   m_lineInfo( lineInfo ) {
-        std::ostringstream oss;
-        oss << lineInfo << ": function ";
-        oss << "not implemented";
-        m_what = oss.str();
-    }
-
-    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
-        return m_what.c_str();
-    }
-
-} // end namespace Catch
-
-// #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
-
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
-
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
-
-    class StreamBufBase : public std::streambuf {
-    public:
-        virtual ~StreamBufBase() CATCH_NOEXCEPT;
-    };
-}
-
-#include <stdexcept>
-#include <cstdio>
-#include <iostream>
-
-namespace Catch {
-
-    template<typename WriterF, size_t bufferSize=256>
-    class StreamBufImpl : public StreamBufBase {
-        char data[bufferSize];
-        WriterF m_writer;
-
-    public:
-        StreamBufImpl() {
-            setp( data, data + sizeof(data) );
-        }
-
-        ~StreamBufImpl() CATCH_NOEXCEPT {
-            sync();
-        }
-
-    private:
-        int overflow( int c ) {
-            sync();
-
-            if( c != EOF ) {
-                if( pbase() == epptr() )
-                    m_writer( std::string( 1, static_cast<char>( c ) ) );
-                else
-                    sputc( static_cast<char>( c ) );
-            }
-            return 0;
-        }
-
-        int sync() {
-            if( pbase() != pptr() ) {
-                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
-                setp( pbase(), epptr() );
-            }
-            return 0;
-        }
-    };
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    struct OutputDebugWriter {
-
-        void operator()( std::string const&str ) {
-            writeToDebugConsole( str );
-        }
-    };
-
-    Stream::Stream()
-    : streamBuf( NULL ), isOwned( false )
-    {}
-
-    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
-    : streamBuf( _streamBuf ), isOwned( _isOwned )
-    {}
-
-    void Stream::release() {
-        if( isOwned ) {
-            delete streamBuf;
-            streamBuf = NULL;
-            isOwned = false;
-        }
-    }
-
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
-    std::ostream& cout() {
-        return std::cout;
-    }
-    std::ostream& cerr() {
-        return std::cerr;
-    }
-#endif
-}
-
-namespace Catch {
-
-    class Context : public IMutableContext {
-
-        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
-        Context( Context const& );
-        void operator=( Context const& );
-
-    public: // IContext
-        virtual IResultCapture* getResultCapture() {
-            return m_resultCapture;
-        }
-        virtual IRunner* getRunner() {
-            return m_runner;
-        }
-        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
-            return getGeneratorsForCurrentTest()
-            .getGeneratorInfo( fileInfo, totalSize )
-            .getCurrentIndex();
-        }
-        virtual bool advanceGeneratorsForCurrentTest() {
-            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
-            return generators && generators->moveNext();
-        }
-
-        virtual Ptr<IConfig const> getConfig() const {
-            return m_config;
-        }
-
-    public: // IMutableContext
-        virtual void setResultCapture( IResultCapture* resultCapture ) {
-            m_resultCapture = resultCapture;
-        }
-        virtual void setRunner( IRunner* runner ) {
-            m_runner = runner;
-        }
-        virtual void setConfig( Ptr<IConfig const> const& config ) {
-            m_config = config;
-        }
-
-        friend IMutableContext& getCurrentMutableContext();
-
-    private:
-        IGeneratorsForTest* findGeneratorsForCurrentTest() {
-            std::string testName = getResultCapture()->getCurrentTestName();
-
-            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
-                m_generatorsByTestName.find( testName );
-            return it != m_generatorsByTestName.end()
-                ? it->second
-                : NULL;
-        }
-
-        IGeneratorsForTest& getGeneratorsForCurrentTest() {
-            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
-            if( !generators ) {
-                std::string testName = getResultCapture()->getCurrentTestName();
-                generators = createGeneratorsForTest();
-                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
-            }
-            return *generators;
-        }
-
-    private:
-        Ptr<IConfig const> m_config;
-        IRunner* m_runner;
-        IResultCapture* m_resultCapture;
-        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
-    };
-
-    namespace {
-        Context* currentContext = NULL;
-    }
-    IMutableContext& getCurrentMutableContext() {
-        if( !currentContext )
-            currentContext = new Context();
-        return *currentContext;
-    }
-    IContext& getCurrentContext() {
-        return getCurrentMutableContext();
-    }
-
-    Stream createStream( std::string const& streamName ) {
-        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
-        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
-        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
-
-        throw std::domain_error( "Unknown stream: " + streamName );
-    }
-
-    void cleanUpContext() {
-        delete currentContext;
-        currentContext = NULL;
-    }
-}
-
-// #included from: catch_console_colour_impl.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
-
-namespace Catch {
-    namespace {
-
-        struct IColourImpl {
-            virtual ~IColourImpl() {}
-            virtual void use( Colour::Code _colourCode ) = 0;
-        };
-
-        struct NoColourImpl : IColourImpl {
-            void use( Colour::Code ) {}
-
-            static IColourImpl* instance() {
-                static NoColourImpl s_instance;
-                return &s_instance;
-            }
-        };
-
-    } // anon namespace
-} // namespace Catch
-
-#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
-#   ifdef CATCH_PLATFORM_WINDOWS
-#       define CATCH_CONFIG_COLOUR_WINDOWS
-#   else
-#       define CATCH_CONFIG_COLOUR_ANSI
-#   endif
-#endif
-
-#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
-
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
-
-namespace Catch {
-namespace {
-
-    class Win32ColourImpl : public IColourImpl {
-    public:
-        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
-        {
-            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
-            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalAttributes = csbiInfo.wAttributes;
-        }
-
-        virtual void use( Colour::Code _colourCode ) {
-            switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalAttributes );
-                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
-                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
-                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
-                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
-                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
-                case Colour::Grey:      return setTextAttribute( 0 );
-
-                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
-                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
-                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
-                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-
-                case Colour::Bright: throw std::logic_error( "not a colour" );
-            }
-        }
-
-    private:
-        void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
-        }
-        HANDLE stdoutHandle;
-        WORD originalAttributes;
-    };
-
-    IColourImpl* platformColourInstance() {
-        static Win32ColourImpl s_instance;
-        return &s_instance;
-    }
-
-} // end anon namespace
-} // end namespace Catch
-
-#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
-
-#include <unistd.h>
-
-namespace Catch {
-namespace {
-
-    // use POSIX/ ANSI console terminal codes
-    // Thanks to Adam Strzelecki for original contribution
-    // (http://github.com/nanoant)
-    // https://github.com/philsquared/Catch/pull/131
-    class PosixColourImpl : public IColourImpl {
-    public:
-        virtual void use( Colour::Code _colourCode ) {
-            switch( _colourCode ) {
-                case Colour::None:
-                case Colour::White:     return setColour( "[0m" );
-                case Colour::Red:       return setColour( "[0;31m" );
-                case Colour::Green:     return setColour( "[0;32m" );
-                case Colour::Blue:      return setColour( "[0:34m" );
-                case Colour::Cyan:      return setColour( "[0;36m" );
-                case Colour::Yellow:    return setColour( "[0;33m" );
-                case Colour::Grey:      return setColour( "[1;30m" );
-
-                case Colour::LightGrey:     return setColour( "[0;37m" );
-                case Colour::BrightRed:     return setColour( "[1;31m" );
-                case Colour::BrightGreen:   return setColour( "[1;32m" );
-                case Colour::BrightWhite:   return setColour( "[1;37m" );
-
-                case Colour::Bright: throw std::logic_error( "not a colour" );
-            }
-        }
-        static IColourImpl* instance() {
-            static PosixColourImpl s_instance;
-            return &s_instance;
-        }
-
-    private:
-        void setColour( const char* _escapeCode ) {
-            Catch::cout() << '\033' << _escapeCode;
-        }
-    };
-
-    IColourImpl* platformColourInstance() {
-        Ptr<IConfig const> config = getCurrentContext().getConfig();
-        return (config && config->forceColour()) || isatty(STDOUT_FILENO)
-            ? PosixColourImpl::instance()
-            : NoColourImpl::instance();
-    }
-
-} // end anon namespace
-} // end namespace Catch
-
-#else  // not Windows or ANSI ///////////////////////////////////////////////
-
-namespace Catch {
-
-    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
-
-} // end namespace Catch
-
-#endif // Windows/ ANSI/ None
-
-namespace Catch {
-
-    Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
-    Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
-    Colour::~Colour(){ if( !m_moved ) use( None ); }
-
-    void Colour::use( Code _colourCode ) {
-        static IColourImpl* impl = isDebuggerActive()
-            ? NoColourImpl::instance()
-            : platformColourInstance();
-        impl->use( _colourCode );
-    }
-
-} // end namespace Catch
-
-// #included from: catch_generators_impl.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
-
-#include <vector>
-#include <string>
-#include <map>
-
-namespace Catch {
-
-    struct GeneratorInfo : IGeneratorInfo {
-
-        GeneratorInfo( std::size_t size )
-        :   m_size( size ),
-            m_currentIndex( 0 )
-        {}
-
-        bool moveNext() {
-            if( ++m_currentIndex == m_size ) {
-                m_currentIndex = 0;
-                return false;
-            }
-            return true;
-        }
-
-        std::size_t getCurrentIndex() const {
-            return m_currentIndex;
-        }
-
-        std::size_t m_size;
-        std::size_t m_currentIndex;
-    };
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    class GeneratorsForTest : public IGeneratorsForTest {
-
-    public:
-        ~GeneratorsForTest() {
-            deleteAll( m_generatorsInOrder );
-        }
-
-        IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
-            std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
-            if( it == m_generatorsByName.end() ) {
-                IGeneratorInfo* info = new GeneratorInfo( size );
-                m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
-                m_generatorsInOrder.push_back( info );
-                return *info;
-            }
-            return *it->second;
-        }
-
-        bool moveNext() {
-            std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
-            std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
-            for(; it != itEnd; ++it ) {
-                if( (*it)->moveNext() )
-                    return true;
-            }
-            return false;
-        }
-
-    private:
-        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
-        std::vector<IGeneratorInfo*> m_generatorsInOrder;
-    };
-
-    IGeneratorsForTest* createGeneratorsForTest()
-    {
-        return new GeneratorsForTest();
-    }
-
-} // end namespace Catch
-
-// #included from: catch_assertionresult.hpp
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
-
-namespace Catch {
-
-    AssertionInfo::AssertionInfo(   std::string const& _macroName,
-                                    SourceLineInfo const& _lineInfo,
-                                    std::string const& _capturedExpression,
-                                    ResultDisposition::Flags _resultDisposition )
-    :   macroName( _macroName ),
-        lineInfo( _lineInfo ),
-        capturedExpression( _capturedExpression ),
-        resultDisposition( _resultDisposition )
-    {}
-
-    AssertionResult::AssertionResult() {}
-
-    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
-    :   m_info( info ),
-        m_resultData( data )
-    {}
-
-    AssertionResult::~AssertionResult() {}
-
-    // Result was a success
-    bool AssertionResult::succeeded() const {
-        return Catch::isOk( m_resultData.resultType );
-    }
-
-    // Result was a success, or failure is suppressed
-    bool AssertionResult::isOk() const {
-        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
-    }
-
-    ResultWas::OfType AssertionResult::getResultType() const {
-        return m_resultData.resultType;
-    }
-
-    bool AssertionResult::hasExpression() const {
-        return !m_info.capturedExpression.empty();
-    }
-
-    bool AssertionResult::hasMessage() const {
-        return !m_resultData.message.empty();
-    }
-
-    std::string AssertionResult::getExpression() const {
-        if( isFalseTest( m_info.resultDisposition ) )
-            return "!" + m_info.capturedExpression;
-        else
-            return m_info.capturedExpression;
-    }
-    std::string AssertionResult::getExpressionInMacro() const {
-        if( m_info.macroName.empty() )
-            return m_info.capturedExpression;
-        else
-            return m_info.macroName + "( " + m_info.capturedExpression + " )";
-    }
-
-    bool AssertionResult::hasExpandedExpression() const {
-        return hasExpression() && getExpandedExpression() != getExpression();
-    }
-
-    std::string AssertionResult::getExpandedExpression() const {
-        return m_resultData.reconstructedExpression;
-    }
-
-    std::string AssertionResult::getMessage() const {
-        return m_resultData.message;
-    }
-    SourceLineInfo AssertionResult::getSourceInfo() const {
-        return m_info.lineInfo;
-    }
-
-    std::string AssertionResult::getTestMacroName() const {
-        return m_info.macroName;
-    }
-
-} // end namespace Catch
-
-// #included from: catch_test_case_info.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
-
-namespace Catch {
-
-    inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
-        if( startsWith( tag, "." ) ||
-            tag == "hide" ||
-            tag == "!hide" )
-            return TestCaseInfo::IsHidden;
-        else if( tag == "!throws" )
-            return TestCaseInfo::Throws;
-        else if( tag == "!shouldfail" )
-            return TestCaseInfo::ShouldFail;
-        else if( tag == "!mayfail" )
-            return TestCaseInfo::MayFail;
-        else
-            return TestCaseInfo::None;
-    }
-    inline bool isReservedTag( std::string const& tag ) {
-        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
-    }
-    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
-        if( isReservedTag( tag ) ) {
-            {
-                Colour colourGuard( Colour::Red );
-                Catch::cerr()
-                    << "Tag name [" << tag << "] not allowed.\n"
-                    << "Tag names starting with non alpha-numeric characters are reserved\n";
-            }
-            {
-                Colour colourGuard( Colour::FileName );
-                Catch::cerr() << _lineInfo << std::endl;
-            }
-            exit(1);
-        }
-    }
-
-    TestCase makeTestCase(  ITestCase* _testCase,
-                            std::string const& _className,
-                            std::string const& _name,
-                            std::string const& _descOrTags,
-                            SourceLineInfo const& _lineInfo )
-    {
-        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
-
-        // Parse out tags
-        std::set<std::string> tags;
-        std::string desc, tag;
-        bool inTag = false;
-        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
-            char c = _descOrTags[i];
-            if( !inTag ) {
-                if( c == '[' )
-                    inTag = true;
-                else
-                    desc += c;
-            }
-            else {
-                if( c == ']' ) {
-                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
-                    if( prop == TestCaseInfo::IsHidden )
-                        isHidden = true;
-                    else if( prop == TestCaseInfo::None )
-                        enforceNotReservedTag( tag, _lineInfo );
-
-                    tags.insert( tag );
-                    tag.clear();
-                    inTag = false;
-                }
-                else
-                    tag += c;
-            }
-        }
-        if( isHidden ) {
-            tags.insert( "hide" );
-            tags.insert( "." );
-        }
-
-        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
-        return TestCase( _testCase, info );
-    }
-
-    TestCaseInfo::TestCaseInfo( std::string const& _name,
-                                std::string const& _className,
-                                std::string const& _description,
-                                std::set<std::string> const& _tags,
-                                SourceLineInfo const& _lineInfo )
-    :   name( _name ),
-        className( _className ),
-        description( _description ),
-        tags( _tags ),
-        lineInfo( _lineInfo ),
-        properties( None )
-    {
-        std::ostringstream oss;
-        for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
-            oss << "[" << *it << "]";
-            std::string lcaseTag = toLower( *it );
-            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
-            lcaseTags.insert( lcaseTag );
-        }
-        tagsAsString = oss.str();
-    }
-
-    TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
-    :   name( other.name ),
-        className( other.className ),
-        description( other.description ),
-        tags( other.tags ),
-        lcaseTags( other.lcaseTags ),
-        tagsAsString( other.tagsAsString ),
-        lineInfo( other.lineInfo ),
-        properties( other.properties )
-    {}
-
-    bool TestCaseInfo::isHidden() const {
-        return ( properties & IsHidden ) != 0;
-    }
-    bool TestCaseInfo::throws() const {
-        return ( properties & Throws ) != 0;
-    }
-    bool TestCaseInfo::okToFail() const {
-        return ( properties & (ShouldFail | MayFail ) ) != 0;
-    }
-    bool TestCaseInfo::expectedToFail() const {
-        return ( properties & (ShouldFail ) ) != 0;
-    }
-
-    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
-
-    TestCase::TestCase( TestCase const& other )
-    :   TestCaseInfo( other ),
-        test( other.test )
-    {}
-
-    TestCase TestCase::withName( std::string const& _newName ) const {
-        TestCase other( *this );
-        other.name = _newName;
-        return other;
-    }
-
-    void TestCase::swap( TestCase& other ) {
-        test.swap( other.test );
-        name.swap( other.name );
-        className.swap( other.className );
-        description.swap( other.description );
-        tags.swap( other.tags );
-        lcaseTags.swap( other.lcaseTags );
-        tagsAsString.swap( other.tagsAsString );
-        std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
-        std::swap( lineInfo, other.lineInfo );
-    }
-
-    void TestCase::invoke() const {
-        test->invoke();
-    }
-
-    bool TestCase::operator == ( TestCase const& other ) const {
-        return  test.get() == other.test.get() &&
-                name == other.name &&
-                className == other.className;
-    }
-
-    bool TestCase::operator < ( TestCase const& other ) const {
-        return name < other.name;
-    }
-    TestCase& TestCase::operator = ( TestCase const& other ) {
-        TestCase temp( other );
-        swap( temp );
-        return *this;
-    }
-
-    TestCaseInfo const& TestCase::getTestCaseInfo() const
-    {
-        return *this;
-    }
-
-} // end namespace Catch
-
-// #included from: catch_version.hpp
-#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
-
-namespace Catch {
-
-    Version::Version
-        (   unsigned int _majorVersion,
-            unsigned int _minorVersion,
-            unsigned int _patchNumber,
-            std::string const& _branchName,
-            unsigned int _buildNumber )
-    :   majorVersion( _majorVersion ),
-        minorVersion( _minorVersion ),
-        patchNumber( _patchNumber ),
-        branchName( _branchName ),
-        buildNumber( _buildNumber )
-    {}
-
-    std::ostream& operator << ( std::ostream& os, Version const& version ) {
-        os  << version.majorVersion << "."
-            << version.minorVersion << "."
-            << version.patchNumber;
-
-        if( !version.branchName.empty() ) {
-            os  << "-" << version.branchName
-                << "." << version.buildNumber;
-        }
-        return os;
-    }
-
-    Version libraryVersion( 1, 2, 1, "", 0 );
-
-}
-
-// #included from: catch_message.hpp
-#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
-
-namespace Catch {
-
-    MessageInfo::MessageInfo(   std::string const& _macroName,
-                                SourceLineInfo const& _lineInfo,
-                                ResultWas::OfType _type )
-    :   macroName( _macroName ),
-        lineInfo( _lineInfo ),
-        type( _type ),
-        sequence( ++globalCount )
-    {}
-
-    // This may need protecting if threading support is added
-    unsigned int MessageInfo::globalCount = 0;
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
-    : m_info( builder.m_info )
-    {
-        m_info.message = builder.m_stream.str();
-        getResultCapture().pushScopedMessage( m_info );
-    }
-    ScopedMessage::ScopedMessage( ScopedMessage const& other )
-    : m_info( other.m_info )
-    {}
-
-    ScopedMessage::~ScopedMessage() {
-        getResultCapture().popScopedMessage( m_info );
-    }
-
-} // end namespace Catch
-
-// #included from: catch_legacy_reporter_adapter.hpp
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
-
-// #included from: catch_legacy_reporter_adapter.h
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
-
-namespace Catch
-{
-    // Deprecated
-    struct IReporter : IShared {
-        virtual ~IReporter();
-
-        virtual bool shouldRedirectStdout() const = 0;
-
-        virtual void StartTesting() = 0;
-        virtual void EndTesting( Totals const& totals ) = 0;
-        virtual void StartGroup( std::string const& groupName ) = 0;
-        virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
-        virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
-        virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
-        virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
-        virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
-        virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
-        virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
-        virtual void Aborted() = 0;
-        virtual void Result( AssertionResult const& result ) = 0;
-    };
-
-    class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
-    {
-    public:
-        LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
-        virtual ~LegacyReporterAdapter();
-
-        virtual ReporterPreferences getPreferences() const;
-        virtual void noMatchingTestCases( std::string const& );
-        virtual void testRunStarting( TestRunInfo const& );
-        virtual void testGroupStarting( GroupInfo const& groupInfo );
-        virtual void testCaseStarting( TestCaseInfo const& testInfo );
-        virtual void sectionStarting( SectionInfo const& sectionInfo );
-        virtual void assertionStarting( AssertionInfo const& );
-        virtual bool assertionEnded( AssertionStats const& assertionStats );
-        virtual void sectionEnded( SectionStats const& sectionStats );
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats );
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats );
-        virtual void testRunEnded( TestRunStats const& testRunStats );
-        virtual void skipTest( TestCaseInfo const& );
-
-    private:
-        Ptr<IReporter> m_legacyReporter;
-    };
-}
-
-namespace Catch
-{
-    LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
-    :   m_legacyReporter( legacyReporter )
-    {}
-    LegacyReporterAdapter::~LegacyReporterAdapter() {}
-
-    ReporterPreferences LegacyReporterAdapter::getPreferences() const {
-        ReporterPreferences prefs;
-        prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
-        return prefs;
-    }
-
-    void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
-    void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
-        m_legacyReporter->StartTesting();
-    }
-    void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
-        m_legacyReporter->StartGroup( groupInfo.name );
-    }
-    void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
-        m_legacyReporter->StartTestCase( testInfo );
-    }
-    void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
-        m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
-    }
-    void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
-        // Not on legacy interface
-    }
-
-    bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
-        if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
-            for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
-                    it != itEnd;
-                    ++it ) {
-                if( it->type == ResultWas::Info ) {
-                    ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
-                    rb << it->message;
-                    rb.setResultType( ResultWas::Info );
-                    AssertionResult result = rb.build();
-                    m_legacyReporter->Result( result );
-                }
-            }
-        }
-        m_legacyReporter->Result( assertionStats.assertionResult );
-        return true;
-    }
-    void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
-        if( sectionStats.missingAssertions )
-            m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
-        m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
-    }
-    void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        m_legacyReporter->EndTestCase
-            (   testCaseStats.testInfo,
-                testCaseStats.totals,
-                testCaseStats.stdOut,
-                testCaseStats.stdErr );
-    }
-    void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        if( testGroupStats.aborting )
-            m_legacyReporter->Aborted();
-        m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
-    }
-    void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
-        m_legacyReporter->EndTesting( testRunStats.totals );
-    }
-    void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
-    }
-}
-
-// #included from: catch_timer.hpp
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++11-long-long"
-#endif
-
-#ifdef CATCH_PLATFORM_WINDOWS
-#include <windows.h>
-#else
-#include <sys/time.h>
-#endif
-
-namespace Catch {
-
-    namespace {
-#ifdef CATCH_PLATFORM_WINDOWS
-        uint64_t getCurrentTicks() {
-            static uint64_t hz=0, hzo=0;
-            if (!hz) {
-                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
-                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
-            }
-            uint64_t t;
-            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
-            return ((t-hzo)*1000000)/hz;
-        }
-#else
-        uint64_t getCurrentTicks() {
-            timeval t;
-            gettimeofday(&t,NULL);
-            return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
-        }
-#endif
-    }
-
-    void Timer::start() {
-        m_ticks = getCurrentTicks();
-    }
-    unsigned int Timer::getElapsedMicroseconds() const {
-        return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
-    }
-    unsigned int Timer::getElapsedMilliseconds() const {
-        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
-    }
-    double Timer::getElapsedSeconds() const {
-        return getElapsedMicroseconds()/1000000.0;
-    }
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-// #included from: catch_common.hpp
-#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
-
-namespace Catch {
-
-    bool startsWith( std::string const& s, std::string const& prefix ) {
-        return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
-    }
-    bool endsWith( std::string const& s, std::string const& suffix ) {
-        return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
-    }
-    bool contains( std::string const& s, std::string const& infix ) {
-        return s.find( infix ) != std::string::npos;
-    }
-    void toLowerInPlace( std::string& s ) {
-        std::transform( s.begin(), s.end(), s.begin(), ::tolower );
-    }
-    std::string toLower( std::string const& s ) {
-        std::string lc = s;
-        toLowerInPlace( lc );
-        return lc;
-    }
-    std::string trim( std::string const& str ) {
-        static char const* whitespaceChars = "\n\r\t ";
-        std::string::size_type start = str.find_first_not_of( whitespaceChars );
-        std::string::size_type end = str.find_last_not_of( whitespaceChars );
-
-        return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
-    }
-
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
-        bool replaced = false;
-        std::size_t i = str.find( replaceThis );
-        while( i != std::string::npos ) {
-            replaced = true;
-            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
-            if( i < str.size()-withThis.size() )
-                i = str.find( replaceThis, i+withThis.size() );
-            else
-                i = std::string::npos;
-        }
-        return replaced;
-    }
-
-    pluralise::pluralise( std::size_t count, std::string const& label )
-    :   m_count( count ),
-        m_label( label )
-    {}
-
-    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
-        os << pluraliser.m_count << " " << pluraliser.m_label;
-        if( pluraliser.m_count != 1 )
-            os << "s";
-        return os;
-    }
-
-    SourceLineInfo::SourceLineInfo() : line( 0 ){}
-    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
-    :   file( _file ),
-        line( _line )
-    {}
-    SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
-    :   file( other.file ),
-        line( other.line )
-    {}
-    bool SourceLineInfo::empty() const {
-        return file.empty();
-    }
-    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
-        return line == other.line && file == other.file;
-    }
-    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
-        return line < other.line || ( line == other.line  && file < other.file );
-    }
-
-    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
-#ifndef __GNUG__
-        os << info.file << "(" << info.line << ")";
-#else
-        os << info.file << ":" << info.line;
-#endif
-        return os;
-    }
-
-    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
-        std::ostringstream oss;
-        oss << locationInfo << ": Internal Catch error: '" << message << "'";
-        if( alwaysTrue() )
-            throw std::logic_error( oss.str() );
-    }
-}
-
-// #included from: catch_section.hpp
-#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
-
-namespace Catch {
-
-    SectionInfo::SectionInfo
-        (   SourceLineInfo const& _lineInfo,
-            std::string const& _name,
-            std::string const& _description )
-    :   name( _name ),
-        description( _description ),
-        lineInfo( _lineInfo )
-    {}
-
-    Section::Section( SectionInfo const& info )
-    :   m_info( info ),
-        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
-    {
-        m_timer.start();
-    }
-
-    Section::~Section() {
-        if( m_sectionIncluded )
-            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
-    }
-
-    // This indicates whether the section should be executed or not
-    Section::operator bool() const {
-        return m_sectionIncluded;
-    }
-
-} // end namespace Catch
-
-// #included from: catch_debugger.hpp
-#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
-
-#include <iostream>
-
-#ifdef CATCH_PLATFORM_MAC
-
-    #include <assert.h>
-    #include <stdbool.h>
-    #include <sys/types.h>
-    #include <unistd.h>
-    #include <sys/sysctl.h>
-
-    namespace Catch{
-
-        // The following function is taken directly from the following technical note:
-        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
-
-        // Returns true if the current process is being debugged (either
-        // running under the debugger or has a debugger attached post facto).
-        bool isDebuggerActive(){
-
-            int                 mib[4];
-            struct kinfo_proc   info;
-            size_t              size;
-
-            // Initialize the flags so that, if sysctl fails for some bizarre
-            // reason, we get a predictable result.
-
-            info.kp_proc.p_flag = 0;
-
-            // Initialize mib, which tells sysctl the info we want, in this case
-            // we're looking for information about a specific process ID.
-
-            mib[0] = CTL_KERN;
-            mib[1] = KERN_PROC;
-            mib[2] = KERN_PROC_PID;
-            mib[3] = getpid();
-
-            // Call sysctl.
-
-            size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
-                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
-                return false;
-            }
-
-            // We're being debugged if the P_TRACED flag is set.
-
-            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
-        }
-    } // namespace Catch
-
-#elif defined(_MSC_VER)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
-        }
-    }
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
-        }
-    }
-#else
-    namespace Catch {
-       inline bool isDebuggerActive() { return false; }
-    }
-#endif // Platform
-
-#ifdef CATCH_PLATFORM_WINDOWS
-    extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            ::OutputDebugStringA( text.c_str() );
-        }
-    }
-#else
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            // !TBD: Need a version for Mac/ XCode and other IDEs
-            Catch::cout() << text;
-        }
-    }
-#endif // Platform
-
-// #included from: catch_tostring.hpp
-#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
-
-namespace Catch {
-
-namespace Detail {
-
-    std::string unprintableString = "{?}";
-
-    namespace {
-        struct Endianness {
-            enum Arch { Big, Little };
-
-            static Arch which() {
-                union _{
-                    int asInt;
-                    char asChar[sizeof (int)];
-                } u;
-
-                u.asInt = 1;
-                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
-            }
-        };
-    }
-
-    std::string rawMemoryToString( const void *object, std::size_t size )
-    {
-        // Reverse order for little endian architectures
-        int i = 0, end = static_cast<int>( size ), inc = 1;
-        if( Endianness::which() == Endianness::Little ) {
-            i = end-1;
-            end = inc = -1;
-        }
-
-        unsigned char const *bytes = static_cast<unsigned char const *>(object);
-        std::ostringstream os;
-        os << "0x" << std::setfill('0') << std::hex;
-        for( ; i != end; i += inc )
-             os << std::setw(2) << static_cast<unsigned>(bytes[i]);
-       return os.str();
-    }
-}
-
-std::string toString( std::string const& value ) {
-    std::string s = value;
-    if( getCurrentContext().getConfig()->showInvisibles() ) {
-        for(size_t i = 0; i < s.size(); ++i ) {
-            std::string subs;
-            switch( s[i] ) {
-            case '\n': subs = "\\n"; break;
-            case '\t': subs = "\\t"; break;
-            default: break;
-            }
-            if( !subs.empty() ) {
-                s = s.substr( 0, i ) + subs + s.substr( i+1 );
-                ++i;
-            }
-        }
-    }
-    return "\"" + s + "\"";
-}
-std::string toString( std::wstring const& value ) {
-
-    std::string s;
-    s.reserve( value.size() );
-    for(size_t i = 0; i < value.size(); ++i )
-        s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
-    return Catch::toString( s );
-}
-
-std::string toString( const char* const value ) {
-    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
-}
-
-std::string toString( char* const value ) {
-    return Catch::toString( static_cast<const char*>( value ) );
-}
-
-std::string toString( const wchar_t* const value )
-{
-       return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
-}
-
-std::string toString( wchar_t* const value )
-{
-       return Catch::toString( static_cast<const wchar_t*>( value ) );
-}
-
-std::string toString( int value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value >= 255 )
-        oss << " (0x" << std::hex << value << ")";
-    return oss.str();
-}
-
-std::string toString( unsigned long value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value >= 255 )
-        oss << " (0x" << std::hex << value << ")";
-    return oss.str();
-}
-
-std::string toString( unsigned int value ) {
-    return Catch::toString( static_cast<unsigned long>( value ) );
-}
-
-template<typename T>
-std::string fpToString( T value, int precision ) {
-    std::ostringstream oss;
-    oss << std::setprecision( precision )
-        << std::fixed
-        << value;
-    std::string d = oss.str();
-    std::size_t i = d.find_last_not_of( '0' );
-    if( i != std::string::npos && i != d.size()-1 ) {
-        if( d[i] == '.' )
-            i++;
-        d = d.substr( 0, i+1 );
-    }
-    return d;
-}
-
-std::string toString( const double value ) {
-    return fpToString( value, 10 );
-}
-std::string toString( const float value ) {
-    return fpToString( value, 5 ) + "f";
-}
-
-std::string toString( bool value ) {
-    return value ? "true" : "false";
-}
-
-std::string toString( char value ) {
-    return value < ' '
-        ? toString( static_cast<unsigned int>( value ) )
-        : Detail::makeString( value );
-}
-
-std::string toString( signed char value ) {
-    return toString( static_cast<char>( value ) );
-}
-
-std::string toString( unsigned char value ) {
-    return toString( static_cast<char>( value ) );
-}
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t ) {
-    return "nullptr";
-}
-#endif
-
-#ifdef __OBJC__
-    std::string toString( NSString const * const& nsstring ) {
-        if( !nsstring )
-            return "nil";
-        return "@" + toString([nsstring UTF8String]);
-    }
-    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
-        if( !nsstring )
-            return "nil";
-        return "@" + toString([nsstring UTF8String]);
-    }
-    std::string toString( NSObject* const& nsObject ) {
-        return toString( [nsObject description] );
-    }
-#endif
-
-} // end namespace Catch
-
-// #included from: catch_result_builder.hpp
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
-
-namespace Catch {
-
-    ResultBuilder::ResultBuilder(   char const* macroName,
-                                    SourceLineInfo const& lineInfo,
-                                    char const* capturedExpression,
-                                    ResultDisposition::Flags resultDisposition )
-    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
-        m_shouldDebugBreak( false ),
-        m_shouldThrow( false )
-    {}
-
-    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
-        m_data.resultType = result;
-        return *this;
-    }
-    ResultBuilder& ResultBuilder::setResultType( bool result ) {
-        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
-        return *this;
-    }
-    ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
-        m_exprComponents.lhs = lhs;
-        return *this;
-    }
-    ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
-        m_exprComponents.rhs = rhs;
-        return *this;
-    }
-    ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
-        m_exprComponents.op = op;
-        return *this;
-    }
-
-    void ResultBuilder::endExpression() {
-        m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
-        captureExpression();
-    }
-
-    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
-        m_assertionInfo.resultDisposition = resultDisposition;
-        m_stream.oss << Catch::translateActiveException();
-        captureResult( ResultWas::ThrewException );
-    }
-
-    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
-        setResultType( resultType );
-        captureExpression();
-    }
-
-    void ResultBuilder::captureExpression() {
-        AssertionResult result = build();
-        getResultCapture().assertionEnded( result );
-
-        if( !result.isOk() ) {
-            if( getCurrentContext().getConfig()->shouldDebugBreak() )
-                m_shouldDebugBreak = true;
-            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
-                m_shouldThrow = true;
-        }
-    }
-    void ResultBuilder::react() {
-        if( m_shouldThrow )
-            throw Catch::TestFailureException();
-    }
-
-    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
-    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
-
-    AssertionResult ResultBuilder::build() const
-    {
-        assert( m_data.resultType != ResultWas::Unknown );
-
-        AssertionResultData data = m_data;
-
-        // Flip bool results if testFalse is set
-        if( m_exprComponents.testFalse ) {
-            if( data.resultType == ResultWas::Ok )
-                data.resultType = ResultWas::ExpressionFailed;
-            else if( data.resultType == ResultWas::ExpressionFailed )
-                data.resultType = ResultWas::Ok;
-        }
-
-        data.message = m_stream.oss.str();
-        data.reconstructedExpression = reconstructExpression();
-        if( m_exprComponents.testFalse ) {
-            if( m_exprComponents.op == "" )
-                data.reconstructedExpression = "!" + data.reconstructedExpression;
-            else
-                data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
-        }
-        return AssertionResult( m_assertionInfo, data );
-    }
-    std::string ResultBuilder::reconstructExpression() const {
-        if( m_exprComponents.op == "" )
-            return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
-        else if( m_exprComponents.op == "matches" )
-            return m_exprComponents.lhs + " " + m_exprComponents.rhs;
-        else if( m_exprComponents.op != "!" ) {
-            if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
-                m_exprComponents.lhs.find("\n") == std::string::npos &&
-                m_exprComponents.rhs.find("\n") == std::string::npos )
-                return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
-            else
-                return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
-        }
-        else
-            return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
-    }
-
-} // end namespace Catch
-
-// #included from: catch_tag_alias_registry.hpp
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
-
-// #included from: catch_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
-
-#include <map>
-
-namespace Catch {
-
-    class TagAliasRegistry : public ITagAliasRegistry {
-    public:
-        virtual ~TagAliasRegistry();
-        virtual Option<TagAlias> find( std::string const& alias ) const;
-        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
-        void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
-        static TagAliasRegistry& get();
-
-    private:
-        std::map<std::string, TagAlias> m_registry;
-    };
-
-} // end namespace Catch
-
-#include <map>
-#include <iostream>
-
-namespace Catch {
-
-    TagAliasRegistry::~TagAliasRegistry() {}
-
-    Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
-        std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
-        if( it != m_registry.end() )
-            return it->second;
-        else
-            return Option<TagAlias>();
-    }
-
-    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
-        std::string expandedTestSpec = unexpandedTestSpec;
-        for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
-                it != itEnd;
-                ++it ) {
-            std::size_t pos = expandedTestSpec.find( it->first );
-            if( pos != std::string::npos ) {
-                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
-                                    it->second.tag +
-                                    expandedTestSpec.substr( pos + it->first.size() );
-            }
-        }
-        return expandedTestSpec;
-    }
-
-    void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
-
-        if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
-            std::ostringstream oss;
-            oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
-            throw std::domain_error( oss.str().c_str() );
-        }
-        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
-            std::ostringstream oss;
-            oss << "error: tag alias, \"" << alias << "\" already registered.\n"
-                << "\tFirst seen at " << find(alias)->lineInfo << "\n"
-                << "\tRedefined at " << lineInfo;
-            throw std::domain_error( oss.str().c_str() );
-        }
-    }
-
-    TagAliasRegistry& TagAliasRegistry::get() {
-        static TagAliasRegistry instance;
-        return instance;
-
-    }
-
-    ITagAliasRegistry::~ITagAliasRegistry() {}
-    ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
-
-    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
-        try {
-            TagAliasRegistry::get().add( alias, tag, lineInfo );
-        }
-        catch( std::exception& ex ) {
-            Colour colourGuard( Colour::Red );
-            Catch::cerr() << ex.what() << std::endl;
-            exit(1);
-        }
-    }
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_xml.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
-
-// #included from: catch_reporter_bases.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
-
-#include <cstring>
-
-namespace Catch {
-
-    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
-
-        StreamingReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {}
-
-        virtual ~StreamingReporterBase();
-
-        virtual void noMatchingTestCases( std::string const& ) {}
-
-        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
-            currentTestRunInfo = _testRunInfo;
-        }
-        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
-            currentGroupInfo = _groupInfo;
-        }
-
-        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
-            currentTestCaseInfo = _testInfo;
-        }
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
-            m_sectionStack.push_back( _sectionInfo );
-        }
-
-        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
-            m_sectionStack.pop_back();
-        }
-        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
-            currentTestCaseInfo.reset();
-        }
-        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
-            currentGroupInfo.reset();
-        }
-        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
-            currentTestCaseInfo.reset();
-            currentGroupInfo.reset();
-            currentTestRunInfo.reset();
-        }
-
-        virtual void skipTest( TestCaseInfo const& ) {
-            // Don't do anything with this by default.
-            // It can optionally be overridden in the derived class.
-        }
-
-        Ptr<IConfig> m_config;
-        std::ostream& stream;
-
-        LazyStat<TestRunInfo> currentTestRunInfo;
-        LazyStat<GroupInfo> currentGroupInfo;
-        LazyStat<TestCaseInfo> currentTestCaseInfo;
-
-        std::vector<SectionInfo> m_sectionStack;
-    };
-
-    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
-        template<typename T, typename ChildNodeT>
-        struct Node : SharedImpl<> {
-            explicit Node( T const& _value ) : value( _value ) {}
-            virtual ~Node() {}
-
-            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
-            T value;
-            ChildNodes children;
-        };
-        struct SectionNode : SharedImpl<> {
-            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
-            virtual ~SectionNode();
-
-            bool operator == ( SectionNode const& other ) const {
-                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
-            }
-            bool operator == ( Ptr<SectionNode> const& other ) const {
-                return operator==( *other );
-            }
-
-            SectionStats stats;
-            typedef std::vector<Ptr<SectionNode> > ChildSections;
-            typedef std::vector<AssertionStats> Assertions;
-            ChildSections childSections;
-            Assertions assertions;
-            std::string stdOut;
-            std::string stdErr;
-        };
-
-        struct BySectionInfo {
-            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
-                       BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
-            bool operator() ( Ptr<SectionNode> const& node ) const {
-                return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
-            }
-        private:
-                       void operator=( BySectionInfo const& );
-            SectionInfo const& m_other;
-        };
-
-        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
-        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
-        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
-
-        CumulativeReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {}
-        ~CumulativeReporterBase();
-
-        virtual void testRunStarting( TestRunInfo const& ) {}
-        virtual void testGroupStarting( GroupInfo const& ) {}
-
-        virtual void testCaseStarting( TestCaseInfo const& ) {}
-
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
-            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
-            Ptr<SectionNode> node;
-            if( m_sectionStack.empty() ) {
-                if( !m_rootSection )
-                    m_rootSection = new SectionNode( incompleteStats );
-                node = m_rootSection;
-            }
-            else {
-                SectionNode& parentNode = *m_sectionStack.back();
-                SectionNode::ChildSections::const_iterator it =
-                    std::find_if(   parentNode.childSections.begin(),
-                                    parentNode.childSections.end(),
-                                    BySectionInfo( sectionInfo ) );
-                if( it == parentNode.childSections.end() ) {
-                    node = new SectionNode( incompleteStats );
-                    parentNode.childSections.push_back( node );
-                }
-                else
-                    node = *it;
-            }
-            m_sectionStack.push_back( node );
-            m_deepestSection = node;
-        }
-
-        virtual void assertionStarting( AssertionInfo const& ) {}
-
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
-            assert( !m_sectionStack.empty() );
-            SectionNode& sectionNode = *m_sectionStack.back();
-            sectionNode.assertions.push_back( assertionStats );
-            return true;
-        }
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
-            assert( !m_sectionStack.empty() );
-            SectionNode& node = *m_sectionStack.back();
-            node.stats = sectionStats;
-            m_sectionStack.pop_back();
-        }
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
-            Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
-            assert( m_sectionStack.size() == 0 );
-            node->children.push_back( m_rootSection );
-            m_testCases.push_back( node );
-            m_rootSection.reset();
-
-            assert( m_deepestSection );
-            m_deepestSection->stdOut = testCaseStats.stdOut;
-            m_deepestSection->stdErr = testCaseStats.stdErr;
-        }
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
-            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
-            node->children.swap( m_testCases );
-            m_testGroups.push_back( node );
-        }
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
-            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
-            node->children.swap( m_testGroups );
-            m_testRuns.push_back( node );
-            testRunEndedCumulative();
-        }
-        virtual void testRunEndedCumulative() = 0;
-
-        virtual void skipTest( TestCaseInfo const& ) {}
-
-        Ptr<IConfig> m_config;
-        std::ostream& stream;
-        std::vector<AssertionStats> m_assertions;
-        std::vector<std::vector<Ptr<SectionNode> > > m_sections;
-        std::vector<Ptr<TestCaseNode> > m_testCases;
-        std::vector<Ptr<TestGroupNode> > m_testGroups;
-
-        std::vector<Ptr<TestRunNode> > m_testRuns;
-
-        Ptr<SectionNode> m_rootSection;
-        Ptr<SectionNode> m_deepestSection;
-        std::vector<Ptr<SectionNode> > m_sectionStack;
-
-    };
-
-    template<char C>
-    char const* getLineOfChars() {
-        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
-        if( !*line ) {
-            memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
-            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
-        }
-        return line;
-    }
-
-} // end namespace Catch
-
-// #included from: ../internal/catch_reporter_registrars.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-
-namespace Catch {
-
-    template<typename T>
-    class LegacyReporterRegistrar {
-
-        class ReporterFactory : public IReporterFactory {
-            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
-                return new LegacyReporterAdapter( new T( config ) );
-            }
-
-            virtual std::string getDescription() const {
-                return T::getDescription();
-            }
-        };
-
-    public:
-
-        LegacyReporterRegistrar( std::string const& name ) {
-            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
-        }
-    };
-
-    template<typename T>
-    class ReporterRegistrar {
-
-        class ReporterFactory : public IReporterFactory {
-
-            // *** Please Note ***:
-            // - If you end up here looking at a compiler error because it's trying to register
-            // your custom reporter class be aware that the native reporter interface has changed
-            // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
-            // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
-            // However please consider updating to the new interface as the old one is now
-            // deprecated and will probably be removed quite soon!
-            // Please contact me via github if you have any questions at all about this.
-            // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
-            // no idea who is actually using custom reporters at all (possibly no-one!).
-            // The new interface is designed to minimise exposure to interface changes in the future.
-            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
-                return new T( config );
-            }
-
-            virtual std::string getDescription() const {
-                return T::getDescription();
-            }
-        };
-
-    public:
-
-        ReporterRegistrar( std::string const& name ) {
-            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
-        }
-    };
-}
-
-#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
-    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
-#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
-    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
-
-// #included from: ../internal/catch_xmlwriter.hpp
-#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace Catch {
-
-    class XmlWriter {
-    public:
-
-        class ScopedElement {
-        public:
-            ScopedElement( XmlWriter* writer )
-            :   m_writer( writer )
-            {}
-
-            ScopedElement( ScopedElement const& other )
-            :   m_writer( other.m_writer ){
-                other.m_writer = NULL;
-            }
-
-            ~ScopedElement() {
-                if( m_writer )
-                    m_writer->endElement();
-            }
-
-            ScopedElement& writeText( std::string const& text, bool indent = true ) {
-                m_writer->writeText( text, indent );
-                return *this;
-            }
-
-            template<typename T>
-            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
-                m_writer->writeAttribute( name, attribute );
-                return *this;
-            }
-
-        private:
-            mutable XmlWriter* m_writer;
-        };
-
-        XmlWriter()
-        :   m_tagIsOpen( false ),
-            m_needsNewline( false ),
-            m_os( &Catch::cout() )
-        {}
-
-        XmlWriter( std::ostream& os )
-        :   m_tagIsOpen( false ),
-            m_needsNewline( false ),
-            m_os( &os )
-        {}
-
-        ~XmlWriter() {
-            while( !m_tags.empty() )
-                endElement();
-        }
-
-        XmlWriter& startElement( std::string const& name ) {
-            ensureTagClosed();
-            newlineIfNecessary();
-            stream() << m_indent << "<" << name;
-            m_tags.push_back( name );
-            m_indent += "  ";
-            m_tagIsOpen = true;
-            return *this;
-        }
-
-        ScopedElement scopedElement( std::string const& name ) {
-            ScopedElement scoped( this );
-            startElement( name );
-            return scoped;
-        }
-
-        XmlWriter& endElement() {
-            newlineIfNecessary();
-            m_indent = m_indent.substr( 0, m_indent.size()-2 );
-            if( m_tagIsOpen ) {
-                stream() << "/>\n";
-                m_tagIsOpen = false;
-            }
-            else {
-                stream() << m_indent << "</" << m_tags.back() << ">\n";
-            }
-            m_tags.pop_back();
-            return *this;
-        }
-
-        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
-            if( !name.empty() && !attribute.empty() ) {
-                stream() << " " << name << "=\"";
-                writeEncodedText( attribute );
-                stream() << "\"";
-            }
-            return *this;
-        }
-
-        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
-            stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
-            return *this;
-        }
-
-        template<typename T>
-        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            if( !name.empty() )
-                stream() << " " << name << "=\"" << attribute << "\"";
-            return *this;
-        }
-
-        XmlWriter& writeText( std::string const& text, bool indent = true ) {
-            if( !text.empty() ){
-                bool tagWasOpen = m_tagIsOpen;
-                ensureTagClosed();
-                if( tagWasOpen && indent )
-                    stream() << m_indent;
-                writeEncodedText( text );
-                m_needsNewline = true;
-            }
-            return *this;
-        }
-
-        XmlWriter& writeComment( std::string const& text ) {
-            ensureTagClosed();
-            stream() << m_indent << "<!--" << text << "-->";
-            m_needsNewline = true;
-            return *this;
-        }
-
-        XmlWriter& writeBlankLine() {
-            ensureTagClosed();
-            stream() << "\n";
-            return *this;
-        }
-
-        void setStream( std::ostream& os ) {
-            m_os = &os;
-        }
-
-    private:
-        XmlWriter( XmlWriter const& );
-        void operator=( XmlWriter const& );
-
-        std::ostream& stream() {
-            return *m_os;
-        }
-
-        void ensureTagClosed() {
-            if( m_tagIsOpen ) {
-                stream() << ">\n";
-                m_tagIsOpen = false;
-            }
-        }
-
-        void newlineIfNecessary() {
-            if( m_needsNewline ) {
-                stream() << "\n";
-                m_needsNewline = false;
-            }
-        }
-
-        void writeEncodedText( std::string const& text ) {
-            static const char* charsToEncode = "<&\"";
-            std::string mtext = text;
-            std::string::size_type pos = mtext.find_first_of( charsToEncode );
-            while( pos != std::string::npos ) {
-                stream() << mtext.substr( 0, pos );
-
-                switch( mtext[pos] ) {
-                    case '<':
-                        stream() << "&lt;";
-                        break;
-                    case '&':
-                        stream() << "&amp;";
-                        break;
-                    case '\"':
-                        stream() << "&quot;";
-                        break;
-                }
-                mtext = mtext.substr( pos+1 );
-                pos = mtext.find_first_of( charsToEncode );
-            }
-            stream() << mtext;
-        }
-
-        bool m_tagIsOpen;
-        bool m_needsNewline;
-        std::vector<std::string> m_tags;
-        std::string m_indent;
-        std::ostream* m_os;
-    };
-
-}
-namespace Catch {
-    class XmlReporter : public StreamingReporterBase {
-    public:
-        XmlReporter( ReporterConfig const& _config )
-        :   StreamingReporterBase( _config ),
-            m_sectionDepth( 0 )
-        {}
-
-        virtual ~XmlReporter();
-
-        static std::string getDescription() {
-            return "Reports test results as an XML document";
-        }
-
-    public: // StreamingReporterBase
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
-
-        virtual void noMatchingTestCases( std::string const& s ) {
-            StreamingReporterBase::noMatchingTestCases( s );
-        }
-
-        virtual void testRunStarting( TestRunInfo const& testInfo ) {
-            StreamingReporterBase::testRunStarting( testInfo );
-            m_xml.setStream( stream );
-            m_xml.startElement( "Catch" );
-            if( !m_config->name().empty() )
-                m_xml.writeAttribute( "name", m_config->name() );
-        }
-
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
-            StreamingReporterBase::testGroupStarting( groupInfo );
-            m_xml.startElement( "Group" )
-                .writeAttribute( "name", groupInfo.name );
-        }
-
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
-            StreamingReporterBase::testCaseStarting(testInfo);
-            m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
-
-            if ( m_config->showDurations() == ShowDurations::Always )
-                m_testCaseTimer.start();
-        }
-
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
-            StreamingReporterBase::sectionStarting( sectionInfo );
-            if( m_sectionDepth++ > 0 ) {
-                m_xml.startElement( "Section" )
-                    .writeAttribute( "name", trim( sectionInfo.name ) )
-                    .writeAttribute( "description", sectionInfo.description );
-            }
-        }
-
-        virtual void assertionStarting( AssertionInfo const& ) { }
-
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
-            const AssertionResult& assertionResult = assertionStats.assertionResult;
-
-            // Print any info messages in <Info> tags.
-            if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
-                for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
-                        it != itEnd;
-                        ++it ) {
-                    if( it->type == ResultWas::Info ) {
-                        m_xml.scopedElement( "Info" )
-                            .writeText( it->message );
-                    } else if ( it->type == ResultWas::Warning ) {
-                        m_xml.scopedElement( "Warning" )
-                            .writeText( it->message );
-                    }
-                }
-            }
-
-            // Drop out if result was successful but we're not printing them.
-            if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
-                return true;
-
-            // Print the expression if there is one.
-            if( assertionResult.hasExpression() ) {
-                m_xml.startElement( "Expression" )
-                    .writeAttribute( "success", assertionResult.succeeded() )
-                                       .writeAttribute( "type", assertionResult.getTestMacroName() )
-                    .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                    .writeAttribute( "line", assertionResult.getSourceInfo().line );
-
-                m_xml.scopedElement( "Original" )
-                    .writeText( assertionResult.getExpression() );
-                m_xml.scopedElement( "Expanded" )
-                    .writeText( assertionResult.getExpandedExpression() );
-            }
-
-            // And... Print a result applicable to each result type.
-            switch( assertionResult.getResultType() ) {
-                case ResultWas::ThrewException:
-                    m_xml.scopedElement( "Exception" )
-                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
-                        .writeText( assertionResult.getMessage() );
-                    break;
-                case ResultWas::FatalErrorCondition:
-                    m_xml.scopedElement( "Fatal Error Condition" )
-                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
-                        .writeText( assertionResult.getMessage() );
-                    break;
-                case ResultWas::Info:
-                    m_xml.scopedElement( "Info" )
-                        .writeText( assertionResult.getMessage() );
-                    break;
-                case ResultWas::Warning:
-                    // Warning will already have been written
-                    break;
-                case ResultWas::ExplicitFailure:
-                    m_xml.scopedElement( "Failure" )
-                        .writeText( assertionResult.getMessage() );
-                    break;
-                default:
-                    break;
-            }
-
-            if( assertionResult.hasExpression() )
-                m_xml.endElement();
-
-            return true;
-        }
-
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
-            StreamingReporterBase::sectionEnded( sectionStats );
-            if( --m_sectionDepth > 0 ) {
-                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
-                e.writeAttribute( "successes", sectionStats.assertions.passed );
-                e.writeAttribute( "failures", sectionStats.assertions.failed );
-                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
-
-                if ( m_config->showDurations() == ShowDurations::Always )
-                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
-
-                m_xml.endElement();
-            }
-        }
-
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
-            StreamingReporterBase::testCaseEnded( testCaseStats );
-            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
-            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
-
-            if ( m_config->showDurations() == ShowDurations::Always )
-                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
-
-            m_xml.endElement();
-        }
-
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
-            StreamingReporterBase::testGroupEnded( testGroupStats );
-            // TODO: Check testGroupStats.aborting and act accordingly.
-            m_xml.scopedElement( "OverallResults" )
-                .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
-                .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
-                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
-            m_xml.endElement();
-        }
-
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
-            StreamingReporterBase::testRunEnded( testRunStats );
-            m_xml.scopedElement( "OverallResults" )
-                .writeAttribute( "successes", testRunStats.totals.assertions.passed )
-                .writeAttribute( "failures", testRunStats.totals.assertions.failed )
-                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
-            m_xml.endElement();
-        }
-
-    private:
-        Timer m_testCaseTimer;
-        XmlWriter m_xml;
-        int m_sectionDepth;
-    };
-
-     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_junit.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
-
-#include <assert.h>
-
-namespace Catch {
-
-    class JunitReporter : public CumulativeReporterBase {
-    public:
-        JunitReporter( ReporterConfig const& _config )
-        :   CumulativeReporterBase( _config ),
-            xml( _config.stream() )
-        {}
-
-        ~JunitReporter();
-
-        static std::string getDescription() {
-            return "Reports test results in an XML format that looks like Ant's junitreport target";
-        }
-
-        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
-
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
-
-        virtual void testRunStarting( TestRunInfo const& runInfo ) {
-            CumulativeReporterBase::testRunStarting( runInfo );
-            xml.startElement( "testsuites" );
-        }
-
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
-            suiteTimer.start();
-            stdOutForSuite.str("");
-            stdErrForSuite.str("");
-            unexpectedExceptions = 0;
-            CumulativeReporterBase::testGroupStarting( groupInfo );
-        }
-
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
-            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
-                unexpectedExceptions++;
-            return CumulativeReporterBase::assertionEnded( assertionStats );
-        }
-
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
-            stdOutForSuite << testCaseStats.stdOut;
-            stdErrForSuite << testCaseStats.stdErr;
-            CumulativeReporterBase::testCaseEnded( testCaseStats );
-        }
-
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
-            double suiteTime = suiteTimer.getElapsedSeconds();
-            CumulativeReporterBase::testGroupEnded( testGroupStats );
-            writeGroup( *m_testGroups.back(), suiteTime );
-        }
-
-        virtual void testRunEndedCumulative() {
-            xml.endElement();
-        }
-
-        void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
-            XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
-            TestGroupStats const& stats = groupNode.value;
-            xml.writeAttribute( "name", stats.groupInfo.name );
-            xml.writeAttribute( "errors", unexpectedExceptions );
-            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
-            xml.writeAttribute( "tests", stats.totals.assertions.total() );
-            xml.writeAttribute( "hostname", "tbd" ); // !TBD
-            if( m_config->showDurations() == ShowDurations::Never )
-                xml.writeAttribute( "time", "" );
-            else
-                xml.writeAttribute( "time", suiteTime );
-            xml.writeAttribute( "timestamp", "tbd" ); // !TBD
-
-            // Write test cases
-            for( TestGroupNode::ChildNodes::const_iterator
-                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
-                    it != itEnd;
-                    ++it )
-                writeTestCase( **it );
-
-            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
-            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
-        }
-
-        void writeTestCase( TestCaseNode const& testCaseNode ) {
-            TestCaseStats const& stats = testCaseNode.value;
-
-            // All test cases have exactly one section - which represents the
-            // test case itself. That section may have 0-n nested sections
-            assert( testCaseNode.children.size() == 1 );
-            SectionNode const& rootSection = *testCaseNode.children.front();
-
-            std::string className = stats.testInfo.className;
-
-            if( className.empty() ) {
-                if( rootSection.childSections.empty() )
-                    className = "global";
-            }
-            writeSection( className, "", rootSection );
-        }
-
-        void writeSection(  std::string const& className,
-                            std::string const& rootName,
-                            SectionNode const& sectionNode ) {
-            std::string name = trim( sectionNode.stats.sectionInfo.name );
-            if( !rootName.empty() )
-                name = rootName + "/" + name;
-
-            if( !sectionNode.assertions.empty() ||
-                !sectionNode.stdOut.empty() ||
-                !sectionNode.stdErr.empty() ) {
-                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
-                if( className.empty() ) {
-                    xml.writeAttribute( "classname", name );
-                    xml.writeAttribute( "name", "root" );
-                }
-                else {
-                    xml.writeAttribute( "classname", className );
-                    xml.writeAttribute( "name", name );
-                }
-                xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
-
-                writeAssertions( sectionNode );
-
-                if( !sectionNode.stdOut.empty() )
-                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
-                if( !sectionNode.stdErr.empty() )
-                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
-            }
-            for( SectionNode::ChildSections::const_iterator
-                    it = sectionNode.childSections.begin(),
-                    itEnd = sectionNode.childSections.end();
-                    it != itEnd;
-                    ++it )
-                if( className.empty() )
-                    writeSection( name, "", **it );
-                else
-                    writeSection( className, name, **it );
-        }
-
-        void writeAssertions( SectionNode const& sectionNode ) {
-            for( SectionNode::Assertions::const_iterator
-                    it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
-                    it != itEnd;
-                    ++it )
-                writeAssertion( *it );
-        }
-        void writeAssertion( AssertionStats const& stats ) {
-            AssertionResult const& result = stats.assertionResult;
-            if( !result.isOk() ) {
-                std::string elementName;
-                switch( result.getResultType() ) {
-                    case ResultWas::ThrewException:
-                    case ResultWas::FatalErrorCondition:
-                        elementName = "error";
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        elementName = "failure";
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        elementName = "failure";
-                        break;
-                    case ResultWas::DidntThrowException:
-                        elementName = "failure";
-                        break;
-
-                    // We should never see these here:
-                    case ResultWas::Info:
-                    case ResultWas::Warning:
-                    case ResultWas::Ok:
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        elementName = "internalError";
-                        break;
-                }
-
-                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
-
-                xml.writeAttribute( "message", result.getExpandedExpression() );
-                xml.writeAttribute( "type", result.getTestMacroName() );
-
-                std::ostringstream oss;
-                if( !result.getMessage().empty() )
-                    oss << result.getMessage() << "\n";
-                for( std::vector<MessageInfo>::const_iterator
-                        it = stats.infoMessages.begin(),
-                        itEnd = stats.infoMessages.end();
-                            it != itEnd;
-                            ++it )
-                    if( it->type == ResultWas::Info )
-                        oss << it->message << "\n";
-
-                oss << "at " << result.getSourceInfo();
-                xml.writeText( oss.str(), false );
-            }
-        }
-
-        XmlWriter xml;
-        Timer suiteTimer;
-        std::ostringstream stdOutForSuite;
-        std::ostringstream stdErrForSuite;
-        unsigned int unexpectedExceptions;
-    };
-
-    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_console.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
-
-namespace Catch {
-
-    struct ConsoleReporter : StreamingReporterBase {
-        ConsoleReporter( ReporterConfig const& _config )
-        :   StreamingReporterBase( _config ),
-            m_headerPrinted( false )
-        {}
-
-        virtual ~ConsoleReporter();
-        static std::string getDescription() {
-            return "Reports test results as plain lines of text";
-        }
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
-
-        virtual void noMatchingTestCases( std::string const& spec ) {
-            stream << "No test cases matched '" << spec << "'" << std::endl;
-        }
-
-        virtual void assertionStarting( AssertionInfo const& ) {
-        }
-
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
-            AssertionResult const& result = _assertionStats.assertionResult;
-
-            bool printInfoMessages = true;
-
-            // Drop out if result was successful and we're not printing those
-            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
-                if( result.getResultType() != ResultWas::Warning )
-                    return false;
-                printInfoMessages = false;
-            }
-
-            lazyPrint();
-
-            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
-            printer.print();
-            stream << std::endl;
-            return true;
-        }
-
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
-            m_headerPrinted = false;
-            StreamingReporterBase::sectionStarting( _sectionInfo );
-        }
-        virtual void sectionEnded( SectionStats const& _sectionStats ) {
-            if( _sectionStats.missingAssertions ) {
-                lazyPrint();
-                Colour colour( Colour::ResultError );
-                if( m_sectionStack.size() > 1 )
-                    stream << "\nNo assertions in section";
-                else
-                    stream << "\nNo assertions in test case";
-                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
-            }
-            if( m_headerPrinted ) {
-                if( m_config->showDurations() == ShowDurations::Always )
-                    stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
-                m_headerPrinted = false;
-            }
-            else {
-                if( m_config->showDurations() == ShowDurations::Always )
-                    stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
-            }
-            StreamingReporterBase::sectionEnded( _sectionStats );
-        }
-
-        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
-            StreamingReporterBase::testCaseEnded( _testCaseStats );
-            m_headerPrinted = false;
-        }
-        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
-            if( currentGroupInfo.used ) {
-                printSummaryDivider();
-                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
-                printTotals( _testGroupStats.totals );
-                stream << "\n" << std::endl;
-            }
-            StreamingReporterBase::testGroupEnded( _testGroupStats );
-        }
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
-            printTotalsDivider( _testRunStats.totals );
-            printTotals( _testRunStats.totals );
-            stream << std::endl;
-            StreamingReporterBase::testRunEnded( _testRunStats );
-        }
-
-    private:
-
-        class AssertionPrinter {
-            void operator= ( AssertionPrinter const& );
-        public:
-            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
-            :   stream( _stream ),
-                stats( _stats ),
-                result( _stats.assertionResult ),
-                colour( Colour::None ),
-                message( result.getMessage() ),
-                messages( _stats.infoMessages ),
-                printInfoMessages( _printInfoMessages )
-            {
-                switch( result.getResultType() ) {
-                    case ResultWas::Ok:
-                        colour = Colour::Success;
-                        passOrFail = "PASSED";
-                        //if( result.hasMessage() )
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "with messages";
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        if( result.isOk() ) {
-                            colour = Colour::Success;
-                            passOrFail = "FAILED - but was ok";
-                        }
-                        else {
-                            colour = Colour::Error;
-                            passOrFail = "FAILED";
-                        }
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "with messages";
-                        break;
-                    case ResultWas::ThrewException:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "due to unexpected exception with message";
-                        break;
-                    case ResultWas::FatalErrorCondition:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "due to a fatal error condition";
-                        break;
-                    case ResultWas::DidntThrowException:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "because no exception was thrown where one was expected";
-                        break;
-                    case ResultWas::Info:
-                        messageLabel = "info";
-                        break;
-                    case ResultWas::Warning:
-                        messageLabel = "warning";
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        passOrFail = "FAILED";
-                        colour = Colour::Error;
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "explicitly with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "explicitly with messages";
-                        break;
-                    // These cases are here to prevent compiler warnings
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        passOrFail = "** internal error **";
-                        colour = Colour::Error;
-                        break;
-                }
-            }
-
-            void print() const {
-                printSourceInfo();
-                if( stats.totals.assertions.total() > 0 ) {
-                    if( result.isOk() )
-                        stream << "\n";
-                    printResultType();
-                    printOriginalExpression();
-                    printReconstructedExpression();
-                }
-                else {
-                    stream << "\n";
-                }
-                printMessage();
-            }
-
-        private:
-            void printResultType() const {
-                if( !passOrFail.empty() ) {
-                    Colour colourGuard( colour );
-                    stream << passOrFail << ":\n";
-                }
-            }
-            void printOriginalExpression() const {
-                if( result.hasExpression() ) {
-                    Colour colourGuard( Colour::OriginalExpression );
-                    stream  << "  ";
-                    stream << result.getExpressionInMacro();
-                    stream << "\n";
-                }
-            }
-            void printReconstructedExpression() const {
-                if( result.hasExpandedExpression() ) {
-                    stream << "with expansion:\n";
-                    Colour colourGuard( Colour::ReconstructedExpression );
-                    stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
-                }
-            }
-            void printMessage() const {
-                if( !messageLabel.empty() )
-                    stream << messageLabel << ":" << "\n";
-                for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
-                        it != itEnd;
-                        ++it ) {
-                    // If this assertion is a warning ignore any INFO messages
-                    if( printInfoMessages || it->type != ResultWas::Info )
-                        stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
-                }
-            }
-            void printSourceInfo() const {
-                Colour colourGuard( Colour::FileName );
-                stream << result.getSourceInfo() << ": ";
-            }
-
-            std::ostream& stream;
-            AssertionStats const& stats;
-            AssertionResult const& result;
-            Colour::Code colour;
-            std::string passOrFail;
-            std::string messageLabel;
-            std::string message;
-            std::vector<MessageInfo> messages;
-            bool printInfoMessages;
-        };
-
-        void lazyPrint() {
-
-            if( !currentTestRunInfo.used )
-                lazyPrintRunInfo();
-            if( !currentGroupInfo.used )
-                lazyPrintGroupInfo();
-
-            if( !m_headerPrinted ) {
-                printTestCaseAndSectionHeader();
-                m_headerPrinted = true;
-            }
-        }
-        void lazyPrintRunInfo() {
-            stream  << "\n" << getLineOfChars<'~'>() << "\n";
-            Colour colour( Colour::SecondaryText );
-            stream  << currentTestRunInfo->name
-                    << " is a Catch v"  << libraryVersion << " host application.\n"
-                    << "Run with -? for options\n\n";
-
-            if( m_config->rngSeed() != 0 )
-                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
-
-            currentTestRunInfo.used = true;
-        }
-        void lazyPrintGroupInfo() {
-            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
-                printClosedHeader( "Group: " + currentGroupInfo->name );
-                currentGroupInfo.used = true;
-            }
-        }
-        void printTestCaseAndSectionHeader() {
-            assert( !m_sectionStack.empty() );
-            printOpenHeader( currentTestCaseInfo->name );
-
-            if( m_sectionStack.size() > 1 ) {
-                Colour colourGuard( Colour::Headers );
-
-                std::vector<SectionInfo>::const_iterator
-                    it = m_sectionStack.begin()+1, // Skip first section (test case)
-                    itEnd = m_sectionStack.end();
-                for( ; it != itEnd; ++it )
-                    printHeaderString( it->name, 2 );
-            }
-
-            SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
-
-            if( !lineInfo.empty() ){
-                stream << getLineOfChars<'-'>() << "\n";
-                Colour colourGuard( Colour::FileName );
-                stream << lineInfo << "\n";
-            }
-            stream << getLineOfChars<'.'>() << "\n" << std::endl;
-        }
-
-        void printClosedHeader( std::string const& _name ) {
-            printOpenHeader( _name );
-            stream << getLineOfChars<'.'>() << "\n";
-        }
-        void printOpenHeader( std::string const& _name ) {
-            stream  << getLineOfChars<'-'>() << "\n";
-            {
-                Colour colourGuard( Colour::Headers );
-                printHeaderString( _name );
-            }
-        }
-
-        // if string has a : in first line will set indent to follow it on
-        // subsequent lines
-        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
-            std::size_t i = _string.find( ": " );
-            if( i != std::string::npos )
-                i+=2;
-            else
-                i = 0;
-            stream << Text( _string, TextAttributes()
-                                        .setIndent( indent+i)
-                                        .setInitialIndent( indent ) ) << "\n";
-        }
-
-        struct SummaryColumn {
-
-            SummaryColumn( std::string const& _label, Colour::Code _colour )
-            :   label( _label ),
-                colour( _colour )
-            {}
-            SummaryColumn addRow( std::size_t count ) {
-                std::ostringstream oss;
-                oss << count;
-                std::string row = oss.str();
-                for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
-                    while( it->size() < row.size() )
-                        *it = " " + *it;
-                    while( it->size() > row.size() )
-                        row = " " + row;
-                }
-                rows.push_back( row );
-                return *this;
-            }
-
-            std::string label;
-            Colour::Code colour;
-            std::vector<std::string> rows;
-
-        };
-
-        void printTotals( Totals const& totals ) {
-            if( totals.testCases.total() == 0 ) {
-                stream << Colour( Colour::Warning ) << "No tests ran\n";
-            }
-            else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
-                stream << Colour( Colour::ResultSuccess ) << "All tests passed";
-                stream << " ("
-                        << pluralise( totals.assertions.passed, "assertion" ) << " in "
-                        << pluralise( totals.testCases.passed, "test case" ) << ")"
-                        << "\n";
-            }
-            else {
-
-                std::vector<SummaryColumn> columns;
-                columns.push_back( SummaryColumn( "", Colour::None )
-                                        .addRow( totals.testCases.total() )
-                                        .addRow( totals.assertions.total() ) );
-                columns.push_back( SummaryColumn( "passed", Colour::Success )
-                                        .addRow( totals.testCases.passed )
-                                        .addRow( totals.assertions.passed ) );
-                columns.push_back( SummaryColumn( "failed", Colour::ResultError )
-                                        .addRow( totals.testCases.failed )
-                                        .addRow( totals.assertions.failed ) );
-                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
-                                        .addRow( totals.testCases.failedButOk )
-                                        .addRow( totals.assertions.failedButOk ) );
-
-                printSummaryRow( "test cases", columns, 0 );
-                printSummaryRow( "assertions", columns, 1 );
-            }
-        }
-        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
-            for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
-                std::string value = it->rows[row];
-                if( it->label.empty() ) {
-                    stream << label << ": ";
-                    if( value != "0" )
-                        stream << value;
-                    else
-                        stream << Colour( Colour::Warning ) << "- none -";
-                }
-                else if( value != "0" ) {
-                    stream  << Colour( Colour::LightGrey ) << " | ";
-                    stream  << Colour( it->colour )
-                            << value << " " << it->label;
-                }
-            }
-            stream << "\n";
-        }
-
-        static std::size_t makeRatio( std::size_t number, std::size_t total ) {
-            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
-            return ( ratio == 0 && number > 0 ) ? 1 : ratio;
-        }
-        static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
-            if( i > j && i > k )
-                return i;
-            else if( j > k )
-                return j;
-            else
-                return k;
-        }
-
-        void printTotalsDivider( Totals const& totals ) {
-            if( totals.testCases.total() > 0 ) {
-                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
-                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
-                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
-                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
-                    findMax( failedRatio, failedButOkRatio, passedRatio )++;
-                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
-                    findMax( failedRatio, failedButOkRatio, passedRatio )--;
-
-                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
-                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
-                if( totals.testCases.allPassed() )
-                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
-                else
-                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
-            }
-            else {
-                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
-            }
-            stream << "\n";
-        }
-        void printSummaryDivider() {
-            stream << getLineOfChars<'-'>() << "\n";
-        }
-
-    private:
-        bool m_headerPrinted;
-    };
-
-    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_compact.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
-
-namespace Catch {
-
-    struct CompactReporter : StreamingReporterBase {
-
-        CompactReporter( ReporterConfig const& _config )
-        : StreamingReporterBase( _config )
-        {}
-
-        virtual ~CompactReporter();
-
-        static std::string getDescription() {
-            return "Reports test results on a single line, suitable for IDEs";
-        }
-
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
-
-        virtual void noMatchingTestCases( std::string const& spec ) {
-            stream << "No test cases matched '" << spec << "'" << std::endl;
-        }
-
-        virtual void assertionStarting( AssertionInfo const& ) {
-        }
-
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
-            AssertionResult const& result = _assertionStats.assertionResult;
-
-            bool printInfoMessages = true;
-
-            // Drop out if result was successful and we're not printing those
-            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
-                if( result.getResultType() != ResultWas::Warning )
-                    return false;
-                printInfoMessages = false;
-            }
-
-            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
-            printer.print();
-
-            stream << std::endl;
-            return true;
-        }
-
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
-            printTotals( _testRunStats.totals );
-            stream << "\n" << std::endl;
-            StreamingReporterBase::testRunEnded( _testRunStats );
-        }
-
-    private:
-        class AssertionPrinter {
-            void operator= ( AssertionPrinter const& );
-        public:
-            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
-            : stream( _stream )
-            , stats( _stats )
-            , result( _stats.assertionResult )
-            , messages( _stats.infoMessages )
-            , itMessage( _stats.infoMessages.begin() )
-            , printInfoMessages( _printInfoMessages )
-            {}
-
-            void print() {
-                printSourceInfo();
-
-                itMessage = messages.begin();
-
-                switch( result.getResultType() ) {
-                    case ResultWas::Ok:
-                        printResultType( Colour::ResultSuccess, passedString() );
-                        printOriginalExpression();
-                        printReconstructedExpression();
-                        if ( ! result.hasExpression() )
-                            printRemainingMessages( Colour::None );
-                        else
-                            printRemainingMessages();
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        if( result.isOk() )
-                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
-                        else
-                            printResultType( Colour::Error, failedString() );
-                        printOriginalExpression();
-                        printReconstructedExpression();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::ThrewException:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "unexpected exception with message:" );
-                        printMessage();
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::FatalErrorCondition:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "fatal error condition with message:" );
-                        printMessage();
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::DidntThrowException:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "expected exception, got none" );
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::Info:
-                        printResultType( Colour::None, "info" );
-                        printMessage();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::Warning:
-                        printResultType( Colour::None, "warning" );
-                        printMessage();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "explicitly" );
-                        printRemainingMessages( Colour::None );
-                        break;
-                    // These cases are here to prevent compiler warnings
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        printResultType( Colour::Error, "** internal error **" );
-                        break;
-                }
-            }
-
-        private:
-            // Colour::LightGrey
-
-            static Colour::Code dimColour() { return Colour::FileName; }
-
-#ifdef CATCH_PLATFORM_MAC
-            static const char* failedString() { return "FAILED"; }
-            static const char* passedString() { return "PASSED"; }
-#else
-            static const char* failedString() { return "failed"; }
-            static const char* passedString() { return "passed"; }
-#endif
-
-            void printSourceInfo() const {
-                Colour colourGuard( Colour::FileName );
-                stream << result.getSourceInfo() << ":";
-            }
-
-            void printResultType( Colour::Code colour, std::string passOrFail ) const {
-                if( !passOrFail.empty() ) {
-                    {
-                        Colour colourGuard( colour );
-                        stream << " " << passOrFail;
-                    }
-                    stream << ":";
-                }
-            }
-
-            void printIssue( std::string issue ) const {
-                stream << " " << issue;
-            }
-
-            void printExpressionWas() {
-                if( result.hasExpression() ) {
-                    stream << ";";
-                    {
-                        Colour colour( dimColour() );
-                        stream << " expression was:";
-                    }
-                    printOriginalExpression();
-                }
-            }
-
-            void printOriginalExpression() const {
-                if( result.hasExpression() ) {
-                    stream << " " << result.getExpression();
-                }
-            }
-
-            void printReconstructedExpression() const {
-                if( result.hasExpandedExpression() ) {
-                    {
-                        Colour colour( dimColour() );
-                        stream << " for: ";
-                    }
-                    stream << result.getExpandedExpression();
-                }
-            }
-
-            void printMessage() {
-                if ( itMessage != messages.end() ) {
-                    stream << " '" << itMessage->message << "'";
-                    ++itMessage;
-                }
-            }
-
-            void printRemainingMessages( Colour::Code colour = dimColour() ) {
-                if ( itMessage == messages.end() )
-                    return;
-
-                // using messages.end() directly yields compilation error:
-                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
-                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
-
-                {
-                    Colour colourGuard( colour );
-                    stream << " with " << pluralise( N, "message" ) << ":";
-                }
-
-                for(; itMessage != itEnd; ) {
-                    // If this assertion is a warning ignore any INFO messages
-                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
-                        stream << " '" << itMessage->message << "'";
-                        if ( ++itMessage != itEnd ) {
-                            Colour colourGuard( dimColour() );
-                            stream << " and";
-                        }
-                    }
-                }
-            }
-
-        private:
-            std::ostream& stream;
-            AssertionStats const& stats;
-            AssertionResult const& result;
-            std::vector<MessageInfo> messages;
-            std::vector<MessageInfo>::const_iterator itMessage;
-            bool printInfoMessages;
-        };
-
-        // Colour, message variants:
-        // - white: No tests ran.
-        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
-        // - white: Passed [both/all] N test cases (no assertions).
-        // -   red: Failed N tests cases, failed M assertions.
-        // - green: Passed [both/all] N tests cases with M assertions.
-
-        std::string bothOrAll( std::size_t count ) const {
-            return count == 1 ? "" : count == 2 ? "both " : "all " ;
-        }
-
-        void printTotals( const Totals& totals ) const {
-            if( totals.testCases.total() == 0 ) {
-                stream << "No tests ran.";
-            }
-            else if( totals.testCases.failed == totals.testCases.total() ) {
-                Colour colour( Colour::ResultError );
-                const std::string qualify_assertions_failed =
-                    totals.assertions.failed == totals.assertions.total() ?
-                        bothOrAll( totals.assertions.failed ) : "";
-                stream <<
-                    "Failed " << bothOrAll( totals.testCases.failed )
-                              << pluralise( totals.testCases.failed, "test case"  ) << ", "
-                    "failed " << qualify_assertions_failed <<
-                                 pluralise( totals.assertions.failed, "assertion" ) << ".";
-            }
-            else if( totals.assertions.total() == 0 ) {
-                stream <<
-                    "Passed " << bothOrAll( totals.testCases.total() )
-                              << pluralise( totals.testCases.total(), "test case" )
-                              << " (no assertions).";
-            }
-            else if( totals.assertions.failed ) {
-                Colour colour( Colour::ResultError );
-                stream <<
-                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", "
-                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
-            }
-            else {
-                Colour colour( Colour::ResultSuccess );
-                stream <<
-                    "Passed " << bothOrAll( totals.testCases.passed )
-                              << pluralise( totals.testCases.passed, "test case"  ) <<
-                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << ".";
-            }
-        }
-    };
-
-    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
-
-} // end namespace Catch
-
-namespace Catch {
-    NonCopyable::~NonCopyable() {}
-    IShared::~IShared() {}
-    StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
-    IContext::~IContext() {}
-    IResultCapture::~IResultCapture() {}
-    ITestCase::~ITestCase() {}
-    ITestCaseRegistry::~ITestCaseRegistry() {}
-    IRegistryHub::~IRegistryHub() {}
-    IMutableRegistryHub::~IMutableRegistryHub() {}
-    IExceptionTranslator::~IExceptionTranslator() {}
-    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
-    IReporter::~IReporter() {}
-    IReporterFactory::~IReporterFactory() {}
-    IReporterRegistry::~IReporterRegistry() {}
-    IStreamingReporter::~IStreamingReporter() {}
-    AssertionStats::~AssertionStats() {}
-    SectionStats::~SectionStats() {}
-    TestCaseStats::~TestCaseStats() {}
-    TestGroupStats::~TestGroupStats() {}
-    TestRunStats::~TestRunStats() {}
-    CumulativeReporterBase::SectionNode::~SectionNode() {}
-    CumulativeReporterBase::~CumulativeReporterBase() {}
-
-    StreamingReporterBase::~StreamingReporterBase() {}
-    ConsoleReporter::~ConsoleReporter() {}
-    CompactReporter::~CompactReporter() {}
-    IRunner::~IRunner() {}
-    IMutableContext::~IMutableContext() {}
-    IConfig::~IConfig() {}
-    XmlReporter::~XmlReporter() {}
-    JunitReporter::~JunitReporter() {}
-    TestRegistry::~TestRegistry() {}
-    FreeFunctionTestCase::~FreeFunctionTestCase() {}
-    IGeneratorInfo::~IGeneratorInfo() {}
-    IGeneratorsForTest::~IGeneratorsForTest() {}
-    TestSpec::Pattern::~Pattern() {}
-    TestSpec::NamePattern::~NamePattern() {}
-    TestSpec::TagPattern::~TagPattern() {}
-    TestSpec::ExcludedPattern::~ExcludedPattern() {}
-
-    Matchers::Impl::StdString::Equals::~Equals() {}
-    Matchers::Impl::StdString::Contains::~Contains() {}
-    Matchers::Impl::StdString::StartsWith::~StartsWith() {}
-    Matchers::Impl::StdString::EndsWith::~EndsWith() {}
-
-    void Config::dummy() {}
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-#endif
-
-#ifdef CATCH_CONFIG_MAIN
-// #included from: internal/catch_default_main.hpp
-#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
-
-#ifndef __OBJC__
-
-// Standard C/C++ main entry point
-int main (int argc, char * const argv[]) {
-    return Catch::Session().run( argc, argv );
-}
-
-#else // __OBJC__
-
-// Objective-C entry point
-int main (int argc, char * const argv[]) {
-#if !CATCH_ARC_ENABLED
-    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-#endif
-
-    Catch::registerTestMethods();
-    int result = Catch::Session().run( argc, (char* const*)argv );
-
-#if !CATCH_ARC_ENABLED
-    [pool drain];
-#endif
-
-    return result;
-}
-
-#endif // __OBJC__
-
-#endif
-
-#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
-#  undef CLARA_CONFIG_MAIN
-#endif
-
-//////
-
-// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
-#ifdef CATCH_CONFIG_PREFIX_ALL
-
-#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
-#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
-
-#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
-#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
-#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
-
-#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
-#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
-#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
-#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
-#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
-
-#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
-#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
-#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
-
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
-#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
-
-#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
-#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
-#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
-#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
-#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
-    #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
-    #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
-    #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
-    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
-    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
-#else
-    #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
-    #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
-    #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
-    #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
-    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
-    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
-#endif
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
-
-#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
-
-// "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#else
-#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
-#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
-#endif
-#define CATCH_GIVEN( desc )    CATCH_SECTION( "Given: " desc, "" )
-#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
-#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
-
-// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
-#else
-
-#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
-#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
-
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
-#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
-#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
-
-#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
-#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
-#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
-#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
-#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
-
-#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
-#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
-#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
-
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
-#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
-
-#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
-#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
-#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
-#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
-#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
-    #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
-    #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
-    #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
-    #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
-    #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
-#else
-    #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
-    #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
-    #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
-    #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
-    #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
-    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
-#endif
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
-
-#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
-
-#endif
-
-#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
-
-// "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#else
-#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
-#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
-#endif
-#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
-#define WHEN( desc )     SECTION( "    When: " desc, "" )
-#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
-#define THEN( desc )     SECTION( "    Then: " desc, "" )
-#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
-
-using Catch::Detail::Approx;
-
-// #included from: internal/catch_reenable_warnings.h
-
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-#    ifdef __ICC // icpc defines the __clang__ macro
-#        pragma warning(pop)
-#    else
-#        pragma clang diagnostic pop
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic pop
-#endif
-
-#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
index ce575fcc9dd38c22a17abb2fc2e0439475756987..a561d1a21f70337f78d44e6391a93c0b34e5e9a1 100644 (file)
 #include "../src/core/const.h"
 #include "../src/core/conf.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 using std::string;
+using namespace giada::m;
 
 
 TEST_CASE("Test Conf class")
 {
-  Conf conf;
-
+  conf::init();
+  
   SECTION("test write")
   {
-    conf.header = "GIADACONFTEST";
-    conf.logMode = 1;
-    conf.soundSystem = 2;
-    conf.soundDeviceOut = 3;
-    conf.soundDeviceIn = 4;
-    conf.channelsOut = 5;
-    conf.channelsIn = 6;
-    conf.samplerate = 7;
-    conf.buffersize = 8;
-    conf.delayComp = 9;
-    conf.limitOutput = true;
-    conf.rsmpQuality = 10;
-    conf.midiSystem = 11;
-    conf.midiPortOut = 12;
-    conf.midiPortIn = 13;
-    conf.noNoteOff = false;
-    conf.midiMapPath = "path/to/midi/map";
-    conf.lastFileMap = "path/to/last/midi/map";
-    conf.midiSync = 14;
-    conf.midiTCfps = 15.1f;
-    conf.midiInRewind = 16;
-    conf.midiInStartStop = 17;
-    conf.midiInActionRec = 18;
-    conf.midiInInputRec = 19;
-    conf.midiInMetronome = 20;
-    conf.midiInVolumeIn = 21;
-    conf.midiInVolumeOut = 22;
-    conf.midiInBeatDouble = 23;
-    conf.midiInBeatHalf = 24;
-    conf.recsStopOnChanHalt = true;
-    conf.chansStopOnSeqHalt = false;
-    conf.treatRecsAsLoops = true;
-    conf.resizeRecordings = false;
-    conf.pluginPath = "path/to/plugins";
-    conf.patchPath = "path/to/patches";
-    conf.samplePath = "path/to/samples";
-    conf.mainWindowX = 0;
-    conf.mainWindowY = 0;
-    conf.mainWindowW = 800;
-    conf.mainWindowH = 600;
-    conf.browserX = 0;
-    conf.browserY = 0;
-    conf.browserW = 800;
-    conf.browserH = 600;
-    conf.actionEditorX = 0;
-    conf.actionEditorY = 0;
-    conf.actionEditorW = 800;
-    conf.actionEditorH = 600;
-    conf.actionEditorZoom = 1;
-    conf.actionEditorGridVal = 10;
-    conf.actionEditorGridOn = 1;
-    conf.sampleEditorX = 0;
-    conf.sampleEditorY = 0;
-    conf.sampleEditorW = 800;
-    conf.sampleEditorH = 600;
-    conf.sampleEditorGridVal = 4;
-    conf.sampleEditorGridOn = 0;
-    conf.pianoRollY = 0;
-    conf.pianoRollH = 900;
-    conf.pluginListX = 0;
-    conf.pluginListY = 50;
-    conf.configX = 20;
-    conf.configY = 20;
-    conf.bpmX = 30;
-    conf.bpmY = 36;
-    conf.beatsX = 1;
-    conf.beatsY = 1;
-    conf.aboutX = 2;
-    conf.aboutY = 2;
+    conf::header = "GIADACONFTEST";
+    conf::logMode = 1;
+    conf::soundSystem = 2;
+    conf::soundDeviceOut = 3;
+    conf::soundDeviceIn = 4;
+    conf::channelsOut = 5;
+    conf::channelsIn = 6;
+    conf::samplerate = 7;
+    conf::buffersize = 8;
+    conf::delayComp = 9;
+    conf::limitOutput = true;
+    conf::rsmpQuality = 10;
+    conf::midiSystem = 11;
+    conf::midiPortOut = 12;
+    conf::midiPortIn = 13;
+    conf::noNoteOff = false;
+    conf::midiMapPath = "path/to/midi/map";
+    conf::lastFileMap = "path/to/last/midi/map";
+    conf::midiSync = 14;
+    conf::midiTCfps = 15.1f;
+    conf::midiInRewind = 16;
+    conf::midiInStartStop = 17;
+    conf::midiInActionRec = 18;
+    conf::midiInInputRec = 19;
+    conf::midiInMetronome = 20;
+    conf::midiInVolumeIn = 21;
+    conf::midiInVolumeOut = 22;
+    conf::midiInBeatDouble = 23;
+    conf::midiInBeatHalf = 24;
+    conf::recsStopOnChanHalt = true;
+    conf::chansStopOnSeqHalt = false;
+    conf::treatRecsAsLoops = true;
+    conf::resizeRecordings = false;
+    conf::pluginPath = "path/to/plugins";
+    conf::patchPath = "path/to/patches";
+    conf::samplePath = "path/to/samples";
+    conf::mainWindowX = 0;
+    conf::mainWindowY = 0;
+    conf::mainWindowW = 800;
+    conf::mainWindowH = 600;
+    conf::browserX = 0;
+    conf::browserY = 0;
+    conf::browserW = 800;
+    conf::browserH = 600;
+    conf::actionEditorX = 0;
+    conf::actionEditorY = 0;
+    conf::actionEditorW = 800;
+    conf::actionEditorH = 600;
+    conf::actionEditorZoom = 1;
+    conf::actionEditorGridVal = 10;
+    conf::actionEditorGridOn = 1;
+    conf::sampleEditorX = 0;
+    conf::sampleEditorY = 0;
+    conf::sampleEditorW = 800;
+    conf::sampleEditorH = 600;
+    conf::sampleEditorGridVal = 4;
+    conf::sampleEditorGridOn = 0;
+    conf::pianoRollY = 0;
+    conf::pianoRollH = 900;
+    conf::pluginListX = 0;
+    conf::pluginListY = 50;
+    conf::configX = 20;
+    conf::configY = 20;
+    conf::bpmX = 30;
+    conf::bpmY = 36;
+    conf::beatsX = 1;
+    conf::beatsY = 1;
+    conf::aboutX = 2;
+    conf::aboutY = 2;
 
-    REQUIRE(conf.write() == 1);
+    REQUIRE(conf::write() == 1);
   }
 
   SECTION("test read")
   {
-    REQUIRE(conf.read() == 1);
-    REQUIRE(conf.header == "GIADACONFTEST");
-    REQUIRE(conf.logMode == 1);
-    REQUIRE(conf.soundSystem == 2);
-    REQUIRE(conf.soundDeviceOut == 3);
-    REQUIRE(conf.soundDeviceIn == 4);
-    REQUIRE(conf.channelsOut == 5);
-    REQUIRE(conf.channelsIn == 6);
-    REQUIRE(conf.samplerate == 44100);  // sanitized
-    REQUIRE(conf.buffersize == 8);
-    REQUIRE(conf.delayComp == 9);
-    REQUIRE(conf.limitOutput == true);
-    REQUIRE(conf.rsmpQuality == 0); // sanitized
-    REQUIRE(conf.midiSystem == 11);
-    REQUIRE(conf.midiPortOut == 12);
-    REQUIRE(conf.midiPortIn == 13);
-    REQUIRE(conf.noNoteOff == false);
-    REQUIRE(conf.midiMapPath == "path/to/midi/map");
-    REQUIRE(conf.lastFileMap == "path/to/last/midi/map");
-    REQUIRE(conf.midiSync == 14);
-    REQUIRE(conf.midiTCfps == Approx(15.1));
-    REQUIRE(conf.midiInRewind == 16);
-    REQUIRE(conf.midiInStartStop == 17);
-    REQUIRE(conf.midiInActionRec == 18);
-    REQUIRE(conf.midiInInputRec == 19);
-    REQUIRE(conf.midiInMetronome == 20);
-    REQUIRE(conf.midiInVolumeIn == 21);
-    REQUIRE(conf.midiInVolumeOut == 22);
-    REQUIRE(conf.midiInBeatDouble == 23);
-    REQUIRE(conf.midiInBeatHalf == 24);
-    REQUIRE(conf.recsStopOnChanHalt == true);
-    REQUIRE(conf.chansStopOnSeqHalt == false);
-    REQUIRE(conf.treatRecsAsLoops == true);
-    REQUIRE(conf.resizeRecordings == false);
-    REQUIRE(conf.pluginPath == "path/to/plugins");
-    REQUIRE(conf.patchPath == "path/to/patches");
-    REQUIRE(conf.samplePath == "path/to/samples");
-    REQUIRE(conf.mainWindowX == 0);
-    REQUIRE(conf.mainWindowY == 0);
-    REQUIRE(conf.mainWindowW == 800);
-    REQUIRE(conf.mainWindowH == 600);
-    REQUIRE(conf.browserX == 0);
-    REQUIRE(conf.browserY == 0);
-    REQUIRE(conf.browserW == 800);
-    REQUIRE(conf.browserH == 600);
-    REQUIRE(conf.actionEditorX == 0);
-    REQUIRE(conf.actionEditorY == 0);
-    REQUIRE(conf.actionEditorW == 800);
-    REQUIRE(conf.actionEditorH == 600);
-    REQUIRE(conf.actionEditorZoom == 100);  // sanitized
-    REQUIRE(conf.actionEditorGridVal == 10);
-    REQUIRE(conf.actionEditorGridOn == 1);
-    REQUIRE(conf.sampleEditorX == 0);
-    REQUIRE(conf.sampleEditorY == 0);
-    REQUIRE(conf.sampleEditorW == 800);
-    REQUIRE(conf.sampleEditorH == 600);
-    REQUIRE(conf.sampleEditorGridVal == 4);
-    REQUIRE(conf.sampleEditorGridOn == 0);
-    REQUIRE(conf.pianoRollY == 0);
-    REQUIRE(conf.pianoRollH == 900);
-    REQUIRE(conf.pluginListX == 0);
-    REQUIRE(conf.pluginListY == 50);
-    REQUIRE(conf.configX == 20);
-    REQUIRE(conf.configY == 20);
-    REQUIRE(conf.bpmX == 30);
-    REQUIRE(conf.bpmY == 36);
-    REQUIRE(conf.beatsX == 1);
-    REQUIRE(conf.beatsY == 1);
-    REQUIRE(conf.aboutX == 2);
-    REQUIRE(conf.aboutY == 2);
+    REQUIRE(conf::read() == 1);
+    REQUIRE(conf::header == "GIADACONFTEST");
+    REQUIRE(conf::logMode == 1);
+    REQUIRE(conf::soundSystem == 2);
+    REQUIRE(conf::soundDeviceOut == 3);
+    REQUIRE(conf::soundDeviceIn == 4);
+    REQUIRE(conf::channelsOut == 5);
+    REQUIRE(conf::channelsIn == 6);
+    REQUIRE(conf::samplerate == 44100);  // sanitized
+    REQUIRE(conf::buffersize == 8);
+    REQUIRE(conf::delayComp == 9);
+    REQUIRE(conf::limitOutput == true);
+    REQUIRE(conf::rsmpQuality == 0); // sanitized
+    REQUIRE(conf::midiSystem == 11);
+    REQUIRE(conf::midiPortOut == 12);
+    REQUIRE(conf::midiPortIn == 13);
+    REQUIRE(conf::noNoteOff == false);
+    REQUIRE(conf::midiMapPath == "path/to/midi/map");
+    REQUIRE(conf::lastFileMap == "path/to/last/midi/map");
+    REQUIRE(conf::midiSync == 14);
+    REQUIRE(conf::midiTCfps == Approx(15.1));
+    REQUIRE(conf::midiInRewind == 16);
+    REQUIRE(conf::midiInStartStop == 17);
+    REQUIRE(conf::midiInActionRec == 18);
+    REQUIRE(conf::midiInInputRec == 19);
+    REQUIRE(conf::midiInMetronome == 20);
+    REQUIRE(conf::midiInVolumeIn == 21);
+    REQUIRE(conf::midiInVolumeOut == 22);
+    REQUIRE(conf::midiInBeatDouble == 23);
+    REQUIRE(conf::midiInBeatHalf == 24);
+    REQUIRE(conf::recsStopOnChanHalt == true);
+    REQUIRE(conf::chansStopOnSeqHalt == false);
+    REQUIRE(conf::treatRecsAsLoops == true);
+    REQUIRE(conf::resizeRecordings == false);
+    REQUIRE(conf::pluginPath == "path/to/plugins");
+    REQUIRE(conf::patchPath == "path/to/patches");
+    REQUIRE(conf::samplePath == "path/to/samples");
+    REQUIRE(conf::mainWindowX == 0);
+    REQUIRE(conf::mainWindowY == 0);
+    REQUIRE(conf::mainWindowW == 800);
+    REQUIRE(conf::mainWindowH == 600);
+    REQUIRE(conf::browserX == 0);
+    REQUIRE(conf::browserY == 0);
+    REQUIRE(conf::browserW == 800);
+    REQUIRE(conf::browserH == 600);
+    REQUIRE(conf::actionEditorX == 0);
+    REQUIRE(conf::actionEditorY == 0);
+    REQUIRE(conf::actionEditorW == 800);
+    REQUIRE(conf::actionEditorH == 600);
+    REQUIRE(conf::actionEditorZoom == 100);  // sanitized
+    REQUIRE(conf::actionEditorGridVal == 10);
+    REQUIRE(conf::actionEditorGridOn == 1);
+    REQUIRE(conf::sampleEditorX == 0);
+    REQUIRE(conf::sampleEditorY == 0);
+    REQUIRE(conf::sampleEditorW == 800);
+    REQUIRE(conf::sampleEditorH == 600);
+    REQUIRE(conf::sampleEditorGridVal == 4);
+    REQUIRE(conf::sampleEditorGridOn == 0);
+    REQUIRE(conf::pianoRollY == 0);
+    REQUIRE(conf::pianoRollH == 900);
+    REQUIRE(conf::pluginListX == 0);
+    REQUIRE(conf::pluginListY == 50);
+    REQUIRE(conf::configX == 20);
+    REQUIRE(conf::configY == 20);
+    REQUIRE(conf::bpmX == 30);
+    REQUIRE(conf::bpmY == 36);
+    REQUIRE(conf::beatsX == 1);
+    REQUIRE(conf::beatsY == 1);
+    REQUIRE(conf::aboutX == 2);
+    REQUIRE(conf::aboutY == 2);
   }
 }
index fb544638b4bac04a510b0e0026660761eabdca91..eb3c215a55ce34866ea956ac246f732ce7d7b537 100644 (file)
@@ -1,2 +1,3 @@
-#define CATCH_CONFIG_MAIN 
-#include "catch.hpp"
+#define CATCH_CONFIG_MAIN
+#define CATCH_CONFIG_FAST_COMPILE
+#include "catch/single_include/catch.hpp"
index ebc9645ff1c55becf4b818d7fc2ebef8ccc26394..28b3779e095ac7c75b119dcc4910b709eafe1983 100644 (file)
 #include "../src/core/const.h"
 #include "../src/core/midiMapConf.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 using std::string;
+using namespace giada::m;
 
 
-TEST_CASE("Test MidiMapConf class")
+TEST_CASE("Test MidiMapConf")
 {
-  MidiMapConf midimap;
-
   SECTION("test default values")
   {
-    midimap.setDefault();
-    REQUIRE(midimap.brand  == "");
-       REQUIRE(midimap.device == "");
-       REQUIRE(midimap.muteOn.channel    == 0);
-       REQUIRE(midimap.muteOn.valueStr   == "");
-       REQUIRE(midimap.muteOn.offset     == -1);
-       REQUIRE(midimap.muteOn.value      == 0);
-       REQUIRE(midimap.muteOff.channel   == 0);
-       REQUIRE(midimap.muteOff.valueStr  == "");
-       REQUIRE(midimap.muteOff.offset    == -1);
-       REQUIRE(midimap.muteOff.value     == 0);
-       REQUIRE(midimap.soloOn.channel    == 0);
-       REQUIRE(midimap.soloOn.valueStr   == "");
-       REQUIRE(midimap.soloOn.offset     == -1);
-       REQUIRE(midimap.soloOn.value      == 0);
-       REQUIRE(midimap.soloOff.channel   == 0);
-       REQUIRE(midimap.soloOff.valueStr  == "");
-       REQUIRE(midimap.soloOff.offset    == -1);
-       REQUIRE(midimap.soloOff.value     == 0);
-       REQUIRE(midimap.waiting.channel   == 0);
-       REQUIRE(midimap.waiting.valueStr  == "");
-       REQUIRE(midimap.waiting.offset    == -1);
-       REQUIRE(midimap.waiting.value     == 0);
-       REQUIRE(midimap.playing.channel   == 0);
-       REQUIRE(midimap.playing.valueStr  == "");
-       REQUIRE(midimap.playing.offset    == -1);
-       REQUIRE(midimap.playing.value     == 0);
-       REQUIRE(midimap.stopping.channel  == 0);
-       REQUIRE(midimap.stopping.valueStr == "");
-       REQUIRE(midimap.stopping.offset   == -1);
-       REQUIRE(midimap.stopping.value    == 0);
-       REQUIRE(midimap.stopped.channel   == 0);
-       REQUIRE(midimap.stopped.valueStr  == "");
-       REQUIRE(midimap.stopped.offset    == -1);
-       REQUIRE(midimap.stopped.value     == 0);
+    midimap::setDefault();
+    REQUIRE(midimap::brand  == "");
+       REQUIRE(midimap::device == "");
+       REQUIRE(midimap::muteOn.channel    == 0);
+       REQUIRE(midimap::muteOn.valueStr   == "");
+       REQUIRE(midimap::muteOn.offset     == -1);
+       REQUIRE(midimap::muteOn.value      == 0);
+       REQUIRE(midimap::muteOff.channel   == 0);
+       REQUIRE(midimap::muteOff.valueStr  == "");
+       REQUIRE(midimap::muteOff.offset    == -1);
+       REQUIRE(midimap::muteOff.value     == 0);
+       REQUIRE(midimap::soloOn.channel    == 0);
+       REQUIRE(midimap::soloOn.valueStr   == "");
+       REQUIRE(midimap::soloOn.offset     == -1);
+       REQUIRE(midimap::soloOn.value      == 0);
+       REQUIRE(midimap::soloOff.channel   == 0);
+       REQUIRE(midimap::soloOff.valueStr  == "");
+       REQUIRE(midimap::soloOff.offset    == -1);
+       REQUIRE(midimap::soloOff.value     == 0);
+       REQUIRE(midimap::waiting.channel   == 0);
+       REQUIRE(midimap::waiting.valueStr  == "");
+       REQUIRE(midimap::waiting.offset    == -1);
+       REQUIRE(midimap::waiting.value     == 0);
+       REQUIRE(midimap::playing.channel   == 0);
+       REQUIRE(midimap::playing.valueStr  == "");
+       REQUIRE(midimap::playing.offset    == -1);
+       REQUIRE(midimap::playing.value     == 0);
+       REQUIRE(midimap::stopping.channel  == 0);
+       REQUIRE(midimap::stopping.valueStr == "");
+       REQUIRE(midimap::stopping.offset   == -1);
+       REQUIRE(midimap::stopping.value    == 0);
+       REQUIRE(midimap::stopped.channel   == 0);
+       REQUIRE(midimap::stopped.valueStr  == "");
+       REQUIRE(midimap::stopped.offset    == -1);
+       REQUIRE(midimap::stopped.value     == 0);
   }
 
 #ifdef RUN_TESTS_WITH_LOCAL_FILES
 
   SECTION("test read")
   {
-    midimap.init();
-    midimap.setDefault();
+    midimap::init();
+    midimap::setDefault();
 
     /* expect more than 2 midifiles */
 
-    REQUIRE(midimap.maps.size() >= 2);
+    REQUIRE(midimap::maps.size() >= 2);
 
     /* try with deprecated mode */
 
-    int res = midimap.read("akai-lpd8.giadamap");
+    int res = midimap::read("akai-lpd8.giadamap");
     if (res != MIDIMAP_READ_OK)
-      res = midimap.readMap_DEPR_("akai-lpd8.giadamap");
+      res = midimap::readMap_DEPR_("akai-lpd8.giadamap");
 
     REQUIRE(res == MIDIMAP_READ_OK);
 
-    REQUIRE(midimap.brand == "AKAI");
-    REQUIRE(midimap.device == "LPD8");
+    REQUIRE(midimap::brand == "AKAI");
+    REQUIRE(midimap::device == "LPD8");
 
-    REQUIRE(midimap.initCommands.size() == 2);
-    REQUIRE(midimap.initCommands[0].channel == 0);
-    REQUIRE(midimap.initCommands[0].value == 0xB0000000);
-    REQUIRE(midimap.initCommands[1].channel == 0);
-    REQUIRE(midimap.initCommands[1].value == 0xB0002800);
+    REQUIRE(midimap::initCommands.size() == 2);
+    REQUIRE(midimap::initCommands[0].channel == 0);
+    REQUIRE(midimap::initCommands[0].value == 0xB0000000);
+    REQUIRE(midimap::initCommands[1].channel == 0);
+    REQUIRE(midimap::initCommands[1].value == 0xB0002800);
 
     /* TODO - can't check 'valueStr' until deprecated methods are alive */
 
-    REQUIRE(midimap.muteOn.channel == 0);
-    //REQUIRE(midimap.muteOn.valueStr == "90nn3F00");
-    REQUIRE(midimap.muteOn.offset == 16);
-    REQUIRE(midimap.muteOn.value == 0x90003F00);
-
-    REQUIRE(midimap.muteOff.channel == 0);
-    //REQUIRE(midimap.muteOff.valueStr == "90nn0C00");
-    REQUIRE(midimap.muteOff.offset == 16);
-    REQUIRE(midimap.muteOff.value == 0x90000C00);
-
-    REQUIRE(midimap.soloOn.channel == 0);
-    //REQUIRE(midimap.soloOn.valueStr == "90nn0F00");
-    REQUIRE(midimap.soloOn.offset == 16);
-    REQUIRE(midimap.soloOn.value == 0x90000F00);
-
-    REQUIRE(midimap.soloOff.channel == 0);
-    //REQUIRE(midimap.soloOff.valueStr == "90nn0C00");
-    REQUIRE(midimap.soloOff.offset == 16);
-    REQUIRE(midimap.soloOff.value == 0x90000C00);
-
-    REQUIRE(midimap.waiting.channel == 0);
-    //REQUIRE(midimap.waiting.valueStr == "90nn7f00");
-    REQUIRE(midimap.waiting.offset == 16);
-    REQUIRE(midimap.waiting.value == 0x90007f00);
-
-    REQUIRE(midimap.playing.channel == 0);
-    //REQUIRE(midimap.playing.valueStr == "90nn7f00");
-    REQUIRE(midimap.playing.offset == 16);
-    REQUIRE(midimap.playing.value == 0x90007f00);
-
-    REQUIRE(midimap.stopping.channel == 0);
-    //REQUIRE(midimap.stopping.valueStr == "90nn7f00");
-    REQUIRE(midimap.stopping.offset == 16);
-    REQUIRE(midimap.stopping.value == 0x90007f00);
-
-    REQUIRE(midimap.stopped.channel == 0);
-    //REQUIRE(midimap.stopped.valueStr == "80nn7f00");
-    REQUIRE(midimap.stopped.offset == 16);
-    REQUIRE(midimap.stopped.value == 0x80007f00);
+    REQUIRE(midimap::muteOn.channel == 0);
+    //REQUIRE(midimap::muteOn.valueStr == "90nn3F00");
+    REQUIRE(midimap::muteOn.offset == 16);
+    REQUIRE(midimap::muteOn.value == 0x90003F00);
+
+    REQUIRE(midimap::muteOff.channel == 0);
+    //REQUIRE(midimap::muteOff.valueStr == "90nn0C00");
+    REQUIRE(midimap::muteOff.offset == 16);
+    REQUIRE(midimap::muteOff.value == 0x90000C00);
+
+    REQUIRE(midimap::soloOn.channel == 0);
+    //REQUIRE(midimap::soloOn.valueStr == "90nn0F00");
+    REQUIRE(midimap::soloOn.offset == 16);
+    REQUIRE(midimap::soloOn.value == 0x90000F00);
+
+    REQUIRE(midimap::soloOff.channel == 0);
+    //REQUIRE(midimap::soloOff.valueStr == "90nn0C00");
+    REQUIRE(midimap::soloOff.offset == 16);
+    REQUIRE(midimap::soloOff.value == 0x90000C00);
+
+    REQUIRE(midimap::waiting.channel == 0);
+    //REQUIRE(midimap::waiting.valueStr == "90nn7f00");
+    REQUIRE(midimap::waiting.offset == 16);
+    REQUIRE(midimap::waiting.value == 0x90007f00);
+
+    REQUIRE(midimap::playing.channel == 0);
+    //REQUIRE(midimap::playing.valueStr == "90nn7f00");
+    REQUIRE(midimap::playing.offset == 16);
+    REQUIRE(midimap::playing.value == 0x90007f00);
+
+    REQUIRE(midimap::stopping.channel == 0);
+    //REQUIRE(midimap::stopping.valueStr == "90nn7f00");
+    REQUIRE(midimap::stopping.offset == 16);
+    REQUIRE(midimap::stopping.value == 0x90007f00);
+
+    REQUIRE(midimap::stopped.channel == 0);
+    //REQUIRE(midimap::stopped.valueStr == "80nn7f00");
+    REQUIRE(midimap::stopped.offset == 16);
+    REQUIRE(midimap::stopped.value == 0x80007f00);
   }
 
 #endif // #ifdef RUN_TESTS_WITH_LOCAL_FILES
index 991ff1f2432fd8c4b4363407eba46453828a27b7..a84c94572e2a53a6e2333c42f1339056de8a29b7 100644 (file)
@@ -1,28 +1,28 @@
 #include "../src/core/patch.h"
 #include "../src/core/const.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 using std::string;
 using std::vector;
+using namespace giada::m;
 
 
 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;
+    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;
+    patch::plugin_t  plugin1;
+    patch::plugin_t  plugin2;
+    patch::plugin_t  plugin3;
 #endif
 
     action1.type   = 0;
@@ -63,8 +63,7 @@ TEST_CASE("Test Patch class")
     channel1.mute_s            = 0;
     channel1.solo              = 0;
     channel1.volume            = 1.0f;
-    channel1.panLeft           = 0.5f;
-    channel1.panRight          = 0.5f;
+    channel1.pan               = 0.5f;
     channel1.midiIn            = true;
     channel1.midiInKeyPress    = UINT32_MAX;  // check maximum value
     channel1.midiInKeyRel      = 1;
@@ -89,62 +88,62 @@ TEST_CASE("Test Patch class")
     channel1.midiInPitch       = 0;
     channel1.midiOut           = 0;
     channel1.midiOutChan       = 5;
-    patch.channels.push_back(channel1);
+    patch::channels.push_back(channel1);
 
     column.index = 0;
     column.width = 500;
-    patch.columns.push_back(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;
+    patch::columns.push_back(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.push_back(plugin1);
-    patch.masterOutPlugins.push_back(plugin2);
+    patch::masterInPlugins.push_back(plugin1);
+    patch::masterOutPlugins.push_back(plugin2);
 
 #endif
 
-    REQUIRE(patch.write(filename) == 1);
+    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(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);
+    patch::channel_t channel0 = patch::channels.at(0);
     REQUIRE(channel0.type == CHANNEL_SAMPLE);
     REQUIRE(channel0.index == 666);
     REQUIRE(channel0.column == 0);
@@ -152,8 +151,7 @@ TEST_CASE("Test Patch class")
     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.pan == Approx(0.5f));
     REQUIRE(channel0.midiIn == true);
     REQUIRE(channel0.midiInKeyPress == UINT32_MAX);
     REQUIRE(channel0.midiInKeyRel == 1);
@@ -171,7 +169,7 @@ TEST_CASE("Test Patch class")
     REQUIRE(channel0.mode == 0);
     REQUIRE(channel0.begin == 0);
     REQUIRE(channel0.end == 0);
-    REQUIRE(channel0.boost == 0);
+    REQUIRE(channel0.boost == 1.0f);
     REQUIRE(channel0.recActive == 0);
     REQUIRE(channel0.pitch == Approx(1.2f));
     REQUIRE(channel0.midiInReadActions == 0);
@@ -179,27 +177,27 @@ TEST_CASE("Test Patch class")
     REQUIRE(channel0.midiOut == 0);
     REQUIRE(channel0.midiOutChan == 5);
 
-    Patch::action_t action0 = channel0.actions.at(0);
+    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);
+    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);
+    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);
+    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));
@@ -210,14 +208,14 @@ TEST_CASE("Test Patch class")
     REQUIRE(plugin1.params.at(5) == Approx(1.0f));
     REQUIRE(plugin1.params.at(6) == Approx(0.333f));
 
-    Patch::plugin_t masterPlugin0 = patch.masterInPlugins.at(0);
+    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);
+    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));
index 3ae679c38ff60f9291003372a7e77d25fcdc06ef..43215bba4cc4520c465238629a2c3bd5dd1c9acf 100644 (file)
@@ -5,7 +5,7 @@
 #if 0
 
 #include "../src/core/pluginHost.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 TEST_CASE("Test PluginHost class")
diff --git a/tests/recorder.cpp b/tests/recorder.cpp
new file mode 100644 (file)
index 0000000..eda8ec8
--- /dev/null
@@ -0,0 +1,484 @@
+#include "../src/core/recorder.h"
+#include "../src/core/const.h"
+#include "catch/single_include/catch.hpp"
+
+
+using std::string;
+using namespace giada::m;
+
+
+TEST_CASE("Test Recorder")
+{
+  /* Each SECTION the TEST_CASE is executed from the start. The following
+  code is exectuted before each SECTION. */
+
+  pthread_mutex_t mutex;
+  pthread_mutex_init(&mutex, nullptr);
+
+  recorder::init();
+  REQUIRE(recorder::frames.size() == 0);
+  REQUIRE(recorder::global.size() == 0);
+
+  SECTION("Test record single action")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 50, 1, 0.5f);
+
+    REQUIRE(recorder::frames.size() == 1);
+    REQUIRE(recorder::frames.at(0) == 50);
+    REQUIRE(recorder::global.at(0).size() == 1);  // 1 action on frame #0
+    REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+    REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+    REQUIRE(recorder::global.at(0).at(0)->iValue == 1);
+    REQUIRE(recorder::global.at(0).at(0)->fValue == 0.5f);
+  }
+
+  SECTION("Test record, two actions on same frame")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+    recorder::rec(0, G_ACTION_KEYREL,   50, 1, 0.5f);
+
+    REQUIRE(recorder::frames.size() == 1);    // same frame, frames.size must stay 1
+    REQUIRE(recorder::frames.at(0) == 50);
+    REQUIRE(recorder::global.at(0).size() == 2);  // 2 actions on frame #0
+
+    REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+    REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+    REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+    REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+    REQUIRE(recorder::global.at(0).at(1)->chan == 0);
+    REQUIRE(recorder::global.at(0).at(1)->type == G_ACTION_KEYREL);
+    REQUIRE(recorder::global.at(0).at(1)->frame == 50);
+    REQUIRE(recorder::global.at(0).at(1)->iValue == 1);
+    REQUIRE(recorder::global.at(0).at(1)->fValue == 0.5f);
+
+    SECTION("Test record, another action on a different frame")
+    {
+      recorder::rec(0, G_ACTION_KEYPRESS, 70, 1, 0.5f);
+
+      REQUIRE(recorder::frames.size() == 2);
+      REQUIRE(recorder::frames.at(1) == 70);
+      REQUIRE(recorder::global.at(0).size() == 2);  // 2 actions on frame #0
+      REQUIRE(recorder::global.at(1).size() == 1);  // 1 actions on frame #1
+      REQUIRE(recorder::global.at(1).at(0)->chan == 0);
+      REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYPRESS);
+      REQUIRE(recorder::global.at(1).at(0)->frame == 70);
+      REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+      REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+    }
+  }
+
+  SECTION("Test retrieval")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+    recorder::rec(0, G_ACTION_KEYREL,   70, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   70, 1, 0.5f);
+    recorder::rec(2, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+    recorder::rec(2, G_ACTION_KEYREL,   120, 1, 0.5f);
+
+    /* Give me action on chan 1, type G_ACTION_KEYREL, frame 70. */
+    recorder::action *action = nullptr;
+    REQUIRE(recorder::getAction(1, G_ACTION_KEYREL, 70, &action) == 1);
+
+    REQUIRE(action != nullptr);
+    REQUIRE(action->chan == 1);
+    REQUIRE(action->type == G_ACTION_KEYREL);
+    REQUIRE(action->frame == 70);
+    REQUIRE(action->iValue == 1);
+    REQUIRE(action->fValue == 0.5f);
+
+    /* Give me *next* action on chan 0, type G_ACTION_KEYREL, starting from frame 20.
+    Must be action #2 */
+
+    REQUIRE(recorder::getNextAction(0, G_ACTION_KEYREL, 20, &action) == 1);
+    REQUIRE(action != nullptr);
+    REQUIRE(action->chan == 0);
+    REQUIRE(action->type == G_ACTION_KEYREL);
+    REQUIRE(action->frame == 70);
+
+    /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+    frame 200. You are requesting frame outside boundaries. */
+
+    REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 200, &action) == -1);
+
+    /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+    frame 100. That action does not exist. */
+
+    REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 100, &action) == -2);
+  }
+
+  SECTION("Test deletion, single action")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+    recorder::rec(0, G_ACTION_KEYREL,   60, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   80, 1, 0.5f);
+
+    /* Delete action #0, don't check values. */
+    recorder::deleteAction(0, 50, G_ACTION_KEYPRESS, false, &mutex);
+
+    REQUIRE(recorder::frames.size() == 3);
+    REQUIRE(recorder::global.size() == 3);
+
+    SECTION("Test deletion checked")
+    {
+      /* Delete action #1, check values. */
+      recorder::deleteAction(1, 70, G_ACTION_KEYPRESS, true, &mutex, 6, 0.3f);
+
+      REQUIRE(recorder::frames.size() == 2);
+      REQUIRE(recorder::global.size() == 2);
+    }
+  }
+
+  SECTION("Test deletion, range of actions")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+    recorder::rec(0, G_ACTION_KEYREL,   60, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   120, 1, 0.5f);
+
+    /* Delete any action on channel 0 of types KEYPRESS | KEYREL between
+    frames 0 and 200. */
+
+    recorder::deleteActions(0, 0, 200, G_ACTION_KEYPRESS | G_ACTION_KEYREL, &mutex);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::global.size() == 2);
+    REQUIRE(recorder::global.at(0).size() == 1);
+    REQUIRE(recorder::global.at(1).size() == 1);
+
+    REQUIRE(recorder::global.at(0).at(0)->chan == 1);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+    REQUIRE(recorder::global.at(0).at(0)->frame == 100);
+    REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+    REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+    REQUIRE(recorder::global.at(1).at(0)->chan == 1);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 120);
+    REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+    REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+  }
+
+  SECTION("Test action presence")
+  {
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   120, 1, 0.5f);
+
+    recorder::deleteAction(0, 80, G_ACTION_KEYREL, false, &mutex);
+
+    REQUIRE(recorder::hasActions(0) == false);
+    REQUIRE(recorder::hasActions(1) == true);
+  }
+
+  SECTION("Test clear actions by channel")
+  {
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   120, 1, 0.5f);
+
+    recorder::clearChan(1);
+
+    REQUIRE(recorder::hasActions(0) == true);
+    REQUIRE(recorder::hasActions(1) == false);
+    REQUIRE(recorder::frames.size() == 1);
+    REQUIRE(recorder::global.size() == 1);
+    REQUIRE(recorder::global.at(0).size() == 1);
+  }
+
+  SECTION("Test clear actions by type")
+  {
+    recorder::rec(1, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+    recorder::rec(1, G_ACTION_KEYREL,   120, 1, 0.5f);
+
+    /* Clear all actions of type KEYREL from channel 1. */
+
+    recorder::clearAction(1, G_ACTION_KEYREL);
+
+    REQUIRE(recorder::hasActions(0) == true);
+    REQUIRE(recorder::hasActions(1) == false);
+    REQUIRE(recorder::frames.size() == 1);
+    REQUIRE(recorder::global.size() == 1);
+    REQUIRE(recorder::global.at(0).size() == 1);
+  }
+
+  SECTION("Test clear all")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYREL,   100, 6, 0.3f);
+    recorder::rec(2, G_ACTION_KILL, 120, 1, 0.5f);
+
+    recorder::clearAll();
+    REQUIRE(recorder::frames.size() == 0);
+    REQUIRE(recorder::global.size() == 0);
+  }
+
+  SECTION("Test optimization")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+    recorder::rec(1, G_ACTION_KEYREL,   80, 1, 0.5f);
+
+    /* Fake frame 80 without actions.*/
+    recorder::global.at(1).clear();
+
+    recorder::optimize();
+
+    REQUIRE(recorder::frames.size() == 1);
+    REQUIRE(recorder::global.size() == 1);
+    REQUIRE(recorder::global.at(0).size() == 2);
+  }
+
+  SECTION("Test BPM update")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS,  0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,   80, 1, 0.5f);
+
+    recorder::updateBpm(60.0f, 120.0f, 44100);  // scaling up
+
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 40);
+
+    recorder::updateBpm(120.0f, 60.0f, 44100);  // scaling down
+
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 80);
+  }
+
+  SECTION("Test samplerate update")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS,   0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,    80, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,   150, 1, 0.5f);
+
+    recorder::updateSamplerate(44100, 22050); // scaling down
+
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 160);
+    REQUIRE(recorder::frames.at(2) == 240);
+    REQUIRE(recorder::frames.at(3) == 300);
+
+    recorder::updateSamplerate(22050, 44100); // scaling up
+
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 80);
+    REQUIRE(recorder::frames.at(2) == 120);
+    REQUIRE(recorder::frames.at(3) == 150);
+  }
+
+  SECTION("Test expand")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS,   0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,    80, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KILL,     200, 1, 0.5f);
+
+    recorder::expand(300, 600);
+
+    REQUIRE(recorder::frames.size() == 6);
+    REQUIRE(recorder::global.size() == 6);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 80);
+    REQUIRE(recorder::frames.at(2) == 200);
+    REQUIRE(recorder::frames.at(3) == 300);
+    REQUIRE(recorder::frames.at(4) == 380);
+    REQUIRE(recorder::frames.at(5) == 500);
+  }
+
+  SECTION("Test shrink")
+  {
+    recorder::rec(0, G_ACTION_KEYPRESS,   0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KEYREL,    80, 1, 0.5f);
+    recorder::rec(0, G_ACTION_KILL,     200, 1, 0.5f);
+
+    recorder::shrink(100);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::global.size() == 2);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 80);
+  }
+
+  SECTION("Test overdub, full overwrite")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,    0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF,  80, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEON,  200, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+    /* Should delete all actions in between and keep the first one, plus a
+    new last action on frame 500. */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 1024);
+    recorder::stopOverdub(500, 500, &mutex);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::global.size() == 2);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 500);
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, left overlap")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,  100, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+    /* Overdub part of the leftmost part of a composite action. Expected result:
+    a new composite action.
+    Original:    ----|########|
+    Overdub:     |#######|-----
+    Result:      |#######|----- */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+    recorder::stopOverdub(300, 500, &mutex);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::global.size() == 2);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 300);
+
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 300);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, right overlap")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,  000, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+    /* Overdub part of the rightmost part of a composite action. Expected result:
+    a new composite action.
+    Original:    |########|------
+    Overdub:     -----|#######|--
+    Result:      |###||#######|-- */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+    recorder::stopOverdub(500, 500, &mutex);
+
+    REQUIRE(recorder::frames.size() == 4);
+    REQUIRE(recorder::global.size() == 4);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+    REQUIRE(recorder::frames.at(2) == 100);
+    REQUIRE(recorder::frames.at(3) == 500);
+
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+    REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+    REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(3).at(0)->frame == 500);
+    REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, hole diggin'")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,    0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+    /* Overdub in the middle of a long, composite action. Expected result:
+    original action trimmed down plus anther action next to it. Total frames
+    should be 4.
+    Original:    |#############|
+    Overdub:     ---|#######|---
+    Result:      |#||#######|--- */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+    recorder::stopOverdub(300, 500, &mutex);
+
+    REQUIRE(recorder::frames.size() == 4);
+    REQUIRE(recorder::global.size() == 4);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+    REQUIRE(recorder::frames.at(2) == 100);
+    REQUIRE(recorder::frames.at(3) == 300);
+
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+    REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+    REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(3).at(0)->frame == 300);
+    REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, cover all")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,    0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 100, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEON,  120, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 200, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEON,  220, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+    /* Overdub all existing actions. Expected result: a single composite one. */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+    recorder::stopOverdub(500, 500, &mutex);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::global.size() == 2);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 500);
+
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, null loop")
+  {
+    recorder::rec(0, G_ACTION_MUTEON,    0, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 500, 1, 0.5f);
+
+    /* A null loop is a loop that begins and ends on the very same frame. */
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 300, 16);
+    recorder::stopOverdub(300, 700, &mutex);
+
+    REQUIRE(recorder::frames.size() == 2);
+    REQUIRE(recorder::frames.at(0) == 0);
+    REQUIRE(recorder::frames.at(1) == 284);  // 300 - bufferSize (16)
+
+    REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+    REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+    REQUIRE(recorder::global.at(1).at(0)->frame == 284);
+    REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+  }
+
+  SECTION("Test overdub, ring loop")
+  {
+    /* A ring loop occurs when you record the last action beyond the end of
+    the sequencer.
+    Original:    ---|#######|---
+    Overdub:     #####|------|##
+    Result:      ---|#######||#| */
+
+    recorder::rec(0, G_ACTION_MUTEON,  200, 1, 0.5f);
+    recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+    recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 400, 16);
+    recorder::stopOverdub(250, 700, &mutex);
+
+    REQUIRE(recorder::frames.size() == 4);
+    REQUIRE(recorder::frames.at(0) == 200);
+    REQUIRE(recorder::frames.at(1) == 300);
+    REQUIRE(recorder::frames.at(2) == 400);
+    REQUIRE(recorder::frames.at(3) == 700);
+  }
+}
index 0f549205daeace38c989116bed2cf28c134ee5fb..378745c350a07c13220e2d2df1d1527aed849030 100644 (file)
@@ -1,6 +1,6 @@
 #include "../src/utils/fs.h"
 #include "../src/utils/string.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 using std::vector;
index 930e7fae9e0eb0d7904c511ef0c7b1ad18e95a04..14666791d7cf2bee9903854c45ec85c2d6d07297 100644 (file)
@@ -1,5 +1,5 @@
 #include "../src/core/wave.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
 
 
 using std::string;