New upstream version 0.16.1+ds1
authorIOhannes m zmölnig <zmoelnig@iem.at>
Wed, 8 Jan 2020 12:27:39 +0000 (13:27 +0100)
committerIOhannes m zmölnig <zmoelnig@iem.at>
Wed, 8 Jan 2020 12:27:39 +0000 (13:27 +0100)
297 files changed:
ChangeLog
Makefile.am
configure.ac
src/core/action.h
src/core/audioBuffer.cpp
src/core/audioBuffer.h
src/core/channel.cpp [deleted file]
src/core/channel.h [deleted file]
src/core/channelManager.cpp [deleted file]
src/core/channelManager.h [deleted file]
src/core/channels/channel.cpp [new file with mode: 0644]
src/core/channels/channel.h [new file with mode: 0644]
src/core/channels/channelManager.cpp [new file with mode: 0644]
src/core/channels/channelManager.h [new file with mode: 0644]
src/core/channels/masterChannel.cpp [new file with mode: 0644]
src/core/channels/masterChannel.h [new file with mode: 0644]
src/core/channels/midiChannel.cpp [new file with mode: 0644]
src/core/channels/midiChannel.h [new file with mode: 0644]
src/core/channels/midiChannelProc.cpp [new file with mode: 0644]
src/core/channels/midiChannelProc.h [new file with mode: 0644]
src/core/channels/sampleChannel.cpp [new file with mode: 0644]
src/core/channels/sampleChannel.h [new file with mode: 0644]
src/core/channels/sampleChannelProc.cpp [new file with mode: 0644]
src/core/channels/sampleChannelProc.h [new file with mode: 0644]
src/core/channels/sampleChannelRec.cpp [new file with mode: 0644]
src/core/channels/sampleChannelRec.h [new file with mode: 0644]
src/core/clock.cpp
src/core/clock.h
src/core/conf.cpp
src/core/conf.h
src/core/const.h
src/core/graphics.cpp
src/core/graphics.h
src/core/idManager.cpp [new file with mode: 0644]
src/core/idManager.h [new file with mode: 0644]
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 [deleted file]
src/core/midiChannel.h [deleted file]
src/core/midiChannelProc.cpp [deleted file]
src/core/midiChannelProc.h [deleted file]
src/core/midiDispatcher.cpp
src/core/midiDispatcher.h
src/core/midiEvent.cpp
src/core/midiEvent.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/model/model.cpp [new file with mode: 0644]
src/core/model/model.h [new file with mode: 0644]
src/core/patch.cpp
src/core/patch.h
src/core/plugin.cpp
src/core/plugin.h
src/core/pluginHost.cpp
src/core/pluginHost.h
src/core/pluginManager.cpp
src/core/pluginManager.h
src/core/queue.h [new file with mode: 0644]
src/core/rcuList.h [new file with mode: 0644]
src/core/recManager.cpp
src/core/recManager.h
src/core/recorder.cpp
src/core/recorder.h
src/core/recorderHandler.cpp
src/core/recorderHandler.h
src/core/sampleChannel.cpp [deleted file]
src/core/sampleChannel.h [deleted file]
src/core/sampleChannelProc.cpp [deleted file]
src/core/sampleChannelProc.h [deleted file]
src/core/sampleChannelRec.cpp [deleted file]
src/core/sampleChannelRec.h [deleted file]
src/core/storager.cpp [deleted file]
src/core/storager.h [deleted file]
src/core/types.h
src/core/wave.cpp
src/core/wave.h
src/core/waveFx.cpp
src/core/waveFx.h
src/core/waveManager.cpp
src/core/waveManager.h
src/deps/rtaudio-mod/RtAudio.cpp
src/deps/rtaudio-mod/RtAudio.h
src/glue/actionEditor.cpp
src/glue/actionEditor.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
src/glue/recorder.h
src/glue/sampleEditor.cpp
src/glue/sampleEditor.h
src/glue/storage.cpp
src/glue/transport.cpp
src/glue/transport.h [deleted file]
src/gui/dialogs/about.cpp
src/gui/dialogs/about.h
src/gui/dialogs/actionEditor/baseActionEditor.cpp
src/gui/dialogs/actionEditor/baseActionEditor.h
src/gui/dialogs/actionEditor/midiActionEditor.cpp
src/gui/dialogs/actionEditor/midiActionEditor.h
src/gui/dialogs/actionEditor/sampleActionEditor.cpp
src/gui/dialogs/actionEditor/sampleActionEditor.h
src/gui/dialogs/beatsInput.cpp
src/gui/dialogs/beatsInput.h
src/gui/dialogs/bpmInput.cpp
src/gui/dialogs/bpmInput.h
src/gui/dialogs/browser/browserBase.cpp
src/gui/dialogs/browser/browserBase.h
src/gui/dialogs/browser/browserDir.cpp
src/gui/dialogs/browser/browserDir.h
src/gui/dialogs/browser/browserLoad.cpp
src/gui/dialogs/browser/browserLoad.h
src/gui/dialogs/browser/browserSave.cpp
src/gui/dialogs/browser/browserSave.h
src/gui/dialogs/channelNameInput.cpp
src/gui/dialogs/channelNameInput.h
src/gui/dialogs/config.cpp
src/gui/dialogs/config.h
src/gui/dialogs/devInfo.cpp
src/gui/dialogs/devInfo.h
src/gui/dialogs/keyGrabber.cpp
src/gui/dialogs/keyGrabber.h
src/gui/dialogs/mainWindow.cpp
src/gui/dialogs/mainWindow.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/pluginChooser.cpp
src/gui/dialogs/pluginChooser.h
src/gui/dialogs/pluginList.cpp
src/gui/dialogs/pluginList.h
src/gui/dialogs/pluginWindow.cpp
src/gui/dialogs/pluginWindow.h
src/gui/dialogs/pluginWindowGUI.cpp
src/gui/dialogs/pluginWindowGUI.h
src/gui/dialogs/sampleEditor.cpp
src/gui/dialogs/sampleEditor.h
src/gui/dialogs/warnings.cpp
src/gui/dialogs/warnings.h
src/gui/dialogs/window.cpp
src/gui/dialogs/window.h
src/gui/dispatcher.cpp
src/gui/dispatcher.h
src/gui/elems/actionEditor/baseAction.cpp
src/gui/elems/actionEditor/baseAction.h
src/gui/elems/actionEditor/baseActionEditor.cpp
src/gui/elems/actionEditor/baseActionEditor.h
src/gui/elems/actionEditor/envelopeEditor.cpp
src/gui/elems/actionEditor/envelopeEditor.h
src/gui/elems/actionEditor/envelopePoint.cpp
src/gui/elems/actionEditor/envelopePoint.h
src/gui/elems/actionEditor/gridTool.cpp
src/gui/elems/actionEditor/gridTool.h
src/gui/elems/actionEditor/noteEditor.cpp
src/gui/elems/actionEditor/noteEditor.h
src/gui/elems/actionEditor/pianoItem.cpp
src/gui/elems/actionEditor/pianoItem.h
src/gui/elems/actionEditor/pianoRoll.cpp
src/gui/elems/actionEditor/pianoRoll.h
src/gui/elems/actionEditor/sampleAction.cpp
src/gui/elems/actionEditor/sampleAction.h
src/gui/elems/actionEditor/sampleActionEditor.cpp
src/gui/elems/actionEditor/sampleActionEditor.h
src/gui/elems/actionEditor/velocityEditor.cpp
src/gui/elems/actionEditor/velocityEditor.h
src/gui/elems/basics/baseButton.cpp
src/gui/elems/basics/baseButton.h
src/gui/elems/basics/button.cpp
src/gui/elems/basics/button.h
src/gui/elems/basics/check.cpp
src/gui/elems/basics/check.h
src/gui/elems/basics/liquidScroll.cpp
src/gui/elems/basics/liquidScroll.h
src/gui/elems/basics/radio.cpp
src/gui/elems/basics/radio.h
src/gui/elems/basics/resizerBar.cpp
src/gui/elems/basics/scroll.cpp
src/gui/elems/basics/scroll.h
src/gui/elems/basics/statusButton.cpp
src/gui/elems/basics/statusButton.h
src/gui/elems/browser.cpp
src/gui/elems/browser.h
src/gui/elems/config/tabAudio.cpp
src/gui/elems/config/tabAudio.h
src/gui/elems/config/tabBehaviors.cpp
src/gui/elems/config/tabBehaviors.h
src/gui/elems/config/tabMidi.cpp
src/gui/elems/config/tabMidi.h
src/gui/elems/config/tabMisc.cpp
src/gui/elems/config/tabMisc.h
src/gui/elems/config/tabPlugins.cpp
src/gui/elems/config/tabPlugins.h
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/midiChannelButton.cpp
src/gui/elems/mainWindow/keyboard/midiChannelButton.h
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/keyboard/sampleChannel.h
src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp
src/gui/elems/mainWindow/keyboard/sampleChannelButton.h
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/plugin/pluginBrowser.cpp
src/gui/elems/plugin/pluginBrowser.h
src/gui/elems/plugin/pluginElement.cpp
src/gui/elems/plugin/pluginElement.h
src/gui/elems/plugin/pluginParameter.cpp
src/gui/elems/plugin/pluginParameter.h
src/gui/elems/sampleEditor/boostTool.cpp
src/gui/elems/sampleEditor/boostTool.h
src/gui/elems/sampleEditor/panTool.cpp
src/gui/elems/sampleEditor/panTool.h
src/gui/elems/sampleEditor/pitchTool.cpp
src/gui/elems/sampleEditor/pitchTool.h
src/gui/elems/sampleEditor/rangeTool.cpp
src/gui/elems/sampleEditor/rangeTool.h
src/gui/elems/sampleEditor/shiftTool.cpp
src/gui/elems/sampleEditor/shiftTool.h
src/gui/elems/sampleEditor/volumeTool.cpp
src/gui/elems/sampleEditor/volumeTool.h
src/gui/elems/sampleEditor/waveTools.cpp
src/gui/elems/sampleEditor/waveTools.h
src/gui/elems/sampleEditor/waveform.cpp
src/gui/elems/sampleEditor/waveform.h
src/gui/elems/soundMeter.cpp
src/gui/elems/soundMeter.h
src/gui/updater.cpp [new file with mode: 0644]
src/gui/updater.h [new file with mode: 0644]
src/main.cpp
src/utils/fs.cpp
src/utils/fs.h
src/utils/gui.cpp
src/utils/gui.h
src/utils/json.cpp [new file with mode: 0644]
src/utils/json.h [new file with mode: 0644]
src/utils/log.cpp
src/utils/log.h
src/utils/math.h
src/utils/string.cpp
src/utils/vector.h
src/utils/ver.cpp
src/utils/ver.h
tests/main.cpp
tests/patch.cpp
tests/rcuList.cpp [new file with mode: 0644]
tests/recorder.cpp
tests/sampleChannel.cpp
tests/sampleChannelProc.cpp
tests/sampleChannelRec.cpp
tests/utils.cpp
tests/wave.cpp
tests/waveFx.cpp
tests/waveManager.cpp

index b4bdc9e74701bd7b005a5d46acf4d8e6f798a623..4e451bad0e6b017674033014571c7fa375221aee 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,3 @@
-
 --------------------------------------------------------------------------------
 
 
 --------------------------------------------------------------------------------
 
 
+0.16.1 --- 2020 . 01 . 08
+- FreeBSD support
+- Ability to remove empty columns manually
+- Gray out bpm value when in JACK client mode
+- 'Reset to init state' becomes 'close project' under File menu
+- [Linux] Upgrade Travis CI Linux machine to Xenial
+- Add namespaces to file system and logging functions 
+- Remove unused G_quit global variable
+- Fix Sample Channels in loop mode not playing automatically after audio 
+  recording
+- Fix action recording button status during audio recording, signal mode
+
+
+0.16.0 --- 2019 . 12 . 02
+- Fix columns' resizer bar height on verical window resize
+- Fix crash on MIDI learn global commands
+- Fix wrong channel routing when triggering MIDI learnt commands
+- Fix rewind button not rewinding sample channels in LOOP_* mode 
+- Use actual buffer size from KernelAudio when loading channels from a patch
+- Remove FLTK multithreading initialization
+
+
+0.16.0 beta-2 --- 2019 . 11 . 11
+- Remove all pthread.h leftovers 
+- Fix Windows build
+- Fix memory corruption on Keyboard refresh
+- Fix wave size corruption while editing samples in Sample Editor
+- Fix freeze when cloning a Sample Channel with a sample in it
+- Fix buffer overflow when playing an edited sample
+- Fix crash when loading a project with missing plug-ins
+- Fix freeze when pressing 'play' during an audio recording session
+- Fix play/ending UI status of MIDI channels
+- Fix plug-in sorting on reload
+- Fix crash when reloading a sample in the Sample Editor
+- Fix messy 'R' button status when toggled
+- Fix missing icons and broken checkboxes
+- Optimize model updates on keyboard interaction
+- Always read Columns data from patch files
+- Show missing (and removable) plug-ins in Plug-in Window list
+- Create default empty columns on 'Reset to initial state'
+- Save relative Wave paths in project files
+
+
+0.16.0 beta-1 --- 2019 . 10 . 19
+- Fix macOS build error + warnings
+
+
+0.16.0 beta-0 --- 2019 . 10 . 19
+- New internal engine<->UI architecture 
+- New persistence layer
+- New MIDI queue for incoming live MIDI messages
+- Switch to std::thread
+- Absolute #include paths in source code
+- Removed Boost parameter from Sample Channel 
+
+
 0.15.4 --- 2019 . 03 . 22
 - New record-on-signal option for input and action recording
 - Initial support for plug-ins with mono I/O buses
index 93e11fc6c2b0f1c306374c99afb2125fe1cb4c92..f73d8750d4597721ae63fa9c109662f5b8ccedd1 100644 (file)
@@ -5,127 +5,132 @@ AUTOMAKE_OPTIONS = foreign
 # inside configure.ac.
 
 
-cppFlags =
+cppFlags = -I$(top_srcdir)/src
 cxxFlags = -std=c++14 -Wall
 ldAdd =
 ldFlags =
 sourcesExtra = 
 sourcesMain = src/main.cpp 
-sourcesCore =                            \
-       src/core/const.h                       \
-       src/core/types.h                       \
-       src/core/range.h                       \
-       src/core/action.h                      \
-       src/core/channel.h                     \
-       src/core/channel.cpp                   \
-       src/core/sampleChannel.h               \
-       src/core/sampleChannel.cpp             \
-       src/core/midiDispatcher.h              \
-       src/core/midiDispatcher.cpp            \
-       src/core/midiChannel.h                 \
-       src/core/midiChannel.cpp               \
-       src/core/midiMapConf.h                 \
-       src/core/midiMapConf.cpp               \
-       src/core/midiEvent.h                   \
-       src/core/midiEvent.cpp                 \
-       src/core/audioBuffer.h                 \
-       src/core/audioBuffer.cpp               \
-       src/core/conf.h                        \
-       src/core/conf.cpp                      \
-       src/core/kernelAudio.h                 \
-       src/core/kernelAudio.cpp               \
-       src/core/pluginHost.h                  \
-       src/core/pluginHost.cpp                \
-       src/core/pluginManager.h               \
-       src/core/pluginManager.cpp             \
-       src/core/mixerHandler.h                \
-       src/core/mixerHandler.cpp              \
-       src/core/init.h                        \
-       src/core/init.cpp                      \
-       src/core/plugin.h                      \
-       src/core/plugin.cpp                    \
-       src/core/wave.h                        \
-       src/core/wave.cpp                      \
-       src/core/waveFx.h                      \
-       src/core/waveFx.cpp                    \
-       src/core/kernelMidi.h                  \
-       src/core/kernelMidi.cpp                \
-       src/core/graphics.h                    \
-       src/core/graphics.cpp                  \
-       src/core/patch.h                       \
-       src/core/patch.cpp                     \
-       src/core/recorderHandler.h             \
-       src/core/recorderHandler.cpp           \
-       src/core/recorder.h                    \
-       src/core/recorder.cpp                  \
-       src/core/mixer.h                       \
-       src/core/mixer.cpp                     \
-       src/core/storager.h                        \
-       src/core/storager.cpp                  \
-       src/core/clock.h                       \
-       src/core/clock.cpp                     \
-       src/core/waveManager.h                 \
-       src/core/waveManager.cpp               \
-       src/core/channelManager.h              \
-       src/core/channelManager.cpp            \
-       src/core/sampleChannelProc.h           \
-       src/core/sampleChannelProc.cpp         \
-       src/core/sampleChannelRec.h            \
-       src/core/sampleChannelRec.cpp          \
-       src/core/midiChannelProc.h             \
-       src/core/midiChannelProc.cpp           \
-       src/core/recManager.h                  \
-       src/core/recManager.cpp                \
-       src/glue/main.h                        \
-       src/glue/main.cpp                      \
-       src/glue/io.h                          \
-       src/glue/io.cpp                        \
-       src/glue/storage.h                     \
-       src/glue/storage.cpp                   \
-       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/glue/sampleEditor.h                \
-       src/glue/sampleEditor.cpp              \
-       src/glue/actionEditor.h                \
-       src/glue/actionEditor.cpp              \
-       src/gui/dialogs/window.h               \
-       src/gui/dialogs/window.cpp             \
-       src/gui/dispatcher.h                   \
-       src/gui/dispatcher.cpp                 \
-       src/gui/dialogs/keyGrabber.h           \
-       src/gui/dialogs/keyGrabber.cpp         \
-       src/gui/dialogs/about.h                \
-       src/gui/dialogs/about.cpp              \
-       src/gui/dialogs/mainWindow.h           \
-       src/gui/dialogs/mainWindow.cpp         \
-       src/gui/dialogs/beatsInput.h           \
-       src/gui/dialogs/beatsInput.cpp         \
-       src/gui/dialogs/warnings.h             \
-       src/gui/dialogs/warnings.cpp           \
-       src/gui/dialogs/bpmInput.h             \
-       src/gui/dialogs/bpmInput.cpp           \
-       src/gui/dialogs/channelNameInput.h     \
-       src/gui/dialogs/channelNameInput.cpp   \
-       src/gui/dialogs/config.h               \
-       src/gui/dialogs/config.cpp             \
-       src/gui/dialogs/devInfo.h              \
-       src/gui/dialogs/devInfo.cpp            \
-       src/gui/dialogs/pluginList.h           \
-       src/gui/dialogs/pluginList.cpp         \
-       src/gui/dialogs/pluginWindow.h         \
-       src/gui/dialogs/pluginWindow.cpp       \
-       src/gui/dialogs/sampleEditor.h         \
-       src/gui/dialogs/sampleEditor.cpp       \
-       src/gui/dialogs/pluginWindowGUI.h      \
-       src/gui/dialogs/pluginWindowGUI.cpp    \
-       src/gui/dialogs/pluginChooser.h        \
-       src/gui/dialogs/pluginChooser.cpp      \
+sourcesCore =                               \
+       src/core/const.h                        \
+       src/core/queue.h                        \
+       src/core/types.h                        \
+       src/core/range.h                        \
+       src/core/action.h                       \
+       src/core/midiDispatcher.h               \
+       src/core/midiDispatcher.cpp             \
+       src/core/midiMapConf.h                  \
+       src/core/midiMapConf.cpp                \
+       src/core/midiEvent.h                    \
+       src/core/midiEvent.cpp                  \
+       src/core/audioBuffer.h                  \
+       src/core/audioBuffer.cpp                \
+       src/core/conf.h                         \
+       src/core/conf.cpp                       \
+       src/core/kernelAudio.h                  \
+       src/core/kernelAudio.cpp                \
+       src/core/pluginHost.h                   \
+       src/core/pluginHost.cpp                 \
+       src/core/pluginManager.h                \
+       src/core/pluginManager.cpp              \
+       src/core/mixerHandler.h                 \
+       src/core/mixerHandler.cpp               \
+       src/core/init.h                         \
+       src/core/init.cpp                       \
+       src/core/plugin.h                       \
+       src/core/plugin.cpp                     \
+       src/core/wave.h                         \
+       src/core/wave.cpp                       \
+       src/core/waveFx.h                       \
+       src/core/waveFx.cpp                     \
+       src/core/kernelMidi.h                   \
+       src/core/kernelMidi.cpp                 \
+       src/core/graphics.h                     \
+       src/core/graphics.cpp                   \
+       src/core/patch.h                        \
+       src/core/patch.cpp                      \
+       src/core/recorderHandler.h              \
+       src/core/recorderHandler.cpp            \
+       src/core/recorder.h                     \
+       src/core/recorder.cpp                   \
+       src/core/mixer.h                        \
+       src/core/mixer.cpp                      \
+       src/core/clock.h                        \
+       src/core/clock.cpp                      \
+       src/core/waveManager.h                  \
+       src/core/waveManager.cpp                \
+       src/core/recManager.h                   \
+       src/core/recManager.cpp                 \
+       src/core/channels/channel.h             \
+       src/core/channels/channel.cpp           \
+       src/core/channels/midiChannel.h         \
+       src/core/channels/midiChannel.cpp       \
+       src/core/channels/masterChannel.h       \
+       src/core/channels/masterChannel.cpp     \
+       src/core/channels/sampleChannel.h       \
+       src/core/channels/sampleChannel.cpp     \
+       src/core/channels/channelManager.h      \
+       src/core/channels/channelManager.cpp    \
+       src/core/channels/sampleChannelProc.h   \
+       src/core/channels/sampleChannelProc.cpp \
+       src/core/channels/sampleChannelRec.h    \
+       src/core/channels/sampleChannelRec.cpp  \
+       src/core/channels/midiChannelProc.h     \
+       src/core/channels/midiChannelProc.cpp   \
+       src/core/model/model.h                  \
+       src/core/model/model.cpp                \
+       src/core/idManager.h                    \
+       src/core/idManager.cpp                  \
+       src/glue/main.h                         \
+       src/glue/main.cpp                       \
+       src/glue/io.h                           \
+       src/glue/io.cpp                         \
+       src/glue/storage.h                      \
+       src/glue/storage.cpp                    \
+       src/glue/channel.h                      \
+       src/glue/channel.cpp                    \
+       src/glue/plugin.h                       \
+       src/glue/plugin.cpp                     \
+       src/glue/recorder.h                     \
+       src/glue/recorder.cpp                   \
+       src/glue/sampleEditor.h                 \
+       src/glue/sampleEditor.cpp               \
+       src/glue/actionEditor.h                 \
+       src/glue/actionEditor.cpp               \
+       src/gui/dialogs/window.h                \
+       src/gui/dialogs/window.cpp              \
+       src/gui/dispatcher.h                    \
+       src/gui/dispatcher.cpp                  \
+       src/gui/updater.h                       \
+       src/gui/updater.cpp                     \
+       src/gui/dialogs/keyGrabber.h            \
+       src/gui/dialogs/keyGrabber.cpp          \
+       src/gui/dialogs/about.h                 \
+       src/gui/dialogs/about.cpp               \
+       src/gui/dialogs/mainWindow.h            \
+       src/gui/dialogs/mainWindow.cpp          \
+       src/gui/dialogs/beatsInput.h            \
+       src/gui/dialogs/beatsInput.cpp          \
+       src/gui/dialogs/warnings.h              \
+       src/gui/dialogs/warnings.cpp            \
+       src/gui/dialogs/bpmInput.h              \
+       src/gui/dialogs/bpmInput.cpp            \
+       src/gui/dialogs/channelNameInput.h      \
+       src/gui/dialogs/channelNameInput.cpp    \
+       src/gui/dialogs/config.h                \
+       src/gui/dialogs/config.cpp              \
+       src/gui/dialogs/devInfo.h               \
+       src/gui/dialogs/devInfo.cpp             \
+       src/gui/dialogs/pluginList.h            \
+       src/gui/dialogs/pluginList.cpp          \
+       src/gui/dialogs/pluginWindow.h          \
+       src/gui/dialogs/pluginWindow.cpp        \
+       src/gui/dialogs/sampleEditor.h          \
+       src/gui/dialogs/sampleEditor.cpp        \
+       src/gui/dialogs/pluginWindowGUI.h       \
+       src/gui/dialogs/pluginWindowGUI.cpp     \
+       src/gui/dialogs/pluginChooser.h         \
+       src/gui/dialogs/pluginChooser.cpp       \
        src/gui/dialogs/actionEditor/baseActionEditor.h     \
        src/gui/dialogs/actionEditor/baseActionEditor.cpp   \
        src/gui/dialogs/actionEditor/sampleActionEditor.h   \
@@ -286,12 +291,15 @@ sourcesCore =                            \
        src/utils/vector.h                     \
        src/utils/ver.h                        \
        src/utils/ver.cpp                      \
+       src/utils/json.h                       \
+       src/utils/json.cpp                     \
        src/utils/string.h                     \
        src/utils/string.cpp                   \
        src/deps/rtaudio-mod/RtAudio.h         \
        src/deps/rtaudio-mod/RtAudio.cpp
 sourcesTests =                   \
        tests/main.cpp               \
+       tests/rcuList.cpp            \
        tests/conf.cpp               \
        tests/wave.cpp               \
        tests/waveManager.cpp        \
@@ -378,6 +386,17 @@ ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
 
 endif
 
+if FREEBSD
+
+# Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio.
+cppFlags += -D__LINUX_PULSE__ -D__UNIX_JACK__
+
+ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
+  -lpthread -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
+  -lfreetype
+
+endif
+
 if OSX
 
 sourcesExtra += src/utils/cocoa.mm src/utils/cocoa.h
index 9f772f66ae689836b204f2f115ae7eced03b65b7..7f907aa1addb2998377e0ba4cbfb94c0d1bdee6a 100644 (file)
@@ -4,7 +4,7 @@
 # prereq & init
 
 AC_PREREQ(2.60)
-AC_INIT([giada], [0.15], [giadaloopmachine@gmail.com])
+AC_INIT([giada], [0.16], [giadaloopmachine@gmail.com])
 AC_CONFIG_SRCDIR([src/main.cpp])
 AM_INIT_AUTOMAKE([subdir-objects])
 
@@ -15,26 +15,34 @@ AM_INIT_AUTOMAKE([subdir-objects])
 # Usage: ./configure --target=[windows | linux | osx]
 
 if test "$target" = ""; then
-       AC_MSG_ERROR(["target OS not specified. Please run ./configure --target=<windows | linux | osx>"])
+       AC_MSG_ERROR(["target OS not specified. Please run ./configure --target=<windows | linux | freebsd | osx>"])
 fi
 
 case "$target" in
   linux)
     os=linux
     ;;
+  freebsd)
+    os=freebsd
+    ;;
   windows)
     os=windows
     ;;
   osx)
     os=osx
     ;;
+  freebsd)
+    os=freebsd
+    ;;
   *)
     AC_MSG_ERROR(["Unrecognised target OS: $target"])
     ;;
 esac
 AM_CONDITIONAL(LINUX,   test "x$os" = "xlinux")
+AM_CONDITIONAL(FREEBSD, test "x$os" = "xfreebsd")
 AM_CONDITIONAL(WINDOWS, test "x$os" = "xwindows")
 AM_CONDITIONAL(OSX,     test "x$os" = "xosx")
+AM_CONDITIONAL(FREEBSD, test "x$os" = "xfreebsd")
 
 # ------------------------------------------------------------------------------
 
@@ -186,7 +194,7 @@ AC_LANG_POP
 
 # Check for linux header files.
 
-if test "x$os" = "xlinux"; then
+if test "x$os" = "xlinux" || test "x$os" = "xfreebsd"; then
 
        AC_LANG_PUSH([C++])
        AC_CHECK_HEADER(
index b4ee058ef2f32a3d1cabc9f98f1c4d260ba56bb9..fefcc7cc8b3cc517f42fd9488f556f4b1b75e988 100644 (file)
@@ -38,19 +38,27 @@ namespace m
 {
 struct Action
 {
-    int       id; // For persistence only
-       int       channel;
+       ID        id = 0;  // Invalid
+       ID        channelId;
        Frame     frame;
        MidiEvent event;
-    int       pluginIndex;
-    int       pluginParam;
-       const Action* prev;
-       const Action* next;
-
-    bool isVolumeEnvelope() const
-    { 
-        return event.getStatus() == MidiEvent::ENVELOPE && pluginIndex == -1; 
-    }
+       ID        pluginId    = -1;
+       int       pluginParam = -1;
+       ID        prevId = 0;
+       ID        nextId = 0;
+       
+       const Action* prev = nullptr;
+       const Action* next = nullptr;
+
+       bool isValid() const 
+       {
+               return id != 0;
+       }    
+
+       bool isVolumeEnvelope() const
+       { 
+               return event.getStatus() == MidiEvent::ENVELOPE && pluginId == -1; 
+       }
 };
 
 }} // giada::m::
index e54f24c8a22a7a2cc5ee26a0052f96a0416a3941..ddb9024a76f4ae172d24bb0aa1438a037ddcd450 100644 (file)
@@ -116,7 +116,7 @@ void AudioBuffer::copyFrame(int frame, float* values)
 
 /* -------------------------------------------------------------------------- */
 
-void AudioBuffer::copyData(float* data, int frames, int offset)
+void AudioBuffer::copyData(const float* data, int frames, int offset)
 {
        assert(m_data != nullptr);
        assert(frames <= m_size - offset);
index b8b05198a1c14d139ea814bacebe8ff896128aaa..ab6fcd32971b30fbe8cbcb51c2f5d8a15f24afaa 100644 (file)
@@ -5,6 +5,7 @@
 namespace giada {
 namespace m
 {
+/* TODO - this class needs a serious modern C++ lifting */
 class AudioBuffer
 {
 public:
@@ -38,7 +39,7 @@ public:
        starting from frame 'offset'. It takes for granted that the new data contains 
        the same number of channels than m_channels. */
 
-       void copyData(float* data, int frames, int offset=0);
+       void copyData(const float* data, int frames, int offset=0);
 
        /* copyFrame
        Copies data pointed by 'values' into m_data[frame]. It takes for granted that
diff --git a/src/core/channel.cpp b/src/core/channel.cpp
deleted file mode 100644 (file)
index ae9654a..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 <cstring>
-#include "../utils/log.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "const.h"
-#include "channelManager.h"
-#include "pluginHost.h"
-#include "pluginManager.h"
-#include "plugin.h"
-#include "kernelMidi.h"
-#include "patch.h"
-#include "clock.h"
-#include "wave.h"
-#include "mixer.h"
-#include "mixerHandler.h"
-#include "recorderHandler.h"
-#include "conf.h"
-#include "patch.h"
-#include "waveFx.h"
-#include "midiMapConf.h"
-#include "channel.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace m 
-{
-Channel::Channel(ChannelType type, ChannelStatus status, int bufferSize)
-:      guiChannel     (nullptr),
-       type           (type),
-       status         (status),
-       recStatus      (ChannelStatus::OFF),
-       previewMode    (PreviewMode::NONE),
-       pan            (0.5f),
-       volume         (G_DEFAULT_VOL),
-       armed          (false),
-       key            (0),
-       mute           (false),
-       solo           (false),
-       volume_i       (1.0f),
-       volume_d       (0.0f),
-       hasActions     (false),
-       readActions    (false),
-       midiIn         (true),
-       midiInKeyPress (0x0),
-       midiInKeyRel   (0x0),
-       midiInKill     (0x0),
-       midiInArm      (0x0),
-       midiInVolume   (0x0),
-       midiInMute     (0x0),
-       midiInSolo     (0x0),
-       midiInFilter   (-1),
-       midiOutL       (false),
-       midiOutLplaying(0x0),
-       midiOutLmute   (0x0),
-       midiOutLsolo   (0x0)
-{
-       buffer.alloc(bufferSize, G_MAX_IO_CHANS);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex)
-{
-       key             = src->key;
-       volume          = src->volume;
-       volume_i        = src->volume_i;
-       volume_d        = src->volume_d;
-       name            = src->name;
-       pan             = src->pan;
-       mute            = src->mute;
-       solo            = src->solo;
-       hasActions      = src->hasActions;
-       recStatus       = src->recStatus;
-       midiIn          = src->midiIn;
-       midiInKeyPress  = src->midiInKeyPress;
-       midiInKeyRel    = src->midiInKeyRel;
-       midiInKill      = src->midiInKill;
-       midiInArm       = src->midiInArm;
-       midiInVolume    = src->midiInVolume;
-       midiInMute      = src->midiInMute;
-       midiInSolo      = src->midiInSolo;
-       midiOutL        = src->midiOutL;
-       midiOutLplaying = src->midiOutLplaying;
-       midiOutLmute    = src->midiOutLmute;
-       midiOutLsolo    = src->midiOutLsolo;
-
-#ifdef WITH_VST
-
-       for (const std::unique_ptr<Plugin>& plugin : src->plugins)
-               pluginHost::addPlugin(pluginManager::makePlugin(*plugin.get()), 
-                       pluginHost::StackType::CHANNEL, pluginMutex, this);
-
-#endif
-
-       hasActions = recorderHandler::cloneActions(src->index, index);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Channel::isPlaying() const
-{
-       return status == ChannelStatus::PLAY || status == ChannelStatus::ENDING;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::writePatch(int i, bool isProject)
-{
-       channelManager::writePatch(this, isProject);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::readPatch(const string& path, const patch::channel_t& pch)
-{
-       channelManager::readPatch(this, pch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::sendMidiLmute()
-{
-       if (!midiOutL || midiOutLmute == 0x0)
-               return;
-       if (mute)
-               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOn);
-       else
-               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOff);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::sendMidiLsolo()
-{
-       if (!midiOutL || midiOutLsolo == 0x0)
-               return;
-       if (solo)
-               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOn);
-       else
-               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOff);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::sendMidiLstatus()
-{
-       if (!midiOutL || midiOutLplaying == 0x0)
-               return;
-       switch (status) {
-               case ChannelStatus::OFF:
-                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopped);
-                       break;
-               case ChannelStatus::WAIT:
-                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::waiting);
-                       break;
-               case ChannelStatus::ENDING:
-                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopping);
-                       break;
-               case ChannelStatus::PLAY:
-                       if ((mixer::isChannelAudible(this) && !(this->mute)) || 
-                               !midimap::isDefined(midimap::playing_inaudible))
-                               kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing);
-                       else
-                               kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing_inaudible);
-                       break;
-               default:
-                       break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Channel::isMidiInAllowed(int c) const
-{
-       return midiInFilter == -1 || midiInFilter == c;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::setPan(float v)
-{
-       if (v > 1.0f)
-               pan = 1.0f;
-       else 
-       if (v < 0.0f)
-               pan = 0.0f;
-       else
-               pan = v;
-}
-
-
-float Channel::getPan() const
-{
-       return pan;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Channel::calcPanning(int ch) const
-{
-       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::calcVolumeEnvelope()
-{
-       volume_i += volume_d;
-       if (volume_i < 0.0f)
-               volume_i = 0.0f;
-       else
-       if (volume_i > 1.0f)
-               volume_i = 1.0f;        
-}
-
-
-bool Channel::isPreview() const
-{
-       return previewMode != PreviewMode::NONE;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Channel::isReadingActions() const
-{
-       return hasActions && readActions;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-const juce::MidiBuffer& Channel::getPluginMidiEvents() const
-{
-       return midiBuffer;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::clearMidiBuffer()
-{
-       midiBuffer.clear();
-}
-
-#endif
-
-}} // giada::m::
diff --git a/src/core/channel.h b/src/core/channel.h
deleted file mode 100644 (file)
index 150d7e8..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_CHANNEL_H
-#define G_CHANNEL_H
-
-
-#include <vector>
-#include <string>
-#include <pthread.h>
-#include "types.h"
-#include "patch.h"
-#include "mixer.h"
-#include "midiMapConf.h"
-#include "midiEvent.h"
-#include "recorder.h"
-#include "audioBuffer.h"
-
-#ifdef WITH_VST
-       #include "../deps/juce-config.h"
-       #include "plugin.h"
-#endif
-
-
-class geChannel;
-
-
-namespace giada {
-namespace m
-{
-class Channel
-{
-public:
-
-       virtual ~Channel() {};
-
-       /* copy
-       Makes a shallow copy (no internal buffers allocation) of another channel. */
-
-       virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0;
-
-       /* parseEvents
-       Prepares channel for rendering. This is called on each frame. */
-
-       virtual void parseEvents(mixer::FrameEvents fe) = 0;
-
-       /* process
-       Merges working buffers into 'out', plus plugin processing (if any). Warning:
-       inBuffer might be unallocated if no input devices are available for 
-       recording. */
-
-       virtual void process(AudioBuffer& out, const AudioBuffer& in, bool audible, 
-               bool running) = 0;
-
-       /* start
-       Action to do when channel starts. doQuantize = false (don't quantize)
-       when Mixer is reading actions from Recorder. */
-
-       virtual void start(int localFrame, bool doQuantize, int velocity) = 0;
-
-       /* stop
-       What to do when channel is stopped normally (via key or MIDI). */
-
-       virtual void stop() = 0;
-
-       /* kill
-       What to do when channel stops abruptly. */
-
-       virtual void kill(int localFrame) = 0;
-
-       /* set mute
-       What to do when channel is un/muted. */
-
-       virtual void setMute(bool value) = 0;
-
-       /* set solo
-       What to do when channel is un/soloed. */
-
-       virtual void setSolo(bool value) = 0;
-
-       /* empty
-       Frees any associated resources (e.g. waveform for SAMPLE). */
-
-       virtual void empty() = 0;
-
-       /* stopBySeq
-       What to do when channel is stopped by sequencer. */
-
-       virtual void stopBySeq(bool chansStopOnSeqHalt) = 0;
-
-       /* rewind
-       Rewinds channel when rewind button is pressed. */
-
-       virtual void rewindBySeq() = 0;
-
-       /* canInputRec
-       Tells whether a channel can accept and handle input audio. Always false for
-       Midi channels, true for Sample channels only if they don't contain a
-       sample yet.*/
-
-       virtual bool canInputRec()    const { return false; };
-       virtual bool hasLogicalData() const { return false; };
-       virtual bool hasEditedData()  const { return false; };
-       virtual bool hasData()        const { return false; };
-
-       virtual bool recordStart(bool canQuantize) { return true; };
-       virtual bool recordKill() { return true; };
-       virtual void recordStop() {};
-
-       /* prepareBuffer
-       Fill audio buffer with audio data from the internal source. This is actually 
-       useful to sample channels only. */
-
-       virtual void prepareBuffer(bool running) {};
-
-       virtual void startReadingActions(bool treatRecsAsLoops, 
-               bool recsStopOnChanHalt) {};
-       virtual void stopReadingActions(bool running, bool treatRecsAsLoops, 
-               bool recsStopOnChanHalt) {};
-
-       virtual void stopInputRec(int globalFrame) {};
-       
-       virtual void readPatch(const std::string& basePath, const patch::channel_t& pch);
-       virtual void writePatch(int i, bool isProject);
-
-       /* receiveMidi
-       Receives and processes midi messages from external devices. */
-
-       virtual void receiveMidi(const MidiEvent& midiEvent) {};
-
-       /* calcPanning
-       Given an audio channel (stereo: 0 or 1) computes the current panning value. */
-
-       float calcPanning(int ch) const;
-
-       bool isPlaying() const;
-       float getPan() const;
-       bool isPreview() const;
-
-       /* isMidiInAllowed
-       Given a MIDI channel 'c' tells whether this channel should be allowed to 
-       receive and process MIDI events on MIDI channel 'c'. */
-
-       bool isMidiInAllowed(int c) const;
-
-       /* isReadingActions
-       Tells whether the channel as actions and it is currently reading them. */
-
-       bool isReadingActions() const;
-
-       /* sendMidiL*
-       Sends MIDI lightning events to a physical device. */
-
-       void sendMidiLmute();
-       void sendMidiLsolo();
-       void sendMidiLstatus();
-
-       void setPan(float v);
-
-       void calcVolumeEnvelope();
-
-#ifdef WITH_VST
-
-       /* getPluginMidiEvents
-       Returns a reference to midiBuffer stack. This is available for any kind of
-       channel, but it makes sense only for MIDI channels. */
-
-       const juce::MidiBuffer& getPluginMidiEvents() const;
-
-       void clearMidiBuffer();
-
-#endif
-
-       /* guiChannel
-       Pointer to a gChannel object, part of the GUI. TODO - remove this and send
-       signals instead. */
-
-       geChannel* guiChannel;
-
-       /* buffer
-       Working buffer for internal processing. */
-       
-       AudioBuffer buffer;
-
-       ChannelType   type;
-       ChannelStatus status;
-       ChannelStatus recStatus;
-
-       /* previewMode
-       Whether the channel is in audio preview mode or not. */
-
-       PreviewMode previewMode;
-
-       float       pan;
-       float       volume;   // global volume
-       bool        armed;
-       std::string name;
-       int         index;    // unique id
-       int         key;      // keyboard button
-       bool        mute;     // global mute
-       bool        solo;
-
-       /* volume_*
-       Internal volume variables: volume_i for envelopes, volume_d keeps track of
-       the delta during volume changes (or the line slope between two volume 
-       points). */
-       
-       double volume_i;
-       double volume_d;
-       
-       bool hasActions;      // If has some actions recorded
-       bool readActions;     // If should read recorded actions
-
-       bool      midiIn;               // enable midi input
-       uint32_t  midiInKeyPress;
-       uint32_t  midiInKeyRel;
-       uint32_t  midiInKill;
-       uint32_t  midiInArm;
-       uint32_t  midiInVolume;
-       uint32_t  midiInMute;
-       uint32_t  midiInSolo;
-
-       /* midiInFilter
-       Which MIDI channel should be filtered out when receiving MIDI messages. -1
-       means 'all'. */
-
-       int midiInFilter;
-
-       /*  midiOutL*
-        * Enable MIDI lightning output, plus a set of midi lighting event to be sent
-        * to a device. Those events basically contains the MIDI channel, everything
-        * else gets stripped out. */
-
-       bool     midiOutL;
-       uint32_t midiOutLplaying;
-       uint32_t midiOutLmute;
-       uint32_t midiOutLsolo;
-
-#ifdef WITH_VST
-       std::vector<std::unique_ptr<Plugin>> plugins;
-#endif
-
-protected:
-
-       Channel(ChannelType type, ChannelStatus status, int bufferSize);
-
-#ifdef WITH_VST
-
-       /* 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. */
-
-       juce::MidiBuffer midiBuffer;
-
-#endif
-};
-
-}} // giada::m::
-
-
-#endif
diff --git a/src/core/channelManager.cpp b/src/core/channelManager.cpp
deleted file mode 100644 (file)
index cd64640..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "../utils/fs.h"
-#include "const.h"
-#include "channel.h"
-#include "patch.h"
-#include "mixer.h"
-#include "wave.h"
-#include "waveManager.h"
-#include "sampleChannel.h"
-#include "midiChannel.h"
-#include "pluginHost.h"
-#include "pluginManager.h"
-#include "plugin.h"
-#include "action.h"
-#include "recorderHandler.h"
-#include "channelManager.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace m {
-namespace channelManager
-{
-namespace
-{
-void writePlugins_(const Channel* ch, patch::channel_t& pch)
-{
-#ifdef WITH_VST
-
-       pluginHost::forEachPlugin(pluginHost::StackType::CHANNEL, ch, [&] (const Plugin* p) {
-               patch::plugin_t pp;
-               pp.path   = p->getUniqueId();
-               pp.bypass = p->isBypassed();
-               for (int k=0; k<p->getNumParameters(); k++)
-                       pp.params.push_back(p->getParameter(k));
-               for (uint32_t param : p->midiInParams)
-                       pp.midiInParams.push_back(param);
-               pch.plugins.push_back(pp);
-       });
-
-#endif
-}
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void readActions_(Channel* ch, const patch::channel_t& pch)
-{
-       recorderHandler::readPatch(pch.actions);
-       ch->hasActions = pch.actions.size() > 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void readPlugins_(Channel* ch, const patch::channel_t& pch)
-{
-#ifdef WITH_VST
-
-       for (const patch::plugin_t& ppl : pch.plugins) {
-
-               std::unique_ptr<Plugin> plugin = pluginManager::makePlugin(ppl.path);
-               if (plugin == nullptr)
-                       continue;
-
-               plugin->setBypass(ppl.bypass);
-               for (unsigned j=0; j<ppl.params.size(); j++)
-                       plugin->setParameter(j, ppl.params.at(j));
-
-               /* Don't fill Channel::midiInParam if Patch::midiInParams are 0: it would
-               wipe out the current default 0x0 values. */
-
-               if (!ppl.midiInParams.empty()) {
-                       plugin->midiInParams.clear();
-                       for (uint32_t midiInParam : ppl.midiInParams)
-                               plugin->midiInParams.push_back(midiInParam);
-               }
-
-               pluginHost::addPlugin(std::move(plugin), pluginHost::StackType::CHANNEL, &mixer::mutex, ch);
-       }
-
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-Channel* create(ChannelType type, int bufferSize, bool inputMonitorOn)
-{
-       if (type == ChannelType::SAMPLE)
-               return new SampleChannel(inputMonitorOn, bufferSize);
-       else
-               return new MidiChannel(bufferSize);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int writePatch(const Channel* ch, bool isProject)
-{
-       patch::channel_t pch;
-       pch.type            = static_cast<int>(ch->type);
-       pch.index           = ch->index;
-       pch.size            = ch->guiChannel->getSize();
-       pch.name            = ch->name;
-       pch.key             = ch->key;
-       pch.armed           = ch->armed;
-       pch.column          = ch->guiChannel->getColumnIndex();
-       pch.mute            = ch->mute;
-       pch.solo            = ch->solo;
-       pch.volume          = ch->volume;
-       pch.pan             = ch->pan;
-       pch.midiIn          = ch->midiIn;
-       pch.midiInKeyPress  = ch->midiInKeyPress;
-       pch.midiInKeyRel    = ch->midiInKeyRel;
-       pch.midiInKill      = ch->midiInKill;
-       pch.midiInArm       = ch->midiInArm;
-       pch.midiInVolume    = ch->midiInVolume;
-       pch.midiInMute      = ch->midiInMute;
-       pch.midiInFilter    = ch->midiInFilter;
-       pch.midiInSolo      = ch->midiInSolo;
-       pch.midiOutL        = ch->midiOutL;
-       pch.midiOutLplaying = ch->midiOutLplaying;
-       pch.midiOutLmute    = ch->midiOutLmute;
-       pch.midiOutLsolo    = ch->midiOutLsolo;
-
-       recorderHandler::writePatch(ch->index, pch.actions);
-       writePlugins_(ch, pch);
-
-       patch::channels.push_back(pch);
-
-       return patch::channels.size() - 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void writePatch(const MidiChannel* ch, bool isProject, int index)
-{
-       patch::channel_t& pch = patch::channels.at(index);
-       pch.midiOut     = ch->midiOut;
-       pch.midiOutChan = ch->midiOutChan;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void writePatch(const SampleChannel* ch, bool isProject, int index)
-{
-       patch::channel_t& pch = patch::channels.at(index);
-
-       if (ch->wave != nullptr) {
-               pch.samplePath = ch->wave->getPath();
-               if (isProject)
-                       pch.samplePath = gu_basename(ch->wave->getPath());  // make it portable
-       }
-       else
-               pch.samplePath = "";
-
-       pch.mode              = static_cast<int>(ch->mode);
-       pch.begin             = ch->getBegin();
-       pch.end               = ch->getEnd();
-       pch.boost             = ch->getBoost();
-       pch.readActions       = ch->readActions;
-       pch.pitch             = ch->getPitch();
-       pch.inputMonitor      = ch->inputMonitor;
-       pch.midiInReadActions = ch->midiInReadActions;
-       pch.midiInPitch       = ch->midiInPitch;        
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void readPatch(Channel* ch, const patch::channel_t& pch)
-{
-       ch->key             = pch.key;
-       ch->armed           = pch.armed;
-       ch->type            = static_cast<ChannelType>(pch.type);
-       ch->name            = pch.name;
-       ch->index           = pch.index;
-       ch->mute            = pch.mute;
-       ch->solo            = pch.solo;
-       ch->volume          = pch.volume;
-       ch->pan             = pch.pan;
-       ch->midiIn          = pch.midiIn;
-       ch->midiInKeyPress  = pch.midiInKeyPress;
-       ch->midiInKeyRel    = pch.midiInKeyRel;
-       ch->midiInKill      = pch.midiInKill;
-       ch->midiInVolume    = pch.midiInVolume;
-       ch->midiInMute      = pch.midiInMute;
-       ch->midiInFilter    = pch.midiInFilter;
-       ch->midiInSolo      = pch.midiInSolo;
-       ch->midiOutL        = pch.midiOutL;
-       ch->midiOutLplaying = pch.midiOutLplaying;
-       ch->midiOutLmute    = pch.midiOutLmute;
-       ch->midiOutLsolo    = pch.midiOutLsolo;
-
-       readActions_(ch, pch);
-       readPlugins_(ch, pch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void readPatch(SampleChannel* ch, const string& basePath, const patch::channel_t& pch)
-{
-       ch->mode              = static_cast<ChannelMode>(pch.mode);
-       ch->readActions       = pch.readActions;
-       ch->recStatus         = pch.readActions ? ChannelStatus::PLAY : ChannelStatus::OFF;
-       ch->midiInVeloAsVol   = pch.midiInVeloAsVol;
-       ch->midiInReadActions = pch.midiInReadActions;
-       ch->midiInPitch       = pch.midiInPitch;
-       ch->inputMonitor      = pch.inputMonitor;
-       ch->setBoost(pch.boost);
-
-       waveManager::Result res = waveManager::createFromFile(basePath + pch.samplePath);
-
-       if (res.status == G_RES_OK) {
-               ch->pushWave(std::move(res.wave));
-               ch->setBegin(pch.begin);
-               ch->setEnd(pch.end);
-               ch->setPitch(pch.pitch);
-       }
-       else {
-               if (res.status == G_RES_ERR_NO_DATA)
-                       ch->status = ChannelStatus::EMPTY;
-               else
-               if (res.status == G_RES_ERR_IO)
-                       ch->status = ChannelStatus::MISSING;
-               ch->sendMidiLstatus();  // FIXME - why sending MIDI lightning if sample status is wrong?
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void readPatch(MidiChannel* ch, const patch::channel_t& pch)
-{
-       ch->midiOut     = pch.midiOut;
-       ch->midiOutChan = pch.midiOutChan;      
-}
-}}}; // giada::m::channelManager
diff --git a/src/core/channelManager.h b/src/core/channelManager.h
deleted file mode 100644 (file)
index d3d7c6b..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_CHANNEL_MANAGER_H
-#define G_CHANNEL_MANAGER_H
-
-
-#include <string>
-#include "types.h"
-
-
-namespace giada {
-namespace m 
-{
-class Channel;
-class SampleChannel;
-class MidiChannel;
-
-namespace patch
-{
-struct channel_t;
-}
-namespace channelManager
-{
-Channel* create(ChannelType type, int bufferSize, bool inputMonitorOn);
-
-int  writePatch(const Channel* ch, bool isProject);
-void writePatch(const SampleChannel* ch, bool isProject, int index);
-void writePatch(const MidiChannel* ch, bool isProject, int index);
-
-void readPatch(Channel* ch, const patch::channel_t& pch);
-void readPatch(SampleChannel* ch, const std::string& basePath, const patch::channel_t& pch);
-void readPatch(MidiChannel* ch, const patch::channel_t& pch);
-}}}; // giada::m::channelManager
-
-
-#endif
\ No newline at end of file
diff --git a/src/core/channels/channel.cpp b/src/core/channels/channel.cpp
new file mode 100644 (file)
index 0000000..86e6a97
--- /dev/null
@@ -0,0 +1,315 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/log.h"
+#include "core/channels/channelManager.h"
+#include "core/const.h"
+#include "core/pluginManager.h"
+#include "core/plugin.h"
+#include "core/kernelMidi.h"
+#include "core/patch.h"
+#include "core/clock.h"
+#include "core/wave.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/recorderHandler.h"
+#include "core/conf.h"
+#include "core/patch.h"
+#include "core/waveFx.h"
+#include "core/midiMapConf.h"
+#include "channel.h"
+
+
+namespace giada {
+namespace m 
+{
+Channel::Channel(ChannelType type, ChannelStatus playStatus, int bufferSize, 
+       ID columnId, ID id)
+: type           (type),
+  playStatus     (playStatus),
+  recStatus      (ChannelStatus::OFF),
+  columnId       (columnId),
+  id             (id),
+  previewMode    (PreviewMode::NONE),
+  pan            (0.5f),
+  volume         (G_DEFAULT_VOL),
+  armed          (false),
+  key            (0),
+  mute           (false),
+  solo           (false),
+  volume_i       (1.0f),
+  volume_d       (0.0f),
+  hasActions     (false),
+  readActions    (false),
+  midiIn         (true),
+  midiInKeyPress (0x0),
+  midiInKeyRel   (0x0),
+  midiInKill     (0x0),
+  midiInArm      (0x0),
+  midiInVolume   (0x0),
+  midiInMute     (0x0),
+  midiInSolo     (0x0),
+  midiInFilter   (-1),
+  midiOutL       (false),
+  midiOutLplaying(0x0),
+  midiOutLmute   (0x0),
+  midiOutLsolo   (0x0)
+{
+       buffer.alloc(bufferSize, G_MAX_IO_CHANS);
+
+#ifdef WITH_VST
+
+       midiBuffer.ensureSize(bufferSize);
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel::Channel(const Channel& o)
+: type           (o.type),
+  playStatus     (o.playStatus),
+  recStatus      (o.recStatus),
+  columnId       (o.columnId),
+  id             (o.id),
+  previewMode    (o.previewMode),
+  pan            (o.pan),
+  volume         (o.volume),
+  armed          (o.armed),
+  name           (o.name),
+  key            (o.key),
+  mute           (o.mute),
+  solo           (o.solo),
+  volume_i       (o.volume_i.load()),
+  volume_d       (o.volume_d),
+  hasActions     (o.hasActions),
+  readActions    (o.readActions),
+  midiIn         (o.midiIn.load()),
+  midiInKeyPress (o.midiInKeyPress.load()),
+  midiInKeyRel   (o.midiInKeyRel.load()),
+  midiInKill     (o.midiInKill.load()),
+  midiInArm      (o.midiInArm.load()),
+  midiInVolume   (o.midiInVolume.load()),
+  midiInMute     (o.midiInMute.load()),
+  midiInSolo     (o.midiInSolo.load()),
+  midiInFilter   (o.midiInFilter.load()),
+  midiOutL       (o.midiOutL.load()),
+  midiOutLplaying(o.midiOutLplaying.load()),
+  midiOutLmute   (o.midiOutLmute.load()),
+  midiOutLsolo   (o.midiOutLsolo.load())
+#ifdef WITH_VST
+ ,pluginIds      (o.pluginIds)
+#endif
+{
+       buffer.alloc(o.buffer.countFrames(), G_MAX_IO_CHANS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel::Channel(const patch::Channel& p, int bufferSize)
+: type           (p.type),
+  playStatus     (p.waveId == 0 ? ChannelStatus::EMPTY : ChannelStatus::OFF),
+  recStatus      (ChannelStatus::OFF),
+  columnId       (p.columnId),
+  id             (p.id),
+  previewMode    (PreviewMode::NONE),
+  pan            (p.pan),
+  volume         (p.volume),
+  armed          (p.armed),
+  name           (p.name),
+  key            (p.key),
+  mute           (p.mute),
+  solo           (p.solo),
+  volume_i       (1.0),
+  volume_d       (0.0),
+  hasActions     (p.hasActions),
+  readActions    (p.readActions),
+  midiIn         (p.midiIn),
+  midiInKeyPress (p.midiInKeyPress),
+  midiInKeyRel   (p.midiInKeyRel),
+  midiInKill     (p.midiInKill),
+  midiInArm      (p.midiInArm),
+  midiInVolume   (p.midiInVolume),
+  midiInMute     (p.midiInMute),
+  midiInSolo     (p.midiInSolo),
+  midiInFilter   (p.midiInFilter),
+  midiOutL       (p.midiOutL),
+  midiOutLplaying(p.midiOutLplaying),
+  midiOutLmute   (p.midiOutLmute),
+  midiOutLsolo   (p.midiOutLsolo)
+#ifdef WITH_VST
+ ,pluginIds      (p.pluginIds)
+#endif
+{
+       buffer.alloc(bufferSize, G_MAX_IO_CHANS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::isPlaying() const
+{
+       return playStatus == ChannelStatus::PLAY || 
+              playStatus == ChannelStatus::ENDING;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::sendMidiLmute()
+{
+       if (!midiOutL || midiOutLmute == 0x0)
+               return;
+       if (mute)
+               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOn);
+       else
+               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOff);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::sendMidiLsolo()
+{
+       if (!midiOutL || midiOutLsolo == 0x0)
+               return;
+       if (solo)
+               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOn);
+       else
+               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOff);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::sendMidiLstatus()
+{
+       if (!midiOutL || midiOutLplaying == 0x0)
+               return;
+       switch (playStatus) {
+               case ChannelStatus::OFF:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopped);
+                       break;
+               case ChannelStatus::WAIT:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::waiting);
+                       break;
+               case ChannelStatus::ENDING:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopping);
+                       break;
+               case ChannelStatus::PLAY:
+                       if ((mixer::isChannelAudible(this) && !mute) || 
+                               !midimap::isDefined(midimap::playingInaudible))
+                               kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing);
+                       else
+                               kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playingInaudible);
+                       break;
+               default:
+                       break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::isMidiInAllowed(int c) const
+{
+       return midiInFilter == -1 || midiInFilter == c;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::setPan(float v)
+{
+       if (v > 1.0f) v = 1.0f;
+       else 
+       if (v < 0.0f) v = 0.0f;
+       pan = v;
+}
+
+
+float Channel::getPan() const
+{
+       return pan;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Channel::calcPanning(int ch) const
+{      
+       float p = pan;
+       if (p  == 0.5f) // center: nothing to do
+               return 1.0;
+       if (ch == 0)
+               return 1.0 - p;
+       else  // channel 1
+               return p; 
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::calcVolumeEnvelope()
+{
+       volume_i = volume_i + volume_d;
+       if (volume_i < 0.0f)
+               volume_i = 0.0f;
+       else
+       if (volume_i > 1.0f)
+               volume_i = 1.0f;        
+}
+
+
+bool Channel::isPreview() const
+{
+       return previewMode != PreviewMode::NONE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::isReadingActions() const
+{
+       return hasActions && readActions;
+}
+
+}} // giada::m::
diff --git a/src/core/channels/channel.h b/src/core/channels/channel.h
new file mode 100644 (file)
index 0000000..493f398
--- /dev/null
@@ -0,0 +1,273 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_CHANNEL_H
+#define G_CHANNEL_H
+
+
+#include <vector>
+#include <string>
+#include "core/types.h"
+#include "core/patch.h"
+#include "core/mixer.h"
+#include "core/midiMapConf.h"
+#include "core/midiEvent.h"
+#include "core/recorder.h"
+#include "core/audioBuffer.h"
+#ifdef WITH_VST
+#include "deps/juce-config.h"
+#include "core/plugin.h"
+#include "core/pluginHost.h"
+#include "core/queue.h"
+#endif
+
+
+namespace giada {
+namespace m
+{
+class Channel
+{
+public:
+
+       virtual ~Channel() {};
+
+       /* clone
+       A trick to give the caller the ability to invoke the derived class copy
+       constructor given the base. TODO - This thing will go away with the Channel
+       "no-virtual inheritance" refactoring. */
+
+       virtual Channel* clone() const = 0;
+
+       /* load
+       Loads persistence data into an existing channel. Used for built-in channels
+       such as masters and preview. */
+
+       virtual void load(const patch::Channel& p) {}
+
+       /* parseEvents
+       Prepares channel for rendering. This is called on each frame. */
+
+       virtual void parseEvents(mixer::FrameEvents fe) {};
+
+       /* render
+       Audio rendering. Warning: inBuffer might be unallocated if no input devices 
+       are available for recording. */
+
+       virtual void render(AudioBuffer& out, const AudioBuffer& in, 
+               AudioBuffer& inToOut, bool audible, bool running) {};
+
+       /* start
+       Action to do when channel starts. doQuantize = false (don't quantize)
+       when Mixer is reading actions from Recorder. */
+
+       virtual void start(int localFrame, bool doQuantize, int velocity) {};
+
+       /* stop
+       What to do when channel is stopped normally (via key or MIDI). */
+
+       virtual void stop() {};
+
+       /* kill
+       What to do when channel stops abruptly. */
+
+       virtual void kill(int localFrame) {};
+
+       /* set mute
+       What to do when channel is un/muted. */
+
+       virtual void setMute(bool value) {};
+
+       /* set solo
+       What to do when channel is un/soloed. */
+
+       virtual void setSolo(bool value) {};
+
+       /* empty
+       Frees any associated resources (e.g. waveform for SAMPLE). */
+
+       virtual void empty() {};
+
+       /* stopBySeq
+       What to do when channel is stopped by sequencer. */
+
+       virtual void stopBySeq(bool chansStopOnSeqHalt) {};
+
+       /* rewind
+       Rewinds channel when rewind button is pressed. */
+
+       virtual void rewindBySeq() {};
+
+       /* canInputRec
+       Tells whether a channel can accept and handle input audio. Always false for
+       Midi channels, true for Sample channels only if they don't contain a
+       sample yet.*/
+
+       virtual bool canInputRec()    const { return false; };
+       virtual bool hasLogicalData() const { return false; };
+       virtual bool hasEditedData()  const { return false; };
+       virtual bool hasData()        const { return false; };
+
+       virtual bool recordStart(bool canQuantize) { return true; };
+       virtual bool recordKill() { return true; };
+       virtual void recordStop() {};
+
+       virtual void startReadingActions(bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt) {};
+       virtual void stopReadingActions(bool running, bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt) {};
+
+       virtual void stopInputRec(int globalFrame) {};
+
+       /* receiveMidi
+       Receives and processes midi messages from external devices. */
+
+       virtual void receiveMidi(const MidiEvent& midiEvent) {};
+
+       /* calcPanning
+       Given an audio channel (stereo: 0 or 1) computes the current panning value. */
+
+       float calcPanning(int ch) const;
+
+       bool isPlaying() const;
+       float getPan() const;
+       bool isPreview() const;
+
+       /* isMidiInAllowed
+       Given a MIDI channel 'c' tells whether this channel should be allowed to 
+       receive and process MIDI events on MIDI channel 'c'. */
+
+       bool isMidiInAllowed(int c) const;
+
+       /* isReadingActions
+       Tells whether the channel as actions and it is currently reading them. */
+
+       bool isReadingActions() const;
+
+       /* sendMidiL*
+       Sends MIDI lightning events to a physical device. */
+
+       void sendMidiLmute();
+       void sendMidiLsolo();
+       void sendMidiLstatus();
+
+       void setPan(float v);
+
+       void calcVolumeEnvelope();
+
+       /* buffer
+       Working buffer for internal processing. */
+       
+       AudioBuffer buffer;
+
+       ChannelType   type;
+       ChannelStatus playStatus;
+       ChannelStatus recStatus;
+
+       ID columnId;
+       ID id;
+
+       /* previewMode
+       Whether the channel is in audio preview mode or not. */
+
+       PreviewMode previewMode;
+
+       float pan;
+       float volume;   // global volume
+       bool armed;
+       std::string name;
+       int  key;
+       bool mute;
+       bool solo;
+
+       /* volume_*
+       Internal volume variables: volume_i for envelopes, volume_d keeps track of
+       the delta during volume changes (or the line slope between two volume 
+       points). */
+       
+       std::atomic<double> volume_i;
+       double volume_d;
+       
+       bool hasActions;  // If has some actions recorded
+       bool readActions; // If should read recorded actions
+
+       std::atomic<bool>     midiIn;               // enable midi input
+       std::atomic<uint32_t> midiInKeyPress;
+       std::atomic<uint32_t> midiInKeyRel;
+       std::atomic<uint32_t> midiInKill;
+       std::atomic<uint32_t> midiInArm;
+       std::atomic<uint32_t> midiInVolume;
+       std::atomic<uint32_t> midiInMute;
+       std::atomic<uint32_t> midiInSolo;
+
+       /* midiInFilter
+       Which MIDI channel should be filtered out when receiving MIDI messages. -1
+       means 'all'. */
+
+       std::atomic<int> midiInFilter;
+
+       /*  midiOutL*
+       Enables MIDI lightning output, plus a set of midi lighting event to be sent
+       to a device. Those events basically contains the MIDI channel, everything
+       else gets stripped out. */
+
+       std::atomic<bool>     midiOutL;
+       std::atomic<uint32_t> midiOutLplaying;
+       std::atomic<uint32_t> midiOutLmute;
+       std::atomic<uint32_t> midiOutLsolo;
+
+#ifdef WITH_VST
+
+       std::vector<ID> pluginIds;
+
+       /* 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. */
+       
+       juce::MidiBuffer midiBuffer;
+
+       /* midiQueue
+       FIFO queue for collecting MIDI events from the MIDI thread and passing them
+       to the audio thread. */
+       /* TODO - magic number */
+
+       Queue<MidiEvent, 32> midiQueue;
+
+#endif
+
+protected:
+
+       Channel(ChannelType type, ChannelStatus status, int bufferSize,
+               ID columnId, ID id);
+       Channel(const Channel& o);
+       Channel(const patch::Channel& p, int bufferSize);
+};
+
+}} // giada::m::
+
+
+#endif
diff --git a/src/core/channels/channelManager.cpp b/src/core/channels/channelManager.cpp
new file mode 100644 (file)
index 0000000..b5cf8f7
--- /dev/null
@@ -0,0 +1,138 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/fs.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/channels/masterChannel.h"
+#include "core/channels/channel.h"
+#include "core/const.h"
+#include "core/patch.h"
+#include "core/mixer.h"
+#include "core/idManager.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include "core/pluginHost.h"
+#include "core/pluginManager.h"
+#include "core/plugin.h"
+#include "core/action.h"
+#include "core/recorderHandler.h"
+#include "channelManager.h"
+
+
+namespace giada {
+namespace m {
+namespace channelManager
+{
+namespace
+{
+IdManager channelId_;
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init()
+{
+       channelId_ = IdManager();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Channel> create(ChannelType type, int bufferSize,
+       bool inputMonitorOn, ID columnId)
+{
+       std::unique_ptr<Channel> ch = nullptr;
+
+       if (type == ChannelType::SAMPLE)
+               ch = std::make_unique<SampleChannel>(inputMonitorOn, bufferSize, columnId, channelId_.get());
+       else
+       if (type == ChannelType::MIDI)
+               ch = std::make_unique<MidiChannel>(bufferSize, columnId, channelId_.get());
+       else
+       if (type == ChannelType::MASTER)
+               ch = std::make_unique<MasterChannel>(bufferSize, channelId_.get());
+       else
+       if (type == ChannelType::PREVIEW)
+               ch = std::make_unique<MasterChannel>(bufferSize, channelId_.get()); // TODO - temporary placeholder
+       
+       assert(ch != nullptr);
+       return ch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Channel> create(const Channel& o)
+{
+       std::unique_ptr<Channel> ch = nullptr;
+       
+       if (o.type == ChannelType::SAMPLE)
+               ch = std::make_unique<SampleChannel>(static_cast<const SampleChannel&>(o));
+       else
+       if (o.type == ChannelType::MIDI)
+               ch = std::make_unique<MidiChannel>(static_cast<const MidiChannel&>(o));
+       else
+       if (o.type == ChannelType::MASTER)
+               ch = std::make_unique<MasterChannel>(static_cast<const MasterChannel&>(o));
+
+       assert(ch != nullptr);
+
+       if (o.type != ChannelType::MASTER)
+               ch->id = channelId_.get();
+
+       return ch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Channel> create(const patch::Channel& pch, int bufferSize)
+{
+       std::unique_ptr<Channel> ch = nullptr;
+
+       if (pch.type == ChannelType::SAMPLE)
+               ch = std::make_unique<SampleChannel>(pch, bufferSize);
+       else
+       if (pch.type == ChannelType::MIDI)
+               ch = std::make_unique<MidiChannel>(pch, bufferSize);
+
+       assert(ch != nullptr);
+
+       channelId_.set(pch.id);
+
+       return ch;
+}
+}}}; // giada::m::channelManager
diff --git a/src/core/channels/channelManager.h b/src/core/channels/channelManager.h
new file mode 100644 (file)
index 0000000..7b9e5b8
--- /dev/null
@@ -0,0 +1,71 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_CHANNEL_MANAGER_H
+#define G_CHANNEL_MANAGER_H
+
+
+#include <memory>
+#include "core/types.h"
+
+
+namespace giada {
+namespace m 
+{
+namespace patch
+{
+struct Channel;
+}
+class Channel;
+class SampleChannel;
+class MidiChannel;
+namespace channelManager
+{
+/* init
+Initializes internal data. */
+       
+void init();
+
+/* create (1)
+Creates a new Channel from scratch. */
+
+std::unique_ptr<Channel> create(ChannelType type, int bufferSize,
+       bool inputMonitorOn, ID columnId);
+
+/* create (2)
+Creates a new Channel given an existing one (i.e. clone). */
+
+std::unique_ptr<Channel> create(const Channel& ch);
+
+/* create (3)
+Creates a new Channel out of a patch::Channel. */
+
+std::unique_ptr<Channel> create(const patch::Channel& c, int bufferSize);
+}}}; // giada::m::channelManager
+
+
+#endif
diff --git a/src/core/channels/masterChannel.cpp b/src/core/channels/masterChannel.cpp
new file mode 100644 (file)
index 0000000..cb5959c
--- /dev/null
@@ -0,0 +1,87 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "masterChannel.h"
+
+
+namespace giada {
+namespace m 
+{
+MasterChannel::MasterChannel(int bufferSize, ID id)
+: Channel(ChannelType::MASTER, ChannelStatus::OFF, bufferSize, 0, id)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MasterChannel::MasterChannel(const patch::Channel& p, int bufferSize)
+: Channel(p, bufferSize)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MasterChannel* MasterChannel::clone() const
+{
+       return new MasterChannel(*this);
+}
+
+       
+/* -------------------------------------------------------------------------- */
+
+
+void MasterChannel::load(const patch::Channel& p)
+{
+       volume    = p.volume;
+#ifdef WITH_VST
+    pluginIds = p.pluginIds;
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MasterChannel::render(AudioBuffer& out, const AudioBuffer& in, 
+       AudioBuffer& inToOut, bool audible, bool running)
+{
+#ifdef WITH_VST
+       if (pluginIds.size() == 0)
+               return;
+       if (id == mixer::MASTER_OUT_CHANNEL_ID)
+               pluginHost::processStack(out, pluginIds);
+       else
+       if (id == mixer::MASTER_IN_CHANNEL_ID)
+               pluginHost::processStack(inToOut, pluginIds);
+#endif
+}
+
+}} // giada::m::
diff --git a/src/core/channels/masterChannel.h b/src/core/channels/masterChannel.h
new file mode 100644 (file)
index 0000000..f97f7c6
--- /dev/null
@@ -0,0 +1,64 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_MASTER_CHANNEL_H
+#define G_MASTER_CHANNEL_H
+
+
+#include "core/channels/channel.h"
+
+
+namespace giada {
+namespace m 
+{
+class MasterChannel : public Channel
+{
+public:
+
+       MasterChannel(int bufferSize, ID id);
+       MasterChannel(const patch::Channel& p, int bufferSize);
+
+       MasterChannel* clone() const override;
+       void load(const patch::Channel& p) override;
+       void parseEvents(mixer::FrameEvents fe) override {};
+       void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut, 
+               bool audible, bool running) override;
+       void start(int frame, bool doQuantize, int velocity) override {};
+       void kill(int localFrame) override {};
+       void empty() override {};
+       void stopBySeq(bool chansStopOnSeqHalt) override {};
+       void stop() override {};
+       void rewindBySeq() override {};
+       void setMute(bool value) override {};
+       void setSolo(bool value) override {};
+       void receiveMidi(const MidiEvent& midiEvent) override {};
+};
+
+}} // giada::m::
+
+
+#endif
diff --git a/src/core/channels/midiChannel.cpp b/src/core/channels/midiChannel.cpp
new file mode 100644 (file)
index 0000000..02f511a
--- /dev/null
@@ -0,0 +1,226 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/log.h"
+#include "core/channels/midiChannelProc.h"
+#include "core/channels/channelManager.h"
+#include "core/channels/channel.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/action.h"
+#include "core/patch.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/mixer.h"
+#include "core/pluginHost.h"
+#include "core/kernelMidi.h"
+#include "midiChannel.h"
+
+
+namespace giada {
+namespace m 
+{
+MidiChannel::MidiChannel(int bufferSize, ID columnId, ID id)
+: Channel    (ChannelType::MIDI, ChannelStatus::OFF, bufferSize, columnId, id),
+  midiOut    (false),
+  midiOutChan(G_MIDI_CHANS[0])
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiChannel::MidiChannel(const MidiChannel& o)
+: Channel    (o),
+  midiOut    (o.midiOut),
+  midiOutChan(o.midiOutChan)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiChannel::MidiChannel(const patch::Channel& p, int bufferSize)
+: Channel    (p, bufferSize),
+  midiOut    (p.midiOut),
+  midiOutChan(p.midiOutChan)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiChannel* MidiChannel::clone() const
+{
+       return new MidiChannel(*this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::parseEvents(mixer::FrameEvents fe)
+{
+       midiChannelProc::parseEvents(this, fe);
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::render(AudioBuffer& out, const AudioBuffer& in, 
+       AudioBuffer& inToOut, bool audible, bool running)
+{
+       midiChannelProc::process(this, out, in, audible);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::stopBySeq(bool chansStopOnSeqHalt)
+{
+       midiChannelProc::stopBySeq(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::start(int frame, bool doQuantize, int velocity)
+{
+       midiChannelProc::start(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::kill(int localFrame)
+{
+       midiChannelProc::kill(this, localFrame);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::rewindBySeq()
+{
+       midiChannelProc::rewindBySeq(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::setMute(bool value)
+{
+       midiChannelProc::setMute(this, value);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::setSolo(bool value)
+{
+       midiChannelProc::setSolo(this, value);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::empty()
+{
+       hasActions = false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::sendMidi(const MidiEvent& e, int localFrame)
+{
+       if (midiOut) {
+               MidiEvent e_ = e;
+               e_.setChannel(midiOutChan);
+               kernelMidi::send(e_.getRaw());
+       }
+
+#ifdef WITH_VST
+
+       /* Enqueue this MIDI event for plug-ins processing. Will be read and
+       rendered later on by the audio thread. */
+
+       MidiEvent e_ = e;
+       e_.setDelta(localFrame);
+       midiQueue.push(e_);
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
+{
+       namespace mrh = m::recorderHandler;
+       namespace mr  = m::recorder;
+
+       if (!armed)
+               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. */
+
+       MidiEvent midiEventFlat(midiEvent);
+       midiEventFlat.setChannel(0);
+
+#ifdef WITH_VST
+
+       /* Enqueue this MIDI event for plug-ins processing. Will be read and
+       rendered later on by the audio thread. */
+
+       midiQueue.push(midiEventFlat);
+
+#endif
+
+       if (recManager::isRecordingAction()) {
+               mrh::liveRec(id, midiEventFlat);
+               hasActions = true;
+       }
+}
+
+}} // giada::m::
diff --git a/src/core/channels/midiChannel.h b/src/core/channels/midiChannel.h
new file mode 100644 (file)
index 0000000..cd5810f
--- /dev/null
@@ -0,0 +1,75 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_MIDI_CHANNEL_H
+#define G_MIDI_CHANNEL_H
+
+
+#ifdef WITH_VST
+#include "deps/juce-config.h"
+#endif
+#include "core/channels/channel.h"
+
+
+namespace giada {
+namespace m 
+{
+class MidiChannel : public Channel
+{
+public:
+
+       MidiChannel(int bufferSize, ID columnId, ID id);
+       MidiChannel(const MidiChannel& o);
+       MidiChannel(const patch::Channel& p, int bufferSize);
+
+       MidiChannel* clone() const override;
+       void parseEvents(mixer::FrameEvents fe) override;
+       void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut, 
+               bool audible, bool running) override;
+       void start(int frame, bool doQuantize, int velocity) override;
+       void kill(int localFrame) override;
+       void empty() override;
+       void stopBySeq(bool chansStopOnSeqHalt) override;
+       void stop() override {};
+       void rewindBySeq() override;
+       void setMute(bool value) override;
+       void setSolo(bool value) override;
+       void receiveMidi(const MidiEvent& midiEvent) override;
+
+       /* sendMidi
+       Sends Midi event to the outside world. */
+
+       void sendMidi(const MidiEvent& e, int localFrame);
+       
+       bool midiOut;      // enable midi output
+       int  midiOutChan;  // midi output channel
+};
+
+}} // giada::m::
+
+
+#endif
diff --git a/src/core/channels/midiChannelProc.cpp b/src/core/channels/midiChannelProc.cpp
new file mode 100644 (file)
index 0000000..f3ba8fe
--- /dev/null
@@ -0,0 +1,215 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "core/channels/midiChannel.h"
+#include "core/model/model.h"
+#include "core/pluginHost.h"
+#include "core/kernelMidi.h"
+#include "core/const.h"
+#include "core/action.h"
+#include "core/mixerHandler.h"
+#include "midiChannelProc.h"
+
+
+namespace giada {
+namespace m {
+namespace midiChannelProc
+{
+namespace
+{
+void onFirstBeat_(MidiChannel* ch)
+{
+       if (ch->playStatus == ChannelStatus::ENDING) {
+               ch->playStatus = ChannelStatus::OFF;
+               ch->sendMidiLstatus();
+       }
+       else
+       if (ch->playStatus == ChannelStatus::WAIT) {
+               ch->playStatus = ChannelStatus::PLAY;
+               ch->sendMidiLstatus();
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendAllNotesOff_(MidiChannel* ch)
+{
+       MidiEvent e(MIDI_ALL_NOTES_OFF);
+       ch->sendMidi(e, /*localFrame=*/0);
+
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents(MidiChannel* ch, mixer::FrameEvents fe)
+{
+       if (fe.onFirstBeat)
+               onFirstBeat_(ch);
+       if (fe.actions != nullptr)
+               for (const Action& action : *fe.actions)
+                       if (action.channelId == ch->id && ch->isPlaying() && !ch->mute)
+                               ch->sendMidi(action.event, fe.frameLocal);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible)
+{
+#ifdef WITH_VST
+
+       ch->midiBuffer.clear();
+       
+       /* Fill the MIDI buffer vector with messages coming from the MIDI queue
+       filled by the MIDI thread. This is for live events, e.g. piano keyboards,
+       controllers, ... */
+
+       MidiEvent e;
+       while (ch->midiQueue.pop(e)) {
+               juce::MidiMessage message = juce::MidiMessage(
+                       e.getStatus(), 
+                       e.getNote(), 
+                       e.getVelocity());
+               ch->midiBuffer.addEvent(message, e.getDelta());
+       }
+       pluginHost::processStack(ch->buffer, ch->pluginIds, &ch->midiBuffer);
+       
+       /* Process the plugin stack first, then quit if the channel is muted/soloed. 
+       This way there's no risk of cutting midi event pairs such as note-on and 
+       note-off while triggering a mute/solo. */
+
+       if (!audible)
+               return;
+
+       for (int i=0; i<out.countFrames(); i++)
+               for (int j=0; j<out.countChannels(); j++)
+                       out[i][j] += ch->buffer[i][j] * ch->volume;     
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void start(MidiChannel* ch)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::PLAY:
+                       ch->playStatus = ChannelStatus::ENDING;
+                       ch->sendMidiLstatus();
+                       break;
+
+               case ChannelStatus::ENDING:
+               case ChannelStatus::WAIT:
+                       ch->playStatus = ChannelStatus::OFF;
+                       ch->sendMidiLstatus();
+                       break;
+
+               case ChannelStatus::OFF:
+                       ch->playStatus = ChannelStatus::WAIT;
+                       ch->sendMidiLstatus();
+                       break;
+
+               default: break;
+       }       
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void kill(MidiChannel* ch, int localFrame)
+{
+       if (ch->isPlaying())
+               sendAllNotesOff_(ch);
+
+       ch->playStatus = ChannelStatus::OFF;
+       ch->sendMidiLstatus();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindBySeq(MidiChannel* ch)
+{
+       sendAllNotesOff_(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setMute(MidiChannel* ch, bool v)
+{
+       ch->mute = v;
+       if (v)
+               sendAllNotesOff_(ch);
+
+       // This is for processing playing_inaudible
+       ch->sendMidiLstatus();  
+
+       ch->sendMidiLmute();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setSolo(MidiChannel* ch, bool v)
+{
+       ch->solo = v;
+       mh::updateSoloCount();
+
+       // This is for processing playing_inaudible
+       // TODO
+       //for (std::unique_ptr<Channel>& c : model::getLayout()->channels)
+       //      c->sendMidiLstatus();
+
+       ch->sendMidiLsolo();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopBySeq(MidiChannel* ch)
+{      
+       sendAllNotesOff_(ch);
+       kill(ch, 0);
+}
+}}};
diff --git a/src/core/channels/midiChannelProc.h b/src/core/channels/midiChannelProc.h
new file mode 100644 (file)
index 0000000..def2e22
--- /dev/null
@@ -0,0 +1,78 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_MIDI_CHANNEL_PROC_H
+#define G_MIDI_CHANNEL_PROC_H
+
+
+#include "core/mixer.h"
+#include "core/audioBuffer.h"
+
+
+namespace giada {
+namespace m 
+{
+class MidiChannel;
+namespace midiChannelProc
+{
+/* parseEvents
+Parses events gathered by Mixer::masterPlay(). */
+
+void parseEvents(MidiChannel* ch, mixer::FrameEvents ev);
+
+/**/
+void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible);
+
+/* kill
+Stops a channel abruptly. */
+
+void kill(MidiChannel* ch, int localFrame);
+
+/* start
+Starts a channel. */
+
+void start(MidiChannel* ch);
+
+/* stopBySeq
+Stops a channel when the stop button on main transport is pressed. */
+
+void stopBySeq(MidiChannel* ch);
+
+/* rewind
+Rewinds channel when rewind button on main transport is pressed. */
+
+void rewindBySeq(MidiChannel* ch);
+
+/* mute|unmute
+Mutes/unmutes a channel. */
+
+void setMute(MidiChannel* ch, bool v);
+void setSolo(MidiChannel* ch, bool v);
+}}};
+
+
+#endif
diff --git a/src/core/channels/sampleChannel.cpp b/src/core/channels/sampleChannel.cpp
new file mode 100644 (file)
index 0000000..fbd54be
--- /dev/null
@@ -0,0 +1,553 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/log.h"
+#include "core/const.h"
+#include "core/wave.h"
+#include "core/model/model.h"
+#include "sampleChannelProc.h"
+#include "sampleChannelRec.h"
+#include "channelManager.h"
+#include "sampleChannel.h"
+
+
+namespace giada {
+namespace m 
+{
+SampleChannel::SampleChannel(bool inputMonitor, int bufferSize,
+       ID columnId, ID id)
+: Channel          (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize,
+                    columnId, id),
+  hasWave          (false),
+  waveId           (0),
+  shift            (0),
+  mode             (ChannelMode::SINGLE_BASIC),
+  quantizing       (false),
+  inputMonitor     (inputMonitor),
+  pitch            (G_DEFAULT_PITCH),
+  tracker          (0),
+  trackerPreview   (0),
+  begin            (0),
+  end              (0),
+  midiInVeloAsVol  (false),
+  midiInReadActions(0x0),
+  midiInPitch      (0x0),
+  bufferOffset     (0),
+  rewinding        (false),
+  rsmp_state       (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
+{
+       if (rsmp_state == nullptr) {
+               u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
+               throw std::bad_alloc();
+       }
+       bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel::SampleChannel(const SampleChannel& o)
+: Channel          (o),
+  hasWave          (o.hasWave),
+  waveId           (o.waveId),
+  shift            (o.shift),
+  mode             (o.mode),
+  quantizing       (o.quantizing),
+  inputMonitor     (o.inputMonitor),
+  pitch            (o.pitch),
+  tracker          (o.tracker.load()),
+  trackerPreview   (0),
+  begin            (o.begin),
+  end              (o.end),
+  midiInVeloAsVol  (o.midiInVeloAsVol),
+  midiInReadActions(o.midiInReadActions.load()),
+  midiInPitch      (o.midiInPitch.load()),
+  bufferOffset     (o.bufferOffset),
+  rewinding        (o.rewinding),
+  rsmp_state       (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
+{
+       if (rsmp_state == nullptr) {
+               u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
+               throw std::bad_alloc();
+       }
+       
+       bufferPreview.alloc(o.bufferPreview.countFrames(), G_MAX_IO_CHANS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel::SampleChannel(const patch::Channel& p, int bufferSize)
+: Channel          (p, bufferSize),
+  hasWave          (p.waveId != 0),
+  waveId           (p.waveId),
+  shift            (0), // TODO
+  mode             (p.mode),
+  quantizing       (false),
+  inputMonitor     (p.inputMonitor),
+  pitch            (p.pitch),
+  tracker          (0),
+  trackerPreview   (0),
+  begin            (p.begin),
+  end              (p.end),
+  midiInVeloAsVol  (p.midiInVeloAsVol),
+  midiInReadActions(p.midiInReadActions),
+  midiInPitch      (p.midiInPitch),
+  bufferOffset     (0),
+  rewinding        (0),
+  rsmp_state       (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
+{
+       if (rsmp_state == nullptr) {
+               u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
+               throw std::bad_alloc();
+       }
+       
+       bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel* SampleChannel::clone() const
+{
+       return new SampleChannel(*this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel::~SampleChannel()
+{
+       if (rsmp_state != nullptr)
+               src_delete(rsmp_state);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::parseEvents(mixer::FrameEvents fe)
+{
+       sampleChannelProc::parseEvents(this, fe);
+       sampleChannelRec::parseEvents(this, fe);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::render(AudioBuffer& out, const AudioBuffer& in, 
+        AudioBuffer& inToOut, bool audible, bool running)
+{
+       sampleChannelProc::render(this, out, in, inToOut, audible, running);
+}
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::rewindBySeq()
+{
+       sampleChannelProc::rewindBySeq(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::start(int localFrame, bool doQuantize, int velocity)
+{
+       sampleChannelProc::start(this, localFrame, doQuantize, velocity);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::stop()
+{
+       sampleChannelProc::stop(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::stopBySeq(bool chansStopOnSeqHalt)
+{
+       sampleChannelProc::stopBySeq(this, chansStopOnSeqHalt);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::kill(int localFrame)
+{
+       sampleChannelProc::kill(this, localFrame);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleChannel::recordStart(bool canQuantize) 
+{
+       return sampleChannelRec::recordStart(this, canQuantize);
+}
+
+
+bool SampleChannel::recordKill()
+{
+       return sampleChannelRec::recordKill(this);
+}
+
+
+void SampleChannel::recordStop()
+{
+       sampleChannelRec::recordStop(this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt)
+{
+       sampleChannelRec::startReadingActions(this, treatRecsAsLoops, recsStopOnChanHalt);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::stopReadingActions(bool running, bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt)
+{
+       sampleChannelRec::stopReadingActions(this, running, treatRecsAsLoops, 
+               recsStopOnChanHalt);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::stopInputRec(int globalFrame)
+{
+       sampleChannelProc::stopInputRec(this, globalFrame);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setMute(bool value)
+{
+       sampleChannelProc::setMute(this, value);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setSolo(bool value)
+{
+       sampleChannelProc::setSolo(this, value);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
+{
+       sampleChannelRec::setReadActions(this, v, recsStopOnChanHalt);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleChannel::hasLogicalData() const
+{ 
+       if (!hasWave)
+               return false;
+
+       model::WavesLock wl(model::waves);
+       return model::get(model::waves, waveId).isLogical();
+};
+
+
+bool SampleChannel::hasEditedData() const
+{ 
+       if (!hasWave)
+               return false;
+
+       model::WavesLock wl(model::waves);
+       return model::get(model::waves, waveId).isEdited();
+};
+
+
+bool SampleChannel::hasData() const
+{
+       return hasWave;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setBegin(int f)
+{
+       model::WavesLock lock(model::waves);
+       const Wave& wave = model::get(model::waves, waveId);
+       
+       if (f < 0)
+               f = 0;
+       else
+       if (f > wave.getSize())
+               f = wave.getSize();
+       else
+       if (f >= end)
+               f = end - 1;
+
+       begin          = f;
+       tracker        = f;
+       trackerPreview = f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setEnd(int f)
+{
+       model::WavesLock lock(model::waves);
+       const Wave& wave = model::get(model::waves, waveId);
+       
+       if (f >= wave.getSize())
+               f = wave.getSize() - 1;
+       else
+       if (f <= begin)
+               f = begin + 1;
+
+       end = f;}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::getBegin() const { return begin; }
+int SampleChannel::getEnd() const   { return end; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::setPitch(float v)
+{
+       if (v > G_MAX_PITCH)
+               pitch = G_MAX_PITCH;
+       else
+       if (v < G_MIN_PITCH)
+               pitch = G_MIN_PITCH;
+       else 
+               pitch = v;
+
+// ????        /* if status is off don't slide between frequencies */
+// ???? 
+// ????        if (status & (STATUS_OFF | STATUS_WAIT))
+// ????                src_set_ratio(rsmp_state, 1/pitch);
+}
+
+
+float SampleChannel::getPitch() const { return pitch; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::getPosition() const
+{
+       if (playStatus != ChannelStatus::EMPTY   && 
+           playStatus != ChannelStatus::MISSING && 
+           playStatus != ChannelStatus::OFF)
+               return tracker - begin;
+       else
+               return -1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::empty()
+{
+       playStatus = ChannelStatus::EMPTY;
+       begin      = 0;
+       end        = 0;
+       tracker    = 0;
+       volume     = G_DEFAULT_VOL;
+       hasActions = false;
+       hasWave    = false;
+       waveId     = 0;
+       sendMidiLstatus();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleChannel::pushWave(ID wid, Frame size)
+{
+       playStatus = ChannelStatus::OFF;
+       waveId     = wid;
+       begin      = 0;
+       end        = size;
+       tracker    = 0;
+       hasWave    = true;
+       sendMidiLstatus();
+}
+
+
+void SampleChannel::popWave()
+{
+       playStatus = ChannelStatus::OFF;
+       waveId     = 0;
+       begin      = 0;
+       end        = 0;
+       tracker    = 0;
+       hasWave    = false;
+       sendMidiLstatus();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string SampleChannel::getSamplePath() const
+{
+       if (!hasWave)
+               return "";
+
+       model::WavesLock wl(model::waves);
+       return model::get(model::waves, waveId).getPath();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleChannel::canInputRec() const
+{
+       return !hasWave && armed == true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::fillBuffer(AudioBuffer& dest, int start, int offset)
+{
+       assert(offset < dest.countFrames());
+       
+       if (pitch == 1.0) return fillBufferCopy(dest, start, offset);
+       else              return fillBufferResampled(dest, start, offset);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::fillBufferResampled(AudioBuffer& dest, int start, int offset)
+{
+       model::WavesLock lock(model::waves);
+       const Wave& wave = model::get(model::waves, waveId);
+       
+       rsmp_data.data_in       = wave.getFrame(start);        // Source data
+       rsmp_data.input_frames  = end - start;                  // How many readable frames
+       rsmp_data.data_out      = dest[offset];                 // Destination (processed data)
+       rsmp_data.output_frames = dest.countFrames() - offset;  // How many frames to process
+       rsmp_data.end_of_input  = false;
+       rsmp_data.src_ratio     = 1 / pitch;
+
+       src_process(rsmp_state, &rsmp_data);
+
+       return rsmp_data.input_frames_used; // Returns used frames
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+int SampleChannel::fillBufferCopy(AudioBuffer& dest, int start, int offset)
+{
+       model::WavesLock lock(model::waves);
+       const Wave& wave = model::get(model::waves, waveId);
+
+       int used = dest.countFrames() - offset;
+       if (used > wave.getSize() - start)
+               used = wave.getSize() - start;
+
+       dest.copyData(wave.getFrame(start), used, offset);
+
+       return used;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleChannel::isAnyLoopMode() const
+{
+       return mode == ChannelMode::LOOP_BASIC  || 
+              mode == ChannelMode::LOOP_ONCE   || 
+              mode == ChannelMode::LOOP_REPEAT || 
+              mode == ChannelMode::LOOP_ONCE_BAR;
+}
+
+
+bool SampleChannel::isAnySingleMode() const
+{
+       return !isAnyLoopMode();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleChannel::isOnLastFrame() const
+{
+       return tracker >= end;
+}
+
+}} // giada::m::
diff --git a/src/core/channels/sampleChannel.h b/src/core/channels/sampleChannel.h
new file mode 100644 (file)
index 0000000..50e1f71
--- /dev/null
@@ -0,0 +1,175 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_H
+#define G_SAMPLE_CHANNEL_H
+
+
+#include <memory>
+#include <functional>
+#include <samplerate.h>
+#include "core/types.h"
+#include "core/channels/channel.h"
+
+
+namespace giada {
+namespace m 
+{
+class Wave;
+
+class SampleChannel : public Channel
+{
+public:
+
+       SampleChannel(bool inputMonitor, int bufferSize, ID columnId, ID id);
+       SampleChannel(const SampleChannel& o);
+       SampleChannel(const patch::Channel& p, int bufferSize);
+       ~SampleChannel();
+
+       SampleChannel* clone() const override;
+       void parseEvents(mixer::FrameEvents fe) override;
+       void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut, 
+               bool audible, bool running) override;
+
+       void start(int frame, bool doQuantize, int velocity) override;
+       void stop() override;
+       void kill(int frame) override;
+       bool recordStart(bool canQuantize) override;
+       bool recordKill() override;
+       void recordStop() override;
+       void setMute(bool value) override;
+       void setSolo(bool value) override;
+       void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override;
+       void stopReadingActions(bool running, bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt) override;
+       void empty() override;
+       void stopBySeq(bool chansStopOnSeqHalt) override;
+       void rewindBySeq() override;
+       void stopInputRec(int globalFrame) override;
+       bool canInputRec() const override;
+       bool hasLogicalData() const override;
+       bool hasEditedData() const override;
+       bool hasData() const override;
+
+       int   getBegin() const;
+       int   getEnd() const;
+       float getPitch() const;
+       bool isAnyLoopMode() const;
+       bool isAnySingleMode() const;
+       bool isOnLastFrame() const;
+       std::string getSamplePath() const;
+
+       /* getPosition
+       Returns the position of an active sample. If EMPTY o MISSING returns -1. */
+
+       int getPosition() const;
+
+       /* fillBuffer
+       Fills 'dest' buffer at point 'offset' with Wave data taken from 'start'. 
+       Returns how many frames have been used from the original Wave data. It also
+       resamples data if pitch != 1.0f. */
+
+       int fillBuffer(AudioBuffer& dest, int start, int offset);
+
+       /* pushWave
+       Adds a new wave to this channel. */
+
+       void pushWave(ID waveId, Frame waveSize);
+       void popWave();
+
+       void setPitch(float v);
+       void setBegin(int f);
+       void setEnd(int f);
+
+       void setReadActions(bool v, bool recsStopOnChanHalt);
+
+       /* bufferPreview
+       Extra buffer for audio preview. */
+
+       AudioBuffer bufferPreview;
+       
+       /* hasWave
+       Tells if a wave is linked to this channel. */
+       /* TODO - useless: check if waveId != 0 */
+
+       bool hasWave;
+
+       /* waveId
+       ID of a Wave object. Might be useless if hasWave == false. */
+
+       ID waveId;
+
+       int shift;
+       ChannelMode mode;
+       bool quantizing;                    // quantization in progress
+       bool inputMonitor;  
+       float pitch;
+       
+       std::atomic<Frame> tracker;         // chan position
+       std::atomic<Frame> trackerPreview;  // chan position for audio preview
+
+       /* begin, end
+       Begin/end point to read wave data from/to. */
+
+       Frame begin;  
+       Frame end;    
+
+       /* midiIn*
+       MIDI input parameters. */
+
+       bool                  midiInVeloAsVol;
+       std::atomic<uint32_t> midiInReadActions;
+       std::atomic<uint32_t> midiInPitch;
+
+       /* bufferOffset
+       Offset used while filling the internal buffer with audio data. Value is 
+       greater than zero on start sample. */
+       
+       Frame bufferOffset;
+
+       /* rewinding
+       Tells whether a rewind event is taking place. Used to fill the audio
+       buffer twice. */
+
+       bool rewinding; 
+
+private:
+
+       /* rsmp_state, rsmp_data
+       Structs from libsamplerate. */
+
+       SRC_STATE* rsmp_state;
+       SRC_DATA   rsmp_data;
+
+       int fillBufferResampled(AudioBuffer& dest, int start, int offset);
+       int fillBufferCopy     (AudioBuffer& dest, int start, int offset);
+};
+
+}} // giada::m::
+
+
+#endif
diff --git a/src/core/channels/sampleChannelProc.cpp b/src/core/channels/sampleChannelProc.cpp
new file mode 100644 (file)
index 0000000..446f8ed
--- /dev/null
@@ -0,0 +1,526 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/math.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/pluginHost.h"
+#include "core/mixerHandler.h"
+#include "sampleChannelProc.h"
+
+
+namespace giada {
+namespace m {
+namespace sampleChannelProc
+{
+namespace
+{
+void rewind_(SampleChannel* ch, Frame localFrame)
+{
+       /* Quantization stops on rewind. */
+
+       ch->quantizing = false; 
+
+       if (ch->isPlaying()) { 
+               ch->rewinding    = true;
+               ch->bufferOffset = localFrame;
+       }
+       else
+               ch->tracker = ch->begin;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* quantize
+Starts channel according to quantizer. */
+
+void quantize_(SampleChannel* ch, int localFrame)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::OFF:
+                       ch->playStatus   = ChannelStatus::PLAY;
+                       ch->bufferOffset = localFrame;
+                       ch->sendMidiLstatus();
+                       // ch->quantizing = false is set by sampleChannelRec::quantize()
+                       break;
+
+               default:
+                       rewind_(ch, localFrame);
+                       break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* onBar
+Things to do when the sequencer is on a bar. */
+
+void onBar_(SampleChannel* ch, int localFrame)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::PLAY:
+                       if (ch->mode == ChannelMode::LOOP_REPEAT)
+                               rewind_(ch, localFrame);
+                       break;
+
+               case ChannelStatus::WAIT:
+                       if (ch->mode == ChannelMode::LOOP_ONCE_BAR) {
+                               ch->playStatus       = ChannelStatus::PLAY;
+                               ch->bufferOffset = localFrame;
+                               ch->sendMidiLstatus();
+                       }
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* onFirstBeat
+Things to do when the sequencer is on the first beat. */
+
+void onFirstBeat_(SampleChannel* ch, Frame localFrame)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::PLAY: 
+                       if (ch->isAnyLoopMode())
+                               rewind_(ch, localFrame);
+                       break;
+
+               case ChannelStatus::WAIT:
+                       ch->playStatus       = ChannelStatus::PLAY;
+                       ch->bufferOffset = localFrame;
+                       ch->sendMidiLstatus();
+                       break;
+
+               case ChannelStatus::ENDING: 
+                       if (ch->isAnyLoopMode())
+                               kill(ch, localFrame);
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* onLastFrame
+Things to do when the sample has reached the end (i.e. last frame). Called by
+prepareBuffer(). */
+
+void onLastFrame_(SampleChannel* ch, bool running)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::PLAY:
+                       /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for
+                       SINGLE_ENDLESS, which runs forever unless it's in ENDING mode. 
+                       Other loop once modes are put in wait mode. */
+                       if ((ch->mode == ChannelMode::SINGLE_BASIC   || 
+                                ch->mode == ChannelMode::SINGLE_PRESS   ||
+                                ch->mode == ChannelMode::SINGLE_RETRIG) || 
+                               (ch->isAnyLoopMode() && !running))
+                               ch->playStatus = ChannelStatus::OFF;
+                       else
+                       if (ch->mode == ChannelMode::LOOP_ONCE     ||
+                           ch->mode == ChannelMode::LOOP_ONCE_BAR)
+                               ch->playStatus = ChannelStatus::WAIT;
+                       ch->sendMidiLstatus();
+                       break;                  
+
+               case ChannelStatus::ENDING:
+                       /* LOOP_ONCE or LOOP_ONCE_BAR: if ending (i.e. the user requested 
+                       their termination), stop 'em. Let them wait otherwise. */
+                       if (ch->mode == ChannelMode::LOOP_ONCE ||
+                           ch->mode == ChannelMode::LOOP_ONCE_BAR)
+                               ch->playStatus = ChannelStatus::WAIT;
+                       else {
+                               ch->playStatus = ChannelStatus::OFF;
+                               ch->sendMidiLstatus();
+                       }
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processIO_(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in, 
+       bool running)
+{
+       assert(out.countSamples() == ch->buffer.countSamples());
+       if (in.isAllocd())
+               assert(in.countSamples() == ch->buffer.countSamples());
+
+       /* If armed and input buffer is not empty (i.e. input device available) and
+       input monitor is on, copy input buffer to channel buffer: this enables the 
+       input monitoring. The channel buffer will be overwritten later on by 
+       pluginHost::processStack, so that you would record "clean" audio 
+       (i.e. not plugin-processed). */
+
+       if (ch->armed && in.isAllocd() && ch->inputMonitor) {
+               for (int i=0; i<ch->buffer.countFrames(); i++)
+                       for (int j=0; j<ch->buffer.countChannels(); j++)
+                               ch->buffer[i][j] += in[i][j];   // add, don't overwrite
+       }
+
+#ifdef WITH_VST
+       pluginHost::processStack(ch->buffer, ch->pluginIds);
+#endif
+
+       for (int i=0; i<out.countFrames(); i++) {
+               if (running)
+                       ch->calcVolumeEnvelope();
+               if (!ch->mute)
+                       for (int j=0; j<out.countChannels(); j++)
+                               out[i][j] += ch->buffer[i][j] * ch->volume * ch->volume_i * ch->calcPanning(j); 
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processPreview_(SampleChannel* ch, m::AudioBuffer& out)
+{
+       ch->bufferPreview.clear();
+
+       /* If the tracker exceedes the end point and preview is looped, split the 
+       rendering as in SampleChannel::reset(). */
+
+       if (ch->trackerPreview == ch->end)
+               ch->trackerPreview = ch->begin;
+       else
+       if (ch->trackerPreview + ch->bufferPreview.countFrames() >= ch->end) {
+               int offset = ch->end - ch->trackerPreview;
+               ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
+               ch->trackerPreview = ch->begin;
+               if (ch->previewMode == PreviewMode::LOOP)
+                       ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->begin, offset);
+               else
+               if (ch->previewMode == PreviewMode::NORMAL)
+                       ch->previewMode = PreviewMode::NONE;
+       }
+       else
+               ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
+
+       for (int i=0; i<out.countFrames(); i++)
+               for (int j=0; j<out.countChannels(); j++)
+                       out[i][j] += ch->bufferPreview[i][j] * ch->volume * ch->calcPanning(j); 
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void fillBuffer_(SampleChannel* ch, bool running)
+{
+       ch->buffer.clear();
+
+       if (!ch->hasData() || !ch->isPlaying())
+               return;
+
+       if (ch->rewinding) {
+
+               /* Fill the tail. */
+               
+               if (!ch->isOnLastFrame())
+                       ch->fillBuffer(ch->buffer, ch->tracker, 0);
+               
+               /* Reset tracker to begin point. */
+
+               ch->tracker = ch->begin;
+               
+               /* Then fill the new head. */
+
+               ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, ch->bufferOffset);
+               ch->bufferOffset = 0;
+               ch->rewinding    = false;
+       }
+       else {
+               Frame framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, ch->bufferOffset);
+               ch->tracker      += framesUsed;
+               ch->bufferOffset  = 0;
+               if (ch->isOnLastFrame()) {
+                       onLastFrame_(ch, running);
+                       ch->tracker = ch->begin;
+                       if (ch->mode == ChannelMode::LOOP_BASIC  || 
+                           ch->mode == ChannelMode::LOOP_REPEAT || 
+                           ch->mode == ChannelMode::SINGLE_ENDLESS) {
+                               /* framesUsed might be imprecise when working with resampled 
+                               audio, which could cause a buffer overflow if used as offset.
+                               Let's clamp it to be at most buffer->countFrames(). */
+                               ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, 
+                                       u::math::bound(framesUsed, 0, ch->buffer.countFrames() - 1));
+                       }
+               }
+       }
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void kill(SampleChannel* ch, int localFrame)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::WAIT:
+               case ChannelStatus::PLAY:
+               case ChannelStatus::ENDING:
+                       /*  Clear data in range [localFrame, (buffer.size)) if the kill event 
+                       occurs in the middle of the buffer. */
+                       if (localFrame != 0)
+                               ch->buffer.clear(localFrame);
+                       ch->playStatus = ChannelStatus::OFF;
+                       ch->sendMidiLstatus();
+                       rewind_(ch, localFrame);
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stop(SampleChannel* ch)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::PLAY:
+                       if (ch->mode == ChannelMode::SINGLE_PRESS)
+                               kill(ch, 0);
+                       break;
+
+               default:
+                       /* If quantizing, stop a SINGLE_PRESS immediately. */
+                       if (ch->mode == ChannelMode::SINGLE_PRESS && ch->quantizing)
+                               ch->quantizing = false; 
+                       break;          
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopInputRec(SampleChannel* ch, int globalFrame)
+{
+       /* 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 global frame, not at the 
+       next first beat. */
+       if (ch->isAnyLoopMode() && ch->playStatus == ChannelStatus::OFF && ch->armed) {
+               ch->playStatus  = ChannelStatus::PLAY;
+               ch->tracker = globalFrame;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt)
+{
+       switch (ch->playStatus) {
+               case ChannelStatus::WAIT:
+                       /* Loop-mode channels in wait status get stopped right away. */
+                       if (ch->isAnyLoopMode())
+                               ch->playStatus = ChannelStatus::OFF;
+                       break;
+
+               case ChannelStatus::PLAY:
+                       /* Kill samples if a) chansStopOnSeqHalt == true (run the sample to end 
+                       otherwise); b) when a channel is reading (and playing) actions. */
+                       if (chansStopOnSeqHalt)
+                               if (ch->isAnyLoopMode() || ch->isReadingActions())
+                                       kill(ch, 0);
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindBySeq(SampleChannel* ch)
+{
+       /* Rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode. Rewind by 
+       sequencer is a user-generated event, it always occurs on local frame 0. */
+
+       if (ch->hasData()) {
+               if ((ch->isAnyLoopMode()) || (ch->recStatus == ChannelStatus::PLAY && (ch->isAnySingleMode())))
+                       rewind_(ch, 0);
+       }       
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setMute(SampleChannel* ch, bool value)
+{
+       ch->mute = value;
+
+       // This is for processing playing_inaudible
+       ch->sendMidiLstatus();  
+
+       ch->sendMidiLmute();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setSolo(SampleChannel* ch, bool value)
+{
+       ch->solo = value;
+
+       // This is for processing playing_inaudible
+       model::ChannelsLock l(model::channels);
+       for (Channel* c : model::channels)
+               c->sendMidiLstatus();
+       
+       ch->sendMidiLsolo();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity)
+{
+       /* For one-shot modes, velocity drives the internal volume. */
+       if (velocity != 0) {
+               if (ch->isAnySingleMode() && ch->midiInVeloAsVol)
+                       ch->volume_i.store(u::math::map<int, float>(velocity, 0, G_MAX_VELOCITY, 0.0, 1.0));            
+       }
+
+       switch (ch->playStatus) {
+               case ChannelStatus::OFF:
+                       ch->bufferOffset = localFrame;
+                       if (ch->isAnyLoopMode()) {
+                               ch->playStatus = ChannelStatus::WAIT;
+                               ch->sendMidiLstatus();
+                       }
+                       else {
+                               if (doQuantize)
+                                       ch->quantizing = true;
+                               else {
+                                       ch->playStatus = ChannelStatus::PLAY;
+                                       ch->sendMidiLstatus();
+                               }
+                       }
+                       break;
+
+               case ChannelStatus::PLAY:
+                       if (ch->mode == ChannelMode::SINGLE_RETRIG) {
+                               if (doQuantize)
+                                       ch->quantizing = true;
+                               else
+                                       rewind_(ch, localFrame);
+                       }
+                       else
+                       if (ch->isAnyLoopMode() || ch->mode == ChannelMode::SINGLE_ENDLESS) {
+                               ch->playStatus = ChannelStatus::ENDING;
+                               ch->sendMidiLstatus();
+                       }
+                       else
+                       if (ch->mode == ChannelMode::SINGLE_BASIC) {
+                               rewind_(ch, localFrame);
+                               ch->playStatus = ChannelStatus::OFF;
+                               ch->sendMidiLstatus();
+                       }
+                       break;
+
+               case ChannelStatus::WAIT:
+                       ch->playStatus = ChannelStatus::OFF;
+                       ch->sendMidiLstatus();
+                       break;
+
+               case ChannelStatus::ENDING:
+                       ch->playStatus = ChannelStatus::PLAY;
+                       ch->sendMidiLstatus();
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void render(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in, 
+               AudioBuffer& inToOut, bool audible, bool running)
+{
+       fillBuffer_(ch, running);
+
+       if (audible)
+               processIO_(ch, out, in, running);
+
+       if (ch->isPreview())
+               processPreview_(ch, out);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
+{
+       if (!ch->hasData())
+               return;
+
+       /* Quantize only if is single mode and in quantizer-wait mode and a
+       quantizer step has passed. */
+
+       if (ch->isAnySingleMode() && ch->quantizing && fe.quantoPassed) 
+               quantize_(ch, fe.frameLocal);
+       if (fe.onBar)
+               onBar_(ch, fe.frameLocal);
+       if (fe.onFirstBeat)
+               onFirstBeat_(ch, fe.frameLocal);
+}
+}}};
diff --git a/src/core/channels/sampleChannelProc.h b/src/core/channels/sampleChannelProc.h
new file mode 100644 (file)
index 0000000..7d9fa3e
--- /dev/null
@@ -0,0 +1,89 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_PROC_H
+#define G_SAMPLE_CHANNEL_PROC_H
+
+
+#include "core/mixer.h"
+#include "core/audioBuffer.h"
+#include "core/types.h"
+
+
+namespace giada {
+namespace m 
+{
+class SampleChannel;
+
+namespace sampleChannelProc
+{
+/**/
+void render(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in, 
+    AudioBuffer& inToOut, bool audible, bool running);
+
+/* parseEvents
+Parses events gathered by Mixer::masterPlay(). */
+
+void parseEvents(SampleChannel* ch, mixer::FrameEvents ev);
+
+/* kill
+Stops a channel abruptly. */
+
+void kill(SampleChannel* ch, int localFrame);
+
+/* stop
+Stops a channel normally (via key or MIDI). */
+
+void stop(SampleChannel* ch);
+
+/* stopInputRec
+Prepare a channel for playing when the input recording is done. */
+
+void stopInputRec(SampleChannel* ch, int globalFrame);
+
+/* stopBySeq
+Stops a channel when the stop button on main transport is pressed. */
+
+void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt);
+
+/* rewind
+Rewinds channel when rewind button on main transport is pressed. */
+
+void rewindBySeq(SampleChannel* ch);
+
+/* start
+Starts a channel. doQuantize = false (don't quantize) when Mixer is reading 
+actions from Recorder. */
+
+void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity);
+
+void setMute(SampleChannel* ch, bool value);
+void setSolo(SampleChannel* ch, bool value);
+}}};
+
+
+#endif
diff --git a/src/core/channels/sampleChannelRec.cpp b/src/core/channels/sampleChannelRec.cpp
new file mode 100644 (file)
index 0000000..c914e1f
--- /dev/null
@@ -0,0 +1,276 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "utils/math.h"
+#include "core/channels/sampleChannel.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/clock.h"
+#include "core/action.h"
+#include "core/kernelAudio.h"
+#include "sampleChannelRec.h"
+
+
+namespace giada {
+namespace m {
+namespace sampleChannelRec
+{
+namespace
+{
+/* onFirstBeat
+Things to do when the sequencer is on the first beat. */
+
+void onFirstBeat_(SampleChannel* ch, bool recsStopOnChanHalt)
+{
+       switch (ch->recStatus) {
+               case ChannelStatus::ENDING:
+                       ch->recStatus = ChannelStatus::OFF;
+                       setReadActions(ch, false, recsStopOnChanHalt);  // rec stop
+                       break;
+
+               case ChannelStatus::WAIT:
+                       ch->recStatus = ChannelStatus::PLAY;
+                       setReadActions(ch, true, recsStopOnChanHalt);   // rec start
+                       break;
+
+               default: break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool recorderCanRec_(SampleChannel* ch)
+{
+       /* Can record on a channel if:
+               - recorder is on
+               - mixer is running
+               - mixer is not recording a take somewhere
+               - channel is MIDI or SAMPLE type with data in it  */
+
+       return recManager::isRecordingAction() && 
+              clock::isRunning()              && 
+              !recManager::isRecordingInput() && 
+              (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData()));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* calcVolumeEnv
+Computes any changes in volume done via envelope tool. */
+
+void calcVolumeEnv_(SampleChannel* ch, const Action& a1)
+{
+       assert(a1.next != nullptr);
+
+       const Action a2 = *a1.next;
+
+       double vf1 = u::math::map<int, double>(a1.event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
+       double vf2 = u::math::map<int, double>(a2.event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
+
+       ch->volume_i = vf1;
+       ch->volume_d = a2.frame == a1.frame ? 0 : (vf2 - vf1) / (a2.frame - a1.frame);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parseAction_(SampleChannel* ch, const Action& a, int localFrame, int globalFrame)
+{
+       switch (a.event.getStatus()) {
+               case MidiEvent::NOTE_ON:
+                       if (ch->isAnySingleMode())
+                               ch->start(localFrame, /*quantize=*/false, /*velocity=*/0);
+                       break;
+               case MidiEvent::NOTE_OFF:
+                       if (ch->isAnySingleMode())
+                               ch->stop();
+                       break;
+               case MidiEvent::NOTE_KILL:
+                       if (ch->isAnySingleMode())
+                               ch->kill(localFrame);
+                       break;
+               case MidiEvent::ENVELOPE:
+                       calcVolumeEnv_(ch, a);
+                       break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordKeyPressAction_(SampleChannel* ch)
+{
+       if (!recorderCanRec_(ch))
+               return;
+
+       /* Disable reading actions while recording SINGLE_PRESS mode. Don't let 
+       existing actions interfere with the current one being recorded. */
+
+       if (ch->mode == ChannelMode::SINGLE_PRESS)
+               ch->readActions = false;
+       
+       recorderHandler::liveRec(ch->id, MidiEvent(MidiEvent::NOTE_ON, 0, 0));
+       ch->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void quantize_(SampleChannel* ch, bool quantoPassed)
+{
+       /* Skip if in loop mode or not in a quantization stage. Otherwise the 
+       quantization wait has expired: record the keypress.  */
+
+       if (!ch->isAnyLoopMode() && ch->quantizing && quantoPassed && ch->playStatus == ChannelStatus::PLAY) {
+               ch->quantizing = false;
+               recordKeyPressAction_(ch);
+       }
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
+{
+       if (!ch->hasWave)
+               return;
+       quantize_(ch, fe.quantoPassed);
+       if (fe.onFirstBeat)
+               onFirstBeat_(ch, conf::recsStopOnChanHalt);
+       if (ch->readActions && fe.actions != nullptr)
+               for (const Action& action : *fe.actions)
+                       if (action.channelId == ch->id)
+                               parseAction_(ch, action, fe.frameLocal, fe.frameGlobal);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool recordStart(SampleChannel* ch, bool canQuantize)
+{
+       /* Record a 'start' event if the quantizer is off, otherwise let mixer to 
+       handle it when a quantoWait has passed (see quantize_()). Also skip if 
+       channel is in any loop mode, where KEYPRESS and KEYREL are meaningless. */
+       
+       if (!canQuantize && !ch->isAnyLoopMode() && recorderCanRec_(ch))
+               recordKeyPressAction_(ch);
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool recordKill(SampleChannel* ch)
+{
+       /* Don't record NOTE_KILL actions for LOOP channels. */
+       if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) {
+               recorder::rec(ch->id, clock::getCurrentFrame(), MidiEvent(MidiEvent::NOTE_KILL, 0, 0));
+               ch->hasActions = true;
+       }
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordStop(SampleChannel* ch)
+{
+       /* Record a stop event only if channel is SINGLE_PRESS. For any other mode 
+       the stop event is meaningless. */
+       if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS)
+               recorderHandler::liveRec(ch->id, MidiEvent(MidiEvent::NOTE_OFF, 0, 0));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt)
+{
+       ch->readActions = v;
+       if (!v && recsStopOnChanHalt)
+               ch->kill(0); // FIXME - wrong frame value
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, bool recsStopOnChanHalt)
+{
+       if (treatRecsAsLoops)
+               ch->recStatus = ChannelStatus::WAIT;
+       else
+               setReadActions(ch, true, recsStopOnChanHalt);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopReadingActions(SampleChannel* ch, bool isClockRunning, bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt)
+{
+       /* First of all, if the clock 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 (!isClockRunning) {
+               ch->recStatus = ChannelStatus::OFF;
+               setReadActions(ch, false, false);
+       }
+       else
+       if (ch->recStatus == ChannelStatus::WAIT)
+               ch->recStatus = ChannelStatus::OFF;
+       else
+       if (ch->recStatus == ChannelStatus::ENDING)
+               ch->recStatus = ChannelStatus::PLAY;
+       else
+       if (treatRecsAsLoops)
+               ch->recStatus = ChannelStatus::ENDING;
+       else
+               setReadActions(ch, false, recsStopOnChanHalt);
+}
+}}};
diff --git a/src/core/channels/sampleChannelRec.h b/src/core/channels/sampleChannelRec.h
new file mode 100644 (file)
index 0000000..ed8dfa0
--- /dev/null
@@ -0,0 +1,71 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_REC_H
+#define G_SAMPLE_CHANNEL_REC_H
+
+
+namespace giada {
+namespace m 
+{
+class SampleChannel;
+
+namespace sampleChannelRec
+{
+void parseEvents(SampleChannel* ch, mixer::FrameEvents fe);
+
+/* recordStart
+Records a 'start' action if capable of. Returns true if a start() call can
+be performed. */
+
+bool recordStart(SampleChannel* ch, bool doQuantize);
+
+/* recordKill
+Records a 'kill' action if capable of. Returns true if a kill() call can
+be performed. */
+
+bool recordKill(SampleChannel* ch);
+
+/* recordStop
+Ends overdub mode SINGLE_PRESS channels. */
+
+void recordStop(SampleChannel* ch);
+
+/* setReadActions
+If enabled (v == true), Recorder will read actions from channel 'ch'. If 
+recsStopOnChanHalt == true and v == false, will also kill the channel. */
+
+void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt);
+
+void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, 
+       bool recsStopOnChanHalt);
+void stopReadingActions(SampleChannel* ch, bool isClockRunning, 
+       bool treatRecsAsLoops, bool recsStopOnChanHalt);
+}}};
+
+
+#endif
\ No newline at end of file
index 4fe106822d24e23161c2660862e5742988df6f4c..35226b1e78e50647c433b66f7f9cef40888f73e8 100644 (file)
 
 #include <atomic>
 #include <cassert>
-#include "../glue/transport.h"
-#include "../glue/main.h"
-#include "conf.h"
-#include "const.h"
-#include "kernelAudio.h"
-#include "kernelMidi.h"
+#include "glue/main.h"
+#include "utils/math.h"
+#include "core/model/model.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/kernelAudio.h"
+#include "core/mixerHandler.h"
+#include "core/kernelMidi.h"
 #include "clock.h"
 
 
@@ -42,38 +44,39 @@ namespace clock
 {
 namespace
 {
-float bpm_      = G_DEFAULT_BPM;
-int   bars_     = G_DEFAULT_BARS;
-int   beats_    = G_DEFAULT_BEATS;
-int   quanto_   = 1;            // quantizer step
-std::atomic<int>         quantize_(G_DEFAULT_QUANTIZE);
-std::atomic<ClockStatus> status_(ClockStatus::STOPPED);
-
-int framesInLoop_ = 0;
-int framesInBar_  = 0;
-int framesInBeat_ = 0;
-int framesInSeq_  = 0;
-std::atomic<int> currentFrameWait_(0);  // Used only in wait mode
+std::atomic<int> currentFrameWait_(0);
 std::atomic<int> currentFrame_(0);
 std::atomic<int> currentBeat_(0);
 
+int quanto_ = 1;             // Quantizer step
+
 int midiTCrate_    = 0;      // Send MTC data every midiTCrate_ frames
 int midiTCframes_  = 0;
 int midiTCseconds_ = 0;
 int midiTCminutes_ = 0;
 int midiTChours_   = 0;
 
-#ifdef G_OS_LINUX
+
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
 kernelAudio::JackState jackStatePrev_;
 #endif
 
 
-void updateQuanto_()
+/* -------------------------------------------------------------------------- */
+
+/* updateFrameBars
+Updates bpm, frames, beats and so on. */
+
+void updateFrameBars_(model::Clock& c)
 {
-       if (quantize_.load() != 0)
-               quanto_ = framesInBeat_ / quantize_.load();
-}
+       c.framesInLoop = (conf::samplerate * (60.0f / c.bpm)) * c.beats;
+       c.framesInBar  = c.framesInLoop / (float) c.bars;
+       c.framesInBeat = c.framesInLoop / (float) c.beats;
+       c.framesInSeq  = c.framesInBeat * G_MAX_BEATS;
 
+       if (c.quantize != 0)
+               quanto_ = c.framesInBeat / c.quantize;
+}
 }; // {anonymous}
 
 
@@ -84,13 +87,16 @@ void updateQuanto_()
 
 void init(int sampleRate, float midiTCfps)
 {
-       status_.store(ClockStatus::STOPPED);  // Must be the first thing to do
        midiTCrate_ = (sampleRate / midiTCfps) * G_MAX_IO_CHANS;  // stereo values
-       bpm_        = G_DEFAULT_BPM;
-       bars_       = G_DEFAULT_BARS;
-       beats_      = G_DEFAULT_BEATS;
-       quantize_.store(G_DEFAULT_QUANTIZE);
-       updateFrameBars();
+
+       model::onSwap(model::clock, [&](model::Clock& c)
+       {
+               c.bars     = G_DEFAULT_BARS;
+               c.beats    = G_DEFAULT_BEATS;
+               c.bpm      = G_DEFAULT_BPM;
+               c.quantize = G_DEFAULT_QUANTIZE;
+               updateFrameBars_(c);
+       });
 }
 
 
@@ -99,35 +105,50 @@ void init(int sampleRate, float midiTCfps)
 
 bool isRunning()
 {
-       return status_.load() == ClockStatus::RUNNING;
+       model::ClockLock lock(model::clock);
+
+       return model::clock.get()->status == ClockStatus::RUNNING;
 }
 
 
 bool isActive()
 {
-       return status_.load() == ClockStatus::RUNNING || status_.load() == ClockStatus::WAITING;
+       model::ClockLock lock(model::clock);
+       
+       ClockStatus status = model::clock.get()->status;
+       return status == ClockStatus::RUNNING || status == ClockStatus::WAITING;
 }
 
 
 bool quantoHasPassed()
 {
-       return currentFrame_.load() % (quanto_) == 0;
+       return currentFrame_.load() % quanto_ == 0;
 }
 
 
 bool isOnBar()
 {
-       if (status_.load() == ClockStatus::WAITING)
+       model::ClockLock lock(model::clock);
+
+       const model::Clock* c = model::clock.get();
+       
+       int currentFrame = currentFrame_.load();
+
+       if (c->status == ClockStatus::WAITING || currentFrame == 0)
                return false;
-       return currentFrame_.load() % framesInBar_ == 0;
+       return currentFrame % c->framesInBar == 0;
 }
 
 
 bool isOnBeat()
 {
-       if (status_.load() == ClockStatus::WAITING)
-               return currentFrameWait_.load() % framesInBeat_ == 0;
-       return currentFrame_.load() % framesInBeat_ == 0;
+       model::ClockLock lock(model::clock);
+       
+       const model::Clock* c = model::clock.get();
+       
+       if (c->status == ClockStatus::WAITING)
+               return currentFrameWait_.load() % c->framesInBeat == 0;
+       return currentFrame_.load() % c->framesInBeat == 0;
 }
 
 
@@ -137,55 +158,60 @@ bool isOnFirstBeat()
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+
 void setBpm(float b)
-{
-       if (b < G_MIN_BPM)
-               b = G_MIN_BPM;
-       bpm_ = b;
-       updateFrameBars();
-}
+{      
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+       
+       /* Can't change bpm from within Giada when using JACK. */
 
+       if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
+               return;
 
-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;
+#endif
+
+       b = u::math::bound(b, G_MIN_BPM, G_MAX_BPM);
+
+       model::onSwap(model::clock, [&](model::Clock& c)
+       {
+               c.bpm = b;
+               updateFrameBars_(c);
+       });
 }
 
 
-void setBeats(int b)
+void setBeats(int newBeats, int newBars)
 {
-       if (b > G_MAX_BEATS)
-               beats_ = G_MAX_BEATS;
-       else if (b < 1)
-               beats_ = 1;
-       else
-               beats_ = b;
+       newBeats = u::math::bound(newBeats, 1, G_MAX_BEATS);
+       newBars  = u::math::bound(newBars, 1, newBeats); // Bars cannot be greater than beats
+
+       model::onSwap(model::clock, [&](model::Clock& c)
+       {
+               c.beats = newBeats;
+               c.bars  = newBars;
+               updateFrameBars_(c);
+       });
 }
 
 
 void setQuantize(int q)
 {
-       quantize_.store(q);
-       updateQuanto_();
+       model::onSwap(model::clock, [&](model::Clock& c)
+       {
+               c.quantize = q; 
+               updateFrameBars_(c);
+       });
 }
 
 
 void setStatus(ClockStatus s)
 {
-       status_.store(s);
+       model::onSwap(model::clock, [&](model::Clock& c)
+       {
+               c.status = s;
+       });
        
        if (s == ClockStatus::RUNNING) {
                if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
@@ -206,29 +232,40 @@ void setStatus(ClockStatus s)
 
 void incrCurrentFrame() 
 {
-       if (status_.load() == ClockStatus::WAITING) {
-               currentFrameWait_++;
-               if (currentFrameWait_.load() >= framesInLoop_)
-                       currentFrameWait_ = 0;
+       model::ClockLock lock(model::clock);
+       
+       const model::Clock* c = model::clock.get();
+
+       if (c->status == ClockStatus::WAITING) {
+               int f = currentFrameWait_.load() + 1;
+               if (f >= c->framesInLoop)
+                               f = 0;
+               currentFrameWait_.store(f);
+               return;
        }
-       else {
-               currentFrame_++;
-               if (currentFrame_.load() >= framesInLoop_) {
-                       currentFrame_.store(0);
-                       currentBeat_.store(0);
-               }
-               else
-               if (isOnBeat())
-                       currentBeat_++;
+
+       int f = currentFrame_.load() + 1;
+       int b = currentBeat_.load();
+
+       if (f >= c->framesInLoop) {
+               f = 0;
+               b = 0;
        }
+       else
+       if (f % c->framesInBeat == 0) // If is on beat
+               b++;
+       
+       currentFrame_.store(f);
+       currentBeat_.store(b);
 }
 
 
 void rewind()
 {
-       currentFrameWait_.store(0);
        currentFrame_.store(0);
        currentBeat_.store(0);
+       currentFrameWait_.store(0);
+       
        sendMIDIrewind();
 }
 
@@ -236,37 +273,23 @@ void rewind()
 /* -------------------------------------------------------------------------- */
 
 
-void updateFrameBars()
-{
-       /* framesInLoop_ ... loop length in frames, or samplerate * # frames per 
-        *                  current bpm_ * beats_;
-        * framesInBar_ .... n. of frames within a bar;
-        * framesInBeat_ ... n. of frames within a beat;
-        * framesInSeq_ .... number of frames in the whole sequencer. */
-
-       framesInLoop_ = (conf::samplerate * (60.0f / bpm_)) * beats_;
-       framesInBar_  = framesInLoop_ / bars_;
-       framesInBeat_ = framesInLoop_ / beats_;
-       framesInSeq_  = framesInBeat_ * G_MAX_BEATS;
-
-       updateQuanto_();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void sendMIDIsync()
 {
+       model::ClockLock lock(model::clock);
+       
+       const model::Clock* c = model::clock.get();
+       
        /* Sending MIDI sync while waiting is meaningless. */
 
-       if (status_.load() == ClockStatus::WAITING)
+       if (c->status == ClockStatus::WAITING)
                return;
 
+       int currentFrame = currentFrame_.load();
+
        /* TODO - only Master (_M) is implemented so far. */
 
        if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
-               if (currentFrame_.load() % (framesInBeat_/24) == 0)
+               if (currentFrame % (c->framesInBeat / 24) == 0)
                        kernelMidi::send(MIDI_CLOCK, -1, -1);
                return;
        }
@@ -278,7 +301,7 @@ void sendMIDIsync()
                 * 1-4 and 5-8. We check timecode frame's parity: if even, send
                 * range 1-4, if odd send 5-8. */
 
-               if (currentFrame_.load() % midiTCrate_ != 0)  // no timecode frame passed
+               if (currentFrame % midiTCrate_ != 0)  // no timecode frame passed
                        return;
 
                /* frame low nibble
@@ -321,7 +344,7 @@ void sendMIDIsync()
                                        midiTCminutes_ = 0;
                                }
                        }
-                       //gu_log("%d:%d:%d:%d\n", midiTChours_, midiTCminutes_, midiTCseconds_, midiTCframes_);
+                       //u::log::print("%d:%d:%d:%d\n", midiTChours_, midiTCminutes_, midiTCseconds_, midiTCframes_);
                }
        }
 }
@@ -354,20 +377,23 @@ void sendMIDIrewind()
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef G_OS_LINUX
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
 
 void recvJackSync()
 {
+       /* TODO - these things should be processed by a higher level, 
+       above clock:: ----> clockManager */
+
        kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
 
        if (jackState.running != jackStatePrev_.running) {
                if (jackState.running) {
                        if (!isRunning())
-                               c::transport::startSeq(false); // not from UI
+                               mh::startSequencer();
                }
                else {
                        if (isRunning())
-                               c::transport::stopSeq(false); // not from UI
+                               mh::stopSequencer();
                }
        }
        if (jackState.bpm != jackStatePrev_.bpm)
@@ -375,7 +401,7 @@ void recvJackSync()
                        c::main::setBpm(jackState.bpm);
 
        if (jackState.frame == 0 && jackState.frame != jackStatePrev_.frame)
-               c::transport::rewindSeq(false, false);  // not from UI, don't notify jack (avoid loop)
+               mh::rewindSequencer();
 
        jackStatePrev_ = jackState;
 }
@@ -388,82 +414,27 @@ void recvJackSync()
 
 bool canQuantize()
 {
-       return getQuantize() > 0 && isRunning();
+       model::ClockLock lock(model::clock);
+       
+       const model::Clock* c = model::clock.get();
+       
+       return c->quantize > 0 && c->status == ClockStatus::RUNNING;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int getCurrentFrame()
-{
-       return currentFrame_.load();
-}
-
-
-int getFramesInLoop()
-{
-       return framesInLoop_;
-}
-
-
-int getCurrentBeat()
-{
-       return currentBeat_.load();
-}
-
-
-int getQuantize()
-{
-       return quantize_.load();
-}
-
-
-float getBpm()
-{
-       return bpm_;
-}
-
-
-int getBeats()
-{
-       return beats_;
-}
-
-
-int getBars()
-{
-       return bars_;
-}
-
-
-int getQuanto()
-{
-       return quanto_;
-}
-
-
-int getFramesInBar()
-{
-       return framesInBar_;
-}
-
-
-int getFramesInBeat()
-{
-       return framesInBeat_;
-}
-
-
-int getFramesInSeq()
-{
-       return framesInSeq_;
-}
-
-
-ClockStatus getStatus()
-{
-       return status_;
-}
-
+int         getCurrentFrame() { return currentFrame_.load(); }
+int         getCurrentBeat()  { return currentBeat_.load(); }
+int         getQuanto()       { return quanto_; }
+ClockStatus getStatus()       { model::ClockLock lock(model::clock); return model::clock.get()->status; }
+int         getFramesInLoop() { model::ClockLock lock(model::clock); return model::clock.get()->framesInLoop; }
+int         getFramesInBar()  { model::ClockLock lock(model::clock); return model::clock.get()->framesInBar; }
+int         getFramesInBeat() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBeat; }
+int         getFramesInSeq()  { model::ClockLock lock(model::clock); return model::clock.get()->framesInSeq; }
+int         getQuantize()     { model::ClockLock lock(model::clock); return model::clock.get()->quantize; }
+float       getBpm()          { model::ClockLock lock(model::clock); return model::clock.get()->bpm; }
+int         getBeats()        { model::ClockLock lock(model::clock); return model::clock.get()->beats; }
+int         getBars()         { model::ClockLock lock(model::clock); return model::clock.get()->bars; }
 }}}; // giada::m::clock::
index 3f5e9fb56b3e441adfb54691af3e404c9b782f2c..21b72d1cd8b14a580bf89a0077b815c46145958c 100644 (file)
@@ -48,7 +48,7 @@ Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */
 
 void sendMIDIrewind();
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 void recvJackSync();
 #endif
 
@@ -80,14 +80,8 @@ Whether the quantizer value is > 0 and the clock is running. */
 
 bool canQuantize();
 
-/* updateFrameBars
-Updates bpm, frames, beats and so on. */
-
-void updateFrameBars();
-
 void setBpm(float b);
-void setBars(int b);
-void setBeats(int b);
+void setBeats(int beats, int bars);
 void setQuantize(int q);
 
 /* isRunning
index ce69e9faa1a43462e5eb2d02c9592cd18205f1a0..52899917f1d038eb2db839fe87c3b64b7ecf19ad 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <string>
 #include <FL/Fl.H>
-#include "../utils/fs.h"
-#include "../utils/log.h"
-#include "storager.h"
-#include "const.h"
-#include "types.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/json.h"
+#include "core/const.h"
+#include "core/types.h"
 #include "conf.h"
 
 
-using std::string;
-
-
 namespace giada {
 namespace m {
 namespace conf
 {
 namespace
 {
-string confFilePath = "";
-string confDirPath = "";
+std::string confFilePath = "";
+std::string confDirPath  = "";
 
 
 /* -------------------------------------------------------------------------- */
@@ -97,7 +95,7 @@ void sanitize()
        if (pluginChooserW < 640) pluginChooserW = 640;
        if (pluginChooserH < 480) pluginChooserW = 480;
 #endif
-  if (bpmX < 0) bpmX = 0;
+       if (bpmX < 0) bpmX = 0;
        if (bpmY < 0) bpmY = 0;
        if (beatsX < 0) beatsX = 0;
        if (beatsY < 0) beatsY = 0;
@@ -117,19 +115,19 @@ to OS. */
 
 int createConfigFolder()
 {
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
 
-       if (gu_dirExists(confDirPath))
+       if (u::fs::dirExists(confDirPath))
                return 1;
 
-       gu_log("[conf::createConfigFolder] .giada folder not present. Updating...\n");
+       u::log::print("[conf::createConfigFolder] .giada folder not present. Updating...\n");
 
-       if (gu_mkdir(confDirPath)) {
-               gu_log("[conf::createConfigFolder] status: ok\n");
+       if (u::fs::mkdir(confDirPath)) {
+               u::log::print("[conf::createConfigFolder] status: ok\n");
                return 1;
        }
        else {
-               gu_log("[conf::createConfigFolder] status: error!\n");
+               u::log::print("[conf::createConfigFolder] status: error!\n");
                return 0;
        }
 
@@ -148,7 +146,7 @@ int createConfigFolder()
 /* -------------------------------------------------------------------------- */
 
 
-string header = "GIADACFG";
+std::string header = "GIADACFG";
 
 int  logMode        = LOG_MODE_MUTE;
 int  soundSystem    = G_DEFAULT_SOUNDSYS;
@@ -161,47 +159,48 @@ int  buffersize     = G_DEFAULT_BUFSIZE;
 bool limitOutput    = false;
 int  rsmpQuality    = 0;
 
-int    midiSystem  = 0;
-int    midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
-int    midiPortIn  = G_DEFAULT_MIDI_PORT_IN;
-string midiMapPath = "";
-string lastFileMap = "";
-int    midiSync    = MIDI_SYNC_NONE;
-float  midiTCfps   = 25.0f;
-
-bool midiIn               = false;
-int midiInFilter          = -1;
-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;
+int         midiSystem  = 0;
+int         midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
+int         midiPortIn  = G_DEFAULT_MIDI_PORT_IN;
+std::string midiMapPath = "";
+std::string lastFileMap = "";
+int         midiSync    = MIDI_SYNC_NONE;
+float       midiTCfps   = 25.0f;
+
+/* TODO - move these into a RCUList */
+std::atomic<bool>     midiIn          (false);
+std::atomic<int>      midiInFilter    (-1);
+std::atomic<uint32_t> midiInRewind    (0x0);
+std::atomic<uint32_t> midiInStartStop (0x0);
+std::atomic<uint32_t> midiInActionRec (0x0);
+std::atomic<uint32_t> midiInInputRec  (0x0);
+std::atomic<uint32_t> midiInVolumeIn  (0x0);
+std::atomic<uint32_t> midiInVolumeOut (0x0);
+std::atomic<uint32_t> midiInBeatDouble(0x0);
+std::atomic<uint32_t> midiInBeatHalf  (0x0);
+std::atomic<uint32_t> midiInMetronome (0x0);
 
 bool recsStopOnChanHalt    = false;
 bool chansStopOnSeqHalt    = false;
 bool treatRecsAsLoops      = false;
 bool inputMonitorDefaultOn = false;
 
-string pluginPath = "";
-string patchPath  = "";
-string samplePath = "";
+std::string pluginPath = "";
+std::string patchPath  = "";
+std::string samplePath = "";
 
 int mainWindowX = (Fl::w() / 2) - (G_MIN_GUI_WIDTH / 2);
 int mainWindowY = (Fl::h() / 2) - (G_MIN_GUI_HEIGHT / 2);
 int mainWindowW = G_MIN_GUI_WIDTH;
 int mainWindowH = G_MIN_GUI_HEIGHT;
 
-int browserX         = 0;
-int browserY         = 0;
-int browserW         = 640;
-int browserH         = 480;
-int browserPosition  = 0;
-int browserLastValue = 0;
-string browserLastPath = "";
+int         browserX         = 0;
+int         browserY         = 0;
+int         browserW         = 640;
+int         browserH         = 480;
+int         browserPosition  = 0;
+int         browserLastValue = 0;
+std::string browserLastPath = "";
 
 int actionEditorX       = 0;
 int actionEditorY       = 0;
@@ -270,10 +269,10 @@ void init()
        /* 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__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
 
-       confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
-       confDirPath  = gu_getHomePath() + G_SLASH;
+       confFilePath = u::fs::getHomePath() + G_SLASH + CONF_FILENAME;
+       confDirPath  = u::fs::getHomePath() + G_SLASH;
 
 #elif defined(_WIN32)
 
@@ -296,233 +295,231 @@ bool isMidiInAllowed(int c)
 /* -------------------------------------------------------------------------- */
 
 
-int read()
+bool read()
 {
+       namespace uj = u::json;
+
        init();
 
-  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);
-    return 0;
-  }
+       json_t* j = uj::load(confFilePath);
+       if (j == nullptr)
+               return false;
 
-  if (!storager::checkObject(jRoot, "root element")) {
-               json_decref(jRoot);
-    return 0;
+       if (!uj::isObject(j)) {
+               json_decref(j);
+               return false;
        }
 
-       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::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::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::setBool(jRoot, CONF_KEY_MIDI_IN, midiIn)) return 0; 
-       if (!storager::setInt(jRoot, CONF_KEY_MIDI_IN_FILTER, midiInFilter)) 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_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_SAMPLE_ACTION_EDITOR_H, sampleActionEditorH)) return 0;
-       if (!storager::setInt(jRoot, CONF_KEY_VELOCITY_EDITOR_H, velocityEditorH)) return 0;
-       if (!storager::setInt(jRoot, CONF_KEY_ENVELOPE_EDITOR_H, envelopeEditorH)) 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_NAME_X, nameX)) return 0;
-       if (!storager::setInt(jRoot, CONF_KEY_NAME_Y, nameY)) 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;
-       if (!storager::setInt(jRoot, CONF_KEY_REC_TRIGGER_MODE, recTriggerMode)) return 0;
-       if (!storager::setFloat(jRoot, CONF_KEY_REC_TRIGGER_LEVEL, recTriggerLevel)) return 0;
+       header         = uj::readString(j, CONF_KEY_HEADER);
+       logMode        = uj::readInt(j, CONF_KEY_LOG_MODE);
+       soundSystem    = uj::readInt(j, CONF_KEY_SOUND_SYSTEM);
+       soundDeviceOut = uj::readInt(j, CONF_KEY_SOUND_DEVICE_OUT);
+       soundDeviceIn  = uj::readInt(j, CONF_KEY_SOUND_DEVICE_IN);
+       channelsOut    = uj::readInt(j, CONF_KEY_CHANNELS_OUT);
+       channelsIn     = uj::readInt(j, CONF_KEY_CHANNELS_IN);
+       samplerate     = uj::readInt(j, CONF_KEY_SAMPLERATE);
+       buffersize     = uj::readInt(j, CONF_KEY_BUFFER_SIZE);
+       limitOutput    = uj::readBool(j, CONF_KEY_LIMIT_OUTPUT);
+       rsmpQuality    = uj::readInt(j, CONF_KEY_RESAMPLE_QUALITY);
+       midiSystem     = uj::readInt(j, CONF_KEY_MIDI_SYSTEM);
+       midiPortOut    = uj::readInt(j, CONF_KEY_MIDI_PORT_OUT);
+       midiPortIn     = uj::readInt(j, CONF_KEY_MIDI_PORT_IN);
+       midiMapPath    = uj::readString(j, CONF_KEY_MIDIMAP_PATH);
+       lastFileMap    = uj::readString(j, CONF_KEY_LAST_MIDIMAP);
+       midiSync       = uj::readInt(j, CONF_KEY_MIDI_SYNC);
+       midiTCfps      = uj::readFloat(j, CONF_KEY_MIDI_TC_FPS);
+       midiIn           = uj::readBool(j, CONF_KEY_MIDI_IN);
+       midiInFilter     = uj::readInt(j, CONF_KEY_MIDI_IN_FILTER)
+       midiInRewind     = uj::readInt(j, CONF_KEY_MIDI_IN_REWIND);
+       midiInStartStop  = uj::readInt(j, CONF_KEY_MIDI_IN_START_STOP);
+       midiInActionRec  = uj::readInt(j, CONF_KEY_MIDI_IN_ACTION_REC);
+       midiInInputRec   = uj::readInt(j, CONF_KEY_MIDI_IN_INPUT_REC);
+       midiInMetronome  = uj::readInt(j, CONF_KEY_MIDI_IN_METRONOME);
+       midiInVolumeIn   = uj::readInt(j, CONF_KEY_MIDI_IN_VOLUME_IN);
+       midiInVolumeOut  = uj::readInt(j, CONF_KEY_MIDI_IN_VOLUME_OUT);
+       midiInBeatDouble = uj::readInt(j, CONF_KEY_MIDI_IN_BEAT_DOUBLE);
+       midiInBeatHalf   = uj::readInt(j, CONF_KEY_MIDI_IN_BEAT_HALF);
+       recsStopOnChanHalt = uj::readBool(j, CONF_KEY_RECS_STOP_ON_CHAN_HALT);
+       chansStopOnSeqHalt = uj::readBool(j, CONF_KEY_CHANS_STOP_ON_SEQ_HALT);
+       treatRecsAsLoops = uj::readBool(j, CONF_KEY_TREAT_RECS_AS_LOOPS);
+       inputMonitorDefaultOn = uj::readBool(j, CONF_KEY_INPUT_MONITOR_DEFAULT_ON);
+       pluginPath = uj::readString(j, CONF_KEY_PLUGINS_PATH);
+       patchPath = uj::readString(j, CONF_KEY_PATCHES_PATH);
+       samplePath = uj::readString(j, CONF_KEY_SAMPLES_PATH);
+       mainWindowX = uj::readInt(j, CONF_KEY_MAIN_WINDOW_X);
+       mainWindowY = uj::readInt(j, CONF_KEY_MAIN_WINDOW_Y);
+       mainWindowW = uj::readInt(j, CONF_KEY_MAIN_WINDOW_W);
+       mainWindowH = uj::readInt(j, CONF_KEY_MAIN_WINDOW_H);
+       browserX = uj::readInt(j, CONF_KEY_BROWSER_X);
+       browserY = uj::readInt(j, CONF_KEY_BROWSER_Y);
+       browserW = uj::readInt(j, CONF_KEY_BROWSER_W);
+       browserH = uj::readInt(j, CONF_KEY_BROWSER_H);
+       browserPosition = uj::readInt(j, CONF_KEY_BROWSER_POSITION);
+       browserLastPath = uj::readString(j, CONF_KEY_BROWSER_LAST_PATH);
+       browserLastValue = uj::readInt(j, CONF_KEY_BROWSER_LAST_VALUE);
+       actionEditorX = uj::readInt(j, CONF_KEY_ACTION_EDITOR_X);
+       actionEditorY = uj::readInt(j, CONF_KEY_ACTION_EDITOR_Y);
+       actionEditorW = uj::readInt(j, CONF_KEY_ACTION_EDITOR_W);
+       actionEditorH = uj::readInt(j, CONF_KEY_ACTION_EDITOR_H);
+       actionEditorZoom = uj::readInt(j, CONF_KEY_ACTION_EDITOR_ZOOM);
+       actionEditorGridVal = uj::readInt(j, CONF_KEY_ACTION_EDITOR_GRID_VAL);
+       actionEditorGridOn = uj::readInt(j, CONF_KEY_ACTION_EDITOR_GRID_ON);
+       sampleEditorX = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_X);
+       sampleEditorY = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_Y);
+       sampleEditorW = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_W);
+       sampleEditorH = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_H);
+       sampleEditorGridVal = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_GRID_VAL);
+       sampleEditorGridOn = uj::readInt(j, CONF_KEY_SAMPLE_EDITOR_GRID_ON);
+       pianoRollY = uj::readInt(j, CONF_KEY_PIANO_ROLL_Y);
+       pianoRollH = uj::readInt(j, CONF_KEY_PIANO_ROLL_H);
+       sampleActionEditorH = uj::readInt(j, CONF_KEY_SAMPLE_ACTION_EDITOR_H);
+       velocityEditorH = uj::readInt(j, CONF_KEY_VELOCITY_EDITOR_H);
+       envelopeEditorH = uj::readInt(j, CONF_KEY_ENVELOPE_EDITOR_H);
+       pluginListX = uj::readInt(j, CONF_KEY_PLUGIN_LIST_X);
+       pluginListY = uj::readInt(j, CONF_KEY_PLUGIN_LIST_Y);
+       configX = uj::readInt(j, CONF_KEY_CONFIG_X);
+       configY = uj::readInt(j, CONF_KEY_CONFIG_Y);
+       bpmX = uj::readInt(j, CONF_KEY_BPM_X);
+       bpmY = uj::readInt(j, CONF_KEY_BPM_Y);
+       beatsX = uj::readInt(j, CONF_KEY_BEATS_X);
+       beatsY = uj::readInt(j, CONF_KEY_BEATS_Y);
+       aboutX = uj::readInt(j, CONF_KEY_ABOUT_X);
+       aboutY = uj::readInt(j, CONF_KEY_ABOUT_Y);
+       nameX = uj::readInt(j, CONF_KEY_NAME_X);
+       nameY = uj::readInt(j, CONF_KEY_NAME_Y);
+       midiInputX = uj::readInt(j, CONF_KEY_MIDI_INPUT_X);
+       midiInputY = uj::readInt(j, CONF_KEY_MIDI_INPUT_Y);
+       midiInputW = uj::readInt(j, CONF_KEY_MIDI_INPUT_W);
+       midiInputH = uj::readInt(j, CONF_KEY_MIDI_INPUT_H);
+       recTriggerMode = uj::readInt(j, CONF_KEY_REC_TRIGGER_MODE);
+       recTriggerLevel = uj::readFloat(j, CONF_KEY_REC_TRIGGER_LEVEL);
 
 #ifdef WITH_VST
 
-       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;
+       pluginChooserX   = uj::readInt(j, CONF_KEY_PLUGIN_CHOOSER_X);
+       pluginChooserY   = uj::readInt(j, CONF_KEY_PLUGIN_CHOOSER_Y);
+       pluginChooserW   = uj::readInt(j, CONF_KEY_PLUGIN_CHOOSER_W);
+       pluginChooserH   = uj::readInt(j, CONF_KEY_PLUGIN_CHOOSER_H);
+       pluginSortMethod = uj::readInt(j, CONF_KEY_PLUGIN_SORT_METHOD);
 
 #endif
 
-       json_decref(jRoot);
+       json_decref(j);
 
        sanitize();
 
-       return 1;
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int write()
+bool write()
 {
        if (!createConfigFolder())
-               return 0;
-
-       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));
-       json_object_set_new(jRoot, CONF_KEY_SOUND_SYSTEM,              json_integer(soundSystem));
-       json_object_set_new(jRoot, CONF_KEY_SOUND_DEVICE_OUT,          json_integer(soundDeviceOut));
-       json_object_set_new(jRoot, CONF_KEY_SOUND_DEVICE_IN,           json_integer(soundDeviceIn));
-       json_object_set_new(jRoot, CONF_KEY_CHANNELS_OUT,              json_integer(channelsOut));
-       json_object_set_new(jRoot, CONF_KEY_CHANNELS_IN,               json_integer(channelsIn));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLERATE,                json_integer(samplerate));
-       json_object_set_new(jRoot, CONF_KEY_BUFFER_SIZE,               json_integer(buffersize));
-       json_object_set_new(jRoot, CONF_KEY_LIMIT_OUTPUT,              json_boolean(limitOutput));
-       json_object_set_new(jRoot, CONF_KEY_RESAMPLE_QUALITY,          json_integer(rsmpQuality));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_SYSTEM,               json_integer(midiSystem));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_PORT_OUT,             json_integer(midiPortOut));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_PORT_IN,              json_integer(midiPortIn));
-       json_object_set_new(jRoot, CONF_KEY_MIDIMAP_PATH,              json_string(midiMapPath.c_str()));
-       json_object_set_new(jRoot, CONF_KEY_LAST_MIDIMAP,              json_string(lastFileMap.c_str()));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_SYNC,                 json_integer(midiSync));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_TC_FPS,               json_real(midiTCfps));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN,                   json_boolean(midiIn));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_FILTER,            json_integer(midiInFilter));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_REWIND,            json_integer(midiInRewind));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_START_STOP,        json_integer(midiInStartStop));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_ACTION_REC,        json_integer(midiInActionRec));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_INPUT_REC,         json_integer(midiInInputRec));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_METRONOME,         json_integer(midiInMetronome));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_VOLUME_IN,         json_integer(midiInVolumeIn));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_VOLUME_OUT,        json_integer(midiInVolumeOut));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_BEAT_DOUBLE,       json_integer(midiInBeatDouble));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_IN_BEAT_HALF,         json_integer(midiInBeatHalf));
-       json_object_set_new(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT,    json_boolean(recsStopOnChanHalt));
-       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_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()));
-       json_object_set_new(jRoot, CONF_KEY_MAIN_WINDOW_X,             json_integer(mainWindowX));
-       json_object_set_new(jRoot, CONF_KEY_MAIN_WINDOW_Y,             json_integer(mainWindowY));
-       json_object_set_new(jRoot, CONF_KEY_MAIN_WINDOW_W,             json_integer(mainWindowW));
-       json_object_set_new(jRoot, CONF_KEY_MAIN_WINDOW_H,             json_integer(mainWindowH));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_X,                 json_integer(browserX));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_Y,                 json_integer(browserY));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_W,                 json_integer(browserW));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_H,                 json_integer(browserH));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_POSITION,          json_integer(browserPosition));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_LAST_PATH,         json_string(browserLastPath.c_str()));
-       json_object_set_new(jRoot, CONF_KEY_BROWSER_LAST_VALUE,        json_integer(browserLastValue));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_X,           json_integer(actionEditorX));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_Y,           json_integer(actionEditorY));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_W,           json_integer(actionEditorW));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_H,           json_integer(actionEditorH));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_ZOOM,        json_integer(actionEditorZoom));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_GRID_VAL,    json_integer(actionEditorGridVal));
-       json_object_set_new(jRoot, CONF_KEY_ACTION_EDITOR_GRID_ON,     json_integer(actionEditorGridOn));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_X,           json_integer(sampleEditorX));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_Y,           json_integer(sampleEditorY));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_W,           json_integer(sampleEditorW));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_H,           json_integer(sampleEditorH));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_VAL,    json_integer(sampleEditorGridVal));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON,     json_integer(sampleEditorGridOn));
-       json_object_set_new(jRoot, CONF_KEY_PIANO_ROLL_Y,              json_integer(pianoRollY));
-       json_object_set_new(jRoot, CONF_KEY_PIANO_ROLL_H,              json_integer(pianoRollH));
-       json_object_set_new(jRoot, CONF_KEY_SAMPLE_ACTION_EDITOR_H,    json_integer(sampleActionEditorH));
-       json_object_set_new(jRoot, CONF_KEY_VELOCITY_EDITOR_H,         json_integer(velocityEditorH));
-       json_object_set_new(jRoot, CONF_KEY_ENVELOPE_EDITOR_H,         json_integer(envelopeEditorH));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_LIST_X,             json_integer(pluginListX));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_LIST_Y,             json_integer(pluginListY));
-       json_object_set_new(jRoot, CONF_KEY_CONFIG_X,                  json_integer(configX));
-       json_object_set_new(jRoot, CONF_KEY_CONFIG_Y,                  json_integer(configY));
-       json_object_set_new(jRoot, CONF_KEY_BPM_X,                     json_integer(bpmX));
-       json_object_set_new(jRoot, CONF_KEY_BPM_Y,                     json_integer(bpmY));
-       json_object_set_new(jRoot, CONF_KEY_BEATS_X,                   json_integer(beatsX));
-       json_object_set_new(jRoot, CONF_KEY_BEATS_Y,                   json_integer(beatsY));
-       json_object_set_new(jRoot, CONF_KEY_ABOUT_X,                   json_integer(aboutX));
-       json_object_set_new(jRoot, CONF_KEY_ABOUT_Y,                   json_integer(aboutY));   
-       json_object_set_new(jRoot, CONF_KEY_NAME_X,                    json_integer(nameX));
-       json_object_set_new(jRoot, CONF_KEY_NAME_Y,                    json_integer(nameY));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_X,              json_integer(midiInputX));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_Y,              json_integer(midiInputY));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_W,              json_integer(midiInputW));
-       json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_H,              json_integer(midiInputH));
-       json_object_set_new(jRoot, CONF_KEY_REC_TRIGGER_MODE,          json_integer(recTriggerMode));
-       json_object_set_new(jRoot, CONF_KEY_REC_TRIGGER_LEVEL,         json_real(recTriggerLevel));
+               return false;
+
+       json_t* j = json_object();
+
+       json_object_set_new(j, CONF_KEY_HEADER,                    json_string(header.c_str()));
+       json_object_set_new(j, CONF_KEY_LOG_MODE,                  json_integer(logMode));
+       json_object_set_new(j, CONF_KEY_SOUND_SYSTEM,              json_integer(soundSystem));
+       json_object_set_new(j, CONF_KEY_SOUND_DEVICE_OUT,          json_integer(soundDeviceOut));
+       json_object_set_new(j, CONF_KEY_SOUND_DEVICE_IN,           json_integer(soundDeviceIn));
+       json_object_set_new(j, CONF_KEY_CHANNELS_OUT,              json_integer(channelsOut));
+       json_object_set_new(j, CONF_KEY_CHANNELS_IN,               json_integer(channelsIn));
+       json_object_set_new(j, CONF_KEY_SAMPLERATE,                json_integer(samplerate));
+       json_object_set_new(j, CONF_KEY_BUFFER_SIZE,               json_integer(buffersize));
+       json_object_set_new(j, CONF_KEY_LIMIT_OUTPUT,              json_boolean(limitOutput));
+       json_object_set_new(j, CONF_KEY_RESAMPLE_QUALITY,          json_integer(rsmpQuality));
+       json_object_set_new(j, CONF_KEY_MIDI_SYSTEM,               json_integer(midiSystem));
+       json_object_set_new(j, CONF_KEY_MIDI_PORT_OUT,             json_integer(midiPortOut));
+       json_object_set_new(j, CONF_KEY_MIDI_PORT_IN,              json_integer(midiPortIn));
+       json_object_set_new(j, CONF_KEY_MIDIMAP_PATH,              json_string(midiMapPath.c_str()));
+       json_object_set_new(j, CONF_KEY_LAST_MIDIMAP,              json_string(lastFileMap.c_str()));
+       json_object_set_new(j, CONF_KEY_MIDI_SYNC,                 json_integer(midiSync));
+       json_object_set_new(j, CONF_KEY_MIDI_TC_FPS,               json_real(midiTCfps));
+       json_object_set_new(j, CONF_KEY_MIDI_IN,                   json_boolean(midiIn));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_FILTER,            json_integer(midiInFilter));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_REWIND,            json_integer(midiInRewind));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_START_STOP,        json_integer(midiInStartStop));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_ACTION_REC,        json_integer(midiInActionRec));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_INPUT_REC,         json_integer(midiInInputRec));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_METRONOME,         json_integer(midiInMetronome));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_VOLUME_IN,         json_integer(midiInVolumeIn));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_VOLUME_OUT,        json_integer(midiInVolumeOut));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_BEAT_DOUBLE,       json_integer(midiInBeatDouble));
+       json_object_set_new(j, CONF_KEY_MIDI_IN_BEAT_HALF,         json_integer(midiInBeatHalf));
+       json_object_set_new(j, CONF_KEY_RECS_STOP_ON_CHAN_HALT,    json_boolean(recsStopOnChanHalt));
+       json_object_set_new(j, CONF_KEY_CHANS_STOP_ON_SEQ_HALT,    json_boolean(chansStopOnSeqHalt));
+       json_object_set_new(j, CONF_KEY_TREAT_RECS_AS_LOOPS,       json_boolean(treatRecsAsLoops));
+       json_object_set_new(j, CONF_KEY_INPUT_MONITOR_DEFAULT_ON,  json_boolean(inputMonitorDefaultOn));
+       json_object_set_new(j, CONF_KEY_PLUGINS_PATH,              json_string(pluginPath.c_str()));
+       json_object_set_new(j, CONF_KEY_PATCHES_PATH,              json_string(patchPath.c_str()));
+       json_object_set_new(j, CONF_KEY_SAMPLES_PATH,              json_string(samplePath.c_str()));
+       json_object_set_new(j, CONF_KEY_MAIN_WINDOW_X,             json_integer(mainWindowX));
+       json_object_set_new(j, CONF_KEY_MAIN_WINDOW_Y,             json_integer(mainWindowY));
+       json_object_set_new(j, CONF_KEY_MAIN_WINDOW_W,             json_integer(mainWindowW));
+       json_object_set_new(j, CONF_KEY_MAIN_WINDOW_H,             json_integer(mainWindowH));
+       json_object_set_new(j, CONF_KEY_BROWSER_X,                 json_integer(browserX));
+       json_object_set_new(j, CONF_KEY_BROWSER_Y,                 json_integer(browserY));
+       json_object_set_new(j, CONF_KEY_BROWSER_W,                 json_integer(browserW));
+       json_object_set_new(j, CONF_KEY_BROWSER_H,                 json_integer(browserH));
+       json_object_set_new(j, CONF_KEY_BROWSER_POSITION,          json_integer(browserPosition));
+       json_object_set_new(j, CONF_KEY_BROWSER_LAST_PATH,         json_string(browserLastPath.c_str()));
+       json_object_set_new(j, CONF_KEY_BROWSER_LAST_VALUE,        json_integer(browserLastValue));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_X,           json_integer(actionEditorX));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_Y,           json_integer(actionEditorY));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_W,           json_integer(actionEditorW));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_H,           json_integer(actionEditorH));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_ZOOM,        json_integer(actionEditorZoom));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_GRID_VAL,    json_integer(actionEditorGridVal));
+       json_object_set_new(j, CONF_KEY_ACTION_EDITOR_GRID_ON,     json_integer(actionEditorGridOn));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_X,           json_integer(sampleEditorX));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_Y,           json_integer(sampleEditorY));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_W,           json_integer(sampleEditorW));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_H,           json_integer(sampleEditorH));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_GRID_VAL,    json_integer(sampleEditorGridVal));
+       json_object_set_new(j, CONF_KEY_SAMPLE_EDITOR_GRID_ON,     json_integer(sampleEditorGridOn));
+       json_object_set_new(j, CONF_KEY_PIANO_ROLL_Y,              json_integer(pianoRollY));
+       json_object_set_new(j, CONF_KEY_PIANO_ROLL_H,              json_integer(pianoRollH));
+       json_object_set_new(j, CONF_KEY_SAMPLE_ACTION_EDITOR_H,    json_integer(sampleActionEditorH));
+       json_object_set_new(j, CONF_KEY_VELOCITY_EDITOR_H,         json_integer(velocityEditorH));
+       json_object_set_new(j, CONF_KEY_ENVELOPE_EDITOR_H,         json_integer(envelopeEditorH));
+       json_object_set_new(j, CONF_KEY_PLUGIN_LIST_X,             json_integer(pluginListX));
+       json_object_set_new(j, CONF_KEY_PLUGIN_LIST_Y,             json_integer(pluginListY));
+       json_object_set_new(j, CONF_KEY_CONFIG_X,                  json_integer(configX));
+       json_object_set_new(j, CONF_KEY_CONFIG_Y,                  json_integer(configY));
+       json_object_set_new(j, CONF_KEY_BPM_X,                     json_integer(bpmX));
+       json_object_set_new(j, CONF_KEY_BPM_Y,                     json_integer(bpmY));
+       json_object_set_new(j, CONF_KEY_BEATS_X,                   json_integer(beatsX));
+       json_object_set_new(j, CONF_KEY_BEATS_Y,                   json_integer(beatsY));
+       json_object_set_new(j, CONF_KEY_ABOUT_X,                   json_integer(aboutX));
+       json_object_set_new(j, CONF_KEY_ABOUT_Y,                   json_integer(aboutY));       
+       json_object_set_new(j, CONF_KEY_NAME_X,                    json_integer(nameX));
+       json_object_set_new(j, CONF_KEY_NAME_Y,                    json_integer(nameY));
+       json_object_set_new(j, CONF_KEY_MIDI_INPUT_X,              json_integer(midiInputX));
+       json_object_set_new(j, CONF_KEY_MIDI_INPUT_Y,              json_integer(midiInputY));
+       json_object_set_new(j, CONF_KEY_MIDI_INPUT_W,              json_integer(midiInputW));
+       json_object_set_new(j, CONF_KEY_MIDI_INPUT_H,              json_integer(midiInputH));
+       json_object_set_new(j, CONF_KEY_REC_TRIGGER_MODE,          json_integer(recTriggerMode));
+       json_object_set_new(j, CONF_KEY_REC_TRIGGER_LEVEL,         json_real(recTriggerLevel));
 
 #ifdef WITH_VST
 
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_CHOOSER_X,   json_integer(pluginChooserX));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_CHOOSER_Y,   json_integer(pluginChooserY));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_CHOOSER_W,   json_integer(pluginChooserW));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_CHOOSER_H,   json_integer(pluginChooserH));
-       json_object_set_new(jRoot, CONF_KEY_PLUGIN_SORT_METHOD, json_integer(pluginSortMethod));
+       json_object_set_new(j, CONF_KEY_PLUGIN_CHOOSER_X,   json_integer(pluginChooserX));
+       json_object_set_new(j, CONF_KEY_PLUGIN_CHOOSER_Y,   json_integer(pluginChooserY));
+       json_object_set_new(j, CONF_KEY_PLUGIN_CHOOSER_W,   json_integer(pluginChooserW));
+       json_object_set_new(j, CONF_KEY_PLUGIN_CHOOSER_H,   json_integer(pluginChooserH));
+       json_object_set_new(j, CONF_KEY_PLUGIN_SORT_METHOD, json_integer(pluginSortMethod));
 
 #endif
 
-  if (json_dump_file(jRoot, confFilePath.c_str(), JSON_INDENT(2)) != 0) {
-    gu_log("[conf::write] unable to write configuration file!\n");
-    return 0;
+  if (json_dump_file(j, confFilePath.c_str(), JSON_INDENT(2)) != 0) {
+       u::log::print("[conf::write] unable to write configuration file!\n");
+       return false;
   }
-  return 1;
+  return true;
 }
 }}}; // giada::m::conf::
index b7aca0f77ec6532ed0e84d490f0adef3d47c8628..5cbf1d2021524e0939d1d57563f375fffe7f0159 100644 (file)
@@ -29,6 +29,7 @@
 #define G_CONF_H
 
 
+#include <atomic>
 #include <string>
 
 
@@ -37,8 +38,8 @@ namespace m {
 namespace conf
 {
 void init();
-int read();
-int write();
+bool read();
+bool write();
 
 /* isMidiAllowed
 Given a MIDI channel 'c' tells whether this channel should be allowed to receive
@@ -67,17 +68,17 @@ extern std::string lastFileMap;
 extern int   midiSync;  // see const.h
 extern float midiTCfps;
 
-extern bool midiIn;
-extern int midiInFilter;
-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 std::atomic<bool>     midiIn;
+extern std::atomic<int>      midiInFilter;
+extern std::atomic<uint32_t> midiInRewind;
+extern std::atomic<uint32_t> midiInStartStop;
+extern std::atomic<uint32_t> midiInActionRec;
+extern std::atomic<uint32_t> midiInInputRec;
+extern std::atomic<uint32_t> midiInMetronome;
+extern std::atomic<uint32_t> midiInVolumeIn;
+extern std::atomic<uint32_t> midiInVolumeOut;
+extern std::atomic<uint32_t> midiInBeatDouble;
+extern std::atomic<uint32_t> midiInBeatHalf;
 
 extern bool recsStopOnChanHalt;
 extern bool chansStopOnSeqHalt;
index 0bf91075bcb34cc2ba7f8d57537ad14e428e7f4a..2597956f6b74b32b9ab5b10b1591d42583c7c3bf 100644 (file)
@@ -36,6 +36,8 @@
        #define G_OS_MAC
 #elif defined(__linux__)
        #define G_OS_LINUX
+#elif defined(__FreeBSD__)
+       #define G_OS_FREEBSD
 #endif
 
 #ifndef BUILD_DATE
 
 /* -- version --------------------------------------------------------------- */
 constexpr auto G_APP_NAME      = "Giada";
-constexpr auto G_VERSION_STR   = "0.15.4";
+constexpr auto G_VERSION_STR   = "0.16.1";
 constexpr int  G_VERSION_MAJOR = 0;
-constexpr int  G_VERSION_MINOR = 15;
-constexpr int  G_VERSION_PATCH = 4;
+constexpr int  G_VERSION_MINOR = 16;
+constexpr int  G_VERSION_PATCH = 1;
 
 constexpr auto CONF_FILENAME = "giada.conf";
 
@@ -63,56 +65,55 @@ constexpr auto CONF_FILENAME = "giada.conf";
 
 
 /* -- GUI ------------------------------------------------------------------- */
-#define G_GUI_REFRESH_RATE   1000/24
-#define G_GUI_PLUGIN_RATE    0.05  // refresh rate for plugin GUI
-#define G_GUI_FONT_SIZE_BASE 12
-#define G_GUI_INNER_MARGIN   4
-#define G_GUI_OUTER_MARGIN   8
-#define G_GUI_UNIT           20    // base unit for elements 
-#define G_GUI_CHANNEL_H_1    G_GUI_UNIT
-#define G_GUI_CHANNEL_H_2    G_GUI_UNIT * 2
-#define G_GUI_CHANNEL_H_3    G_GUI_UNIT * 4
-#define G_GUI_CHANNEL_H_4    G_GUI_UNIT * 6
-#define G_GUI_ZOOM_FACTOR    2
-
+constexpr float G_GUI_REFRESH_RATE   = 0.05;
+constexpr float G_GUI_PLUGIN_RATE    = 0.05;   // refresh rate for plugin GUI
+constexpr int   G_GUI_FONT_SIZE_BASE = 12;
+constexpr int   G_GUI_INNER_MARGIN   = 4;
+constexpr int   G_GUI_OUTER_MARGIN   = 8;
+constexpr int   G_GUI_UNIT           = 20;    // base unit for elements 
+constexpr int   G_GUI_CHANNEL_H_1    = G_GUI_UNIT;
+constexpr int   G_GUI_CHANNEL_H_2    = G_GUI_UNIT * 2;
+constexpr int   G_GUI_CHANNEL_H_3    = G_GUI_UNIT * 4;
+constexpr int   G_GUI_CHANNEL_H_4    = G_GUI_UNIT * 6;
+constexpr int   G_GUI_ZOOM_FACTOR    = 2;
 
 #define G_COLOR_RED       fl_rgb_color(28,  32,  80)
 #define G_COLOR_BLUE      fl_rgb_color(113, 31,  31)
 #define G_COLOR_RED_ALERT fl_rgb_color(239, 75,  53)
-
-#define G_COLOR_LIGHT_2  fl_rgb_color(200, 200, 200)
-#define G_COLOR_LIGHT_1  fl_rgb_color(170, 170, 170)
-#define G_COLOR_GREY_4   fl_rgb_color(78,  78,  78)
-#define G_COLOR_GREY_3   fl_rgb_color(54,  54,  54)
-#define G_COLOR_GREY_2   fl_rgb_color(37,  37,  37)
-#define G_COLOR_GREY_1_5 fl_rgb_color(28,  28,  28)
-#define G_COLOR_GREY_1   fl_rgb_color(25,  25,  25)
-#define G_COLOR_BLACK    fl_rgb_color(0,   0,   0)
+#define G_COLOR_LIGHT_2   fl_rgb_color(200, 200, 200)
+#define G_COLOR_LIGHT_1   fl_rgb_color(170, 170, 170)
+#define G_COLOR_GREY_4    fl_rgb_color(78,  78,  78)
+#define G_COLOR_GREY_3    fl_rgb_color(54,  54,  54)
+#define G_COLOR_GREY_2    fl_rgb_color(37,  37,  37)
+#define G_COLOR_GREY_1_5  fl_rgb_color(28,  28,  28)
+#define G_COLOR_GREY_1    fl_rgb_color(25,  25,  25)
+#define G_COLOR_BLACK     fl_rgb_color(0,   0,   0)
 
 
 
 /* -- MIN/MAX values -------------------------------------------------------- */
-constexpr float G_MIN_BPM          = 20.0f;
-constexpr auto  G_MIN_BPM_STR      = "20.0";
-constexpr float G_MAX_BPM          = 999.0f;
-constexpr auto  G_MAX_BPM_STR      = "999.0";
-constexpr int   G_MAX_BEATS        = 32;
-constexpr int   G_MAX_BARS         = 32;
-constexpr int   G_MAX_QUANTIZE     = 8;
-constexpr float G_MIN_DB_SCALE     = 60.0f;
-constexpr int   G_MIN_COLUMN_WIDTH = 140;
-constexpr float G_MAX_BOOST_DB     = 20.0f;
-constexpr float G_MIN_PITCH        = 0.1f;
-constexpr float G_MAX_PITCH        = 4.0f;
-constexpr int   G_MAX_GRID_VAL     = 64;
-constexpr int   G_MIN_BUF_SIZE     = 8;
-constexpr int   G_MAX_BUF_SIZE     = 4096;
-constexpr int   G_MIN_GUI_WIDTH    = 816;
-constexpr int   G_MIN_GUI_HEIGHT   = 510;
-constexpr int   G_MAX_IO_CHANS     = 2;
-constexpr int   G_MAX_VELOCITY     = 0x7F;
-constexpr int   G_MAX_MIDI_CHANS   = 16;
-constexpr int   G_MAX_POLYPHONY    = 32;
+constexpr float  G_MIN_BPM          = 20.0f;
+constexpr auto   G_MIN_BPM_STR      = "20.0";
+constexpr float  G_MAX_BPM          = 999.0f;
+constexpr auto   G_MAX_BPM_STR      = "999.0";
+constexpr int    G_MAX_BEATS        = 32;
+constexpr int    G_MAX_BARS         = 32;
+constexpr int    G_MAX_QUANTIZE     = 8;
+constexpr float  G_MIN_DB_SCALE     = 60.0f;
+constexpr int    G_MIN_COLUMN_WIDTH = 140;
+constexpr float  G_MAX_BOOST_DB     = 20.0f;
+constexpr float  G_MIN_PITCH        = 0.1f;
+constexpr float  G_MAX_PITCH        = 4.0f;
+constexpr float  G_MAX_VOLUME       = 1.0f;
+constexpr int    G_MAX_GRID_VAL     = 64;
+constexpr int    G_MIN_BUF_SIZE     = 8;
+constexpr int    G_MAX_BUF_SIZE     = 4096;
+constexpr int    G_MIN_GUI_WIDTH    = 816;
+constexpr int    G_MIN_GUI_HEIGHT   = 510;
+constexpr int    G_MAX_IO_CHANS     = 2;
+constexpr int    G_MAX_VELOCITY     = 0x7F;
+constexpr int    G_MAX_MIDI_CHANS   = 16;
+constexpr int    G_MAX_POLYPHONY    = 32;
 
 
 
@@ -138,6 +139,8 @@ constexpr int G_MIDI_API_ALSA = 0x02;  // 0000 0010
 /* -- default system -------------------------------------------------------- */
 #if defined(G_OS_LINUX)
        #define G_DEFAULT_SOUNDSYS      G_SYS_API_NONE
+#elif defined(G_OS_FREEBSD)
+       #define G_DEFAULT_SOUNDSYS      G_SYS_API_PULSE
 #elif defined(G_OS_WINDOWS)
        #define G_DEFAULT_SOUNDSYS      G_SYS_API_DS
 #elif defined(G_OS_MAC)
@@ -154,9 +157,6 @@ constexpr int   G_DEFAULT_BUFSIZE           = 1024;
 constexpr int   G_DEFAULT_BIT_DEPTH         = 32;     // float
 constexpr float G_DEFAULT_VOL               = 1.0f;
 constexpr float G_DEFAULT_PITCH             = 1.0f;
-constexpr float G_DEFAULT_BOOST             = 1.0f;
-constexpr float G_DEFAULT_OUT_VOL           = 1.0f;
-constexpr float G_DEFAULT_IN_VOL            = 1.0f;
 constexpr float G_DEFAULT_BPM               = 120.0f;
 constexpr int   G_DEFAULT_BEATS             = 4;
 constexpr int   G_DEFAULT_BARS              = 1;
@@ -173,46 +173,48 @@ constexpr float G_DEFAULT_REC_TRIGGER_LEVEL = -10.0f;
 
 
 /* -- responses and return codes -------------------------------------------- */
-#define G_RES_ERR_PROCESSING    -6
-#define G_RES_ERR_WRONG_DATA    -5
-#define G_RES_ERR_NO_DATA       -4
-#define G_RES_ERR_PATH_TOO_LONG -3
-#define G_RES_ERR_IO            -2
-#define G_RES_ERR_MEMORY        -1
-#define G_RES_ERR                0
-#define G_RES_OK                 1
+constexpr int G_RES_ERR_PROCESSING    = -6;
+constexpr int G_RES_ERR_WRONG_DATA    = -5;
+constexpr int G_RES_ERR_NO_DATA       = -4;
+constexpr int G_RES_ERR_PATH_TOO_LONG = -3;
+constexpr int G_RES_ERR_IO            = -2;
+constexpr int G_RES_ERR_MEMORY        = -1;
+constexpr int G_RES_ERR               =  0;
+constexpr int G_RES_OK                =  1;
 
 
 
 /* -- log modes ------------------------------------------------------------- */
-#define LOG_MODE_STDOUT 0x01
-#define LOG_MODE_FILE   0x02
-#define LOG_MODE_MUTE   0x04
+constexpr int LOG_MODE_STDOUT = 0x01;
+constexpr int LOG_MODE_FILE   = 0x02;
+constexpr int LOG_MODE_MUTE   = 0x04;
 
 
 
 /* -- unique IDs of mainWin's subwindows ------------------------------------ */
 /* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */
-#define WID_BEATS         -1
-#define WID_BPM           -2
-#define WID_ABOUT         -3
-#define WID_FILE_BROWSER  -4
-#define WID_CONFIG        -5
-#define WID_FX_LIST       -6
-#define WID_ACTION_EDITOR -7
-#define WID_SAMPLE_EDITOR -8
-#define WID_FX            -9
-#define WID_KEY_GRABBER   -10
-#define WID_SAMPLE_NAME   -11
+constexpr int WID_BEATS         = -1;
+constexpr int WID_BPM           = -2;
+constexpr int WID_ABOUT         = -3;
+constexpr int WID_FILE_BROWSER  = -4;
+constexpr int WID_CONFIG        = -5;
+constexpr int WID_FX_LIST       = -6;
+constexpr int WID_ACTION_EDITOR = -7;
+constexpr int WID_SAMPLE_EDITOR = -8;
+constexpr int WID_FX            = -9;
+constexpr int WID_KEY_GRABBER   = -10;
+constexpr int WID_SAMPLE_NAME   = -11;
+constexpr int WID_FX_CHOOSER    = -12;
+constexpr int WID_MIDI_INPUT    = -13;
+constexpr int WID_MIDI_OUTPUT   = -14;
 
 
 
 /* -- patch signals --------------------------------------------------------- */
-#define PATCH_UNREADABLE    0x01
-#define PATCH_INVALID       0x02
-#define PATCH_READ_OK       0x04
-#define PATCH_WRONG_PLUGINS 0x08  // currently unused
-#define PATCH_WRONG_SAMPLES 0x10  // currently unused
+constexpr int G_PATCH_UNSUPPORTED = -2;
+constexpr int G_PATCH_UNREADABLE  = -1;
+constexpr int G_PATCH_INVALID     =  0;
+constexpr int G_PATCH_OK          =  1;
 
 
 
@@ -262,7 +264,6 @@ constexpr int G_MIDI_CHANS[G_MAX_MIDI_CHANS] = {
 /* JSON patch keys */
 
 constexpr auto PATCH_KEY_HEADER                       = "header";
-constexpr auto PATCH_KEY_VERSION                      = "version";
 constexpr auto PATCH_KEY_VERSION_MAJOR                = "version_major";
 constexpr auto PATCH_KEY_VERSION_MINOR                = "version_minor";
 constexpr auto PATCH_KEY_VERSION_PATCH                = "version_patch";
@@ -277,11 +278,12 @@ constexpr auto PATCH_KEY_METRONOME                    = "metronome";
 constexpr auto PATCH_KEY_LAST_TAKE_ID                 = "last_take_id";
 constexpr auto PATCH_KEY_SAMPLERATE                   = "samplerate";
 constexpr auto PATCH_KEY_COLUMNS                      = "columns";
+constexpr auto PATCH_KEY_PLUGINS                      = "plugins";
 constexpr auto PATCH_KEY_MASTER_OUT_PLUGINS           = "master_out_plugins";
 constexpr auto PATCH_KEY_MASTER_IN_PLUGINS            = "master_in_plugins";
 constexpr auto PATCH_KEY_CHANNELS                     = "channels";
 constexpr auto PATCH_KEY_CHANNEL_TYPE                 = "type";
-constexpr auto PATCH_KEY_CHANNEL_INDEX                = "index";
+constexpr auto PATCH_KEY_CHANNEL_ID                   = "id";
 constexpr auto PATCH_KEY_CHANNEL_SIZE                 = "size";
 constexpr auto PATCH_KEY_CHANNEL_NAME                 = "name";
 constexpr auto PATCH_KEY_CHANNEL_COLUMN               = "column";
@@ -303,13 +305,13 @@ constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L           = "midi_out_l";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING   = "midi_out_l_playing";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE      = "midi_out_l_mute";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO      = "midi_out_l_solo";
-constexpr auto PATCH_KEY_CHANNEL_SAMPLE_PATH          = "sample_path";
+constexpr auto PATCH_KEY_CHANNEL_WAVE_ID              = "wave_id";
 constexpr auto PATCH_KEY_CHANNEL_KEY                  = "key";
 constexpr auto PATCH_KEY_CHANNEL_MODE                 = "mode";
 constexpr auto PATCH_KEY_CHANNEL_BEGIN                = "begin";
 constexpr auto PATCH_KEY_CHANNEL_END                  = "end";
-constexpr auto PATCH_KEY_CHANNEL_BOOST                = "boost";
-constexpr auto PATCH_KEY_CHANNEL_READ_ACTIONS         = "rec_active";  // TODO update string key in 1.0
+constexpr auto PATCH_KEY_CHANNEL_HAS_ACTIONS          = "has_actions";
+constexpr auto PATCH_KEY_CHANNEL_READ_ACTIONS         = "read_actions";
 constexpr auto PATCH_KEY_CHANNEL_PITCH                = "pitch";
 constexpr auto PATCH_KEY_CHANNEL_INPUT_MONITOR        = "input_monitor";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS = "midi_in_read_actions";
@@ -317,17 +319,22 @@ constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_PITCH        = "midi_in_pitch";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT             = "midi_out";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_CHAN        = "midi_out_chan";
 constexpr auto PATCH_KEY_CHANNEL_PLUGINS              = "plugins";
-constexpr auto PATCH_KEY_CHANNEL_ACTIONS              = "actions";
+constexpr auto PATCH_KEY_CHANNEL_PLUGIN_ID            = "plugin_id";
 constexpr auto PATCH_KEY_CHANNEL_ARMED                = "armed";
+constexpr auto PATCH_KEY_WAVES                        = "waves";
+constexpr auto PATCH_KEY_WAVE_ID                      = "id";
+constexpr auto PATCH_KEY_WAVE_PATH                    = "path";
+constexpr auto PATCH_KEY_ACTIONS                      = "actions";
 constexpr auto PATCH_KEY_ACTION_TYPE                  = "type";
 constexpr auto PATCH_KEY_ACTION_FRAME                 = "frame";
 constexpr auto PATCH_KEY_ACTION_F_VALUE               = "f_value";
 constexpr auto PATCH_KEY_ACTION_I_VALUE               = "i_value";
+constexpr auto PATCH_KEY_PLUGIN_ID                    = "id";
 constexpr auto PATCH_KEY_PLUGIN_PATH                  = "path";
 constexpr auto PATCH_KEY_PLUGIN_BYPASS                = "bypass";
 constexpr auto PATCH_KEY_PLUGIN_PARAMS                = "params";
 constexpr auto PATCH_KEY_PLUGIN_MIDI_IN_PARAMS        = "midi_in_params";
-constexpr auto PATCH_KEY_COLUMN_INDEX                 = "index";
+constexpr auto PATCH_KEY_COLUMN_ID                    = "id";
 constexpr auto PATCH_KEY_COLUMN_WIDTH                 = "width";
 constexpr auto PATCH_KEY_COLUMN_CHANNELS              = "channels";
 constexpr auto G_PATCH_KEY_ACTION_ID                  = "id";
index 7655b91d83059e874a5e120e30d11f933740786f..c49e1a028644ea5b12e547b3e880b1d850644d9b 100644 (file)
@@ -1,10 +1,8 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
- * graphics
- *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
 
 #include "graphics.h"
 
+
 const char* giada_logo_xpm[] = {
 "245 86 12 1",
 "      c #191919",
@@ -781,48 +781,6 @@ const char* inputRecOff_xpm[] = {
 "                       ",
 "                       "};
 
-const char* inputToOutputOn_xpm[] = {
-"10 10 8 1",
-"      c #4D4F4C",
-".     c #585A57",
-"+     c #666765",
-"@     c #6F716E",
-"#     c #939592",
-"$     c #999B98",
-"%     c #AEB0AD",
-"&     c #BCBEBB",
-"          ",
-"          ",
-" .#@      ",
-"  #&&#+   ",
-"    @$&%. ",
-"    @$&%. ",
-"  #&&#+   ",
-" .#@      ",
-"          ",
-"          "};
-
-const char* inputToOutputOff_xpm[] = {
-"10 10 8 1",
-"      c #242523",
-".     c #2E302D",
-"+     c #3A3B39",
-"@     c #4F514E",
-"#     c #828481",
-"$     c #8B8D8A",
-"%     c #A7A9A6",
-"&     c #B9BBB7",
-"          ",
-"          ",
-" +$@      ",
-" .#&&#+   ",
-"    @$&%. ",
-"    @$&%. ",
-" .#&&#+   ",
-" +$@      ",
-"          ",
-"          "};
-
 const char* muteOff_xpm[] = {
 "18 18 8 1",
 "      c #242523",
@@ -943,104 +901,133 @@ const char* readActionOn_xpm[] = {
 
 
 const char* metronomeOff_xpm[] = {
-"13 13 8 1",
-"      c #242523",
-".     c #2D2928",
-"+     c #34302F",
-"@     c #443D3C",
-"#     c #4F4445",
-"$     c #685659",
-"%     c #826A68",
-"&     c #A18282",
-"             ",
-"             ",
-"  .       .  ",
-" #%       %# ",
-" .&+     +&. ",
-"  %$     $%  ",
-"  @&     &@  ",
-"   &@   @&   ",
-"   $%   %$   ",
-"   +&. .&+   ",
-"    %# #%    ",
-"    .   .    ",
-"             "};
+"13 23 3 1",
+"      c None",
+".     c #252525",
+"+     c #B18E8E",
+".............",
+".............",
+".............",
+".............",
+".............",
+".............",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+".............",
+".............",
+".............",
+".............",
+".............",
+".............",
+"............."};
 
 
 const char* metronomeOn_xpm[] = {
-"13 13 8 1",
-"      c #4D4F4C",
-".     c #565150",
-"+     c #645C5C",
-"@     c #716465",
-"#     c #837070",
-"$     c #8F7775",
-"%     c #977C7B",
-"&     c #A68787",
-"             ",
-"             ",
-"  .       .  ",
-" @%       %@ ",
-" .&.     .&. ",
-"  $#     #$  ",
-"  +&     &+  ",
-"   &+   +&   ",
-"   #$   $#   ",
-"   .&. .&.   ",
-"    %@ @%    ",
-"    .   .    ",
-"             "};
+"13 23 3 1",
+"      c None",
+".     c #4E4E4E",
+"+     c #B18E8E",
+".............",
+".............",
+".............",
+".............",
+".............",
+".............",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+"....+...+....",
+".............",
+".............",
+".............",
+".............",
+".............",
+".............",
+"............."};
 
 
 const char* recTriggerModeOff_xpm[] = {
-"13 13 8 1",
-"   c #242523",
-".  c #272826",
-"+  c #2C2827",
-"@  c #292B28",
-"#  c #493E3F",
-"$  c #6A595B",
-"%  c #896E6D",
-"&  c #B08E8E",
-"@...........@",
-".           .",
-". #$$$$$$$# .",
-". +%&&&&&%+ .",
-".  +%&&&%+  .",
-".   +%&%+   .",
-".    +&+    .",
-".   +%&%+   .",
-".  +%&&&%+  .",
-". +%&&&&&%+ .",
-". #$$$$$$$# .",
-".           .",
-"@...........@"};
+"13 23 8 1",
+"      c #232523",
+".     c #2A2625",
+"+     c #43393A",
+"@     c #514647",
+"#     c #6F5C59",
+"$     c #8B7170",
+"%     c #AA8889",
+"&     c #B08E8F",
+"             ",
+"             ",
+"             ",
+"             ",
+"             ",
+"     @$@     ",
+"     %&%     ",
+"     $&$     ",
+"     .+.     ",
+"             ",
+"     #%#     ",
+"     %&%     ",
+"     #%#     ",
+"             ",
+"     .+.     ",
+"     $&$     ",
+"     %&%     ",
+"     @$@     ",
+"             ",
+"             ",
+"             ",
+"             ",
+"             "};
 
 
 const char* recTriggerModeOn_xpm[] = {
-"13 13 9 1",
-"   c None",
-".  c #4D4F4C",
-"+  c #544F4E",
-"@  c #675C5C",
-"#  c #6A5F5F",
-"$  c #7D6B6E",
-"%  c #827072",
-"&  c #967B7A",
-"*  c #B18E8F",
-".............",
-".............",
-"..@$$$$$$$@..",
-"..+&*****&+..",
-"...+&***&+...",
-"....+&*&+....",
-".....+*+.....",
-"....+&*&+....",
-"...+&***&+...",
-"..+&*****&+..",
-"..#%%%%%%%#..",
-".............",
-"............."};
+"13 23 8 1",
+"      c #4D4F4C",
+".     c #534E4D",
+"+     c #605B5A",
+"@     c #6D6363",
+"#     c #817072",
+"$     c #967C7B",
+"%     c #AC8A8B",
+"&     c #B08E8F",
+"             ",
+"             ",
+"             ",
+"             ",
+"             ",
+"     @$@     ",
+"     %&%     ",
+"     $&$     ",
+"     .+.     ",
+"             ",
+"     #%#     ",
+"     %&%     ",
+"     #%#     ",
+"             ",
+"     .+.     ",
+"     $&$     ",
+"     %&%     ",
+"     @$@     ",
+"             ",
+"             ",
+"             ",
+"             ",
+"             "};    
 
 
 const char* zoomInOff_xpm[] = {
index 6427a31658ed7968e0c84f18ece909bab329efa4..60edc41dce6f6df5f6eb8f7d77717d8ba4736651 100644 (file)
@@ -1,10 +1,10 @@
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
  * graphics
  *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
  *
  * along with Giada - Your Hardcore Loopmachine. If not, see
  * <http://www.gnu.org/licenses/>.
  *
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
 
 #ifndef G_GRAPHICS_H
 #define G_GRAPHICS_H
 
+
 extern const char* giada_logo_xpm[];
 
 extern const char* loopRepeat_xpm[];
@@ -71,9 +73,6 @@ extern const char* recTriggerModeOn_xpm[];
 extern const char* inputRecOn_xpm[];
 extern const char* inputRecOff_xpm[];
 
-extern const char* inputToOutputOn_xpm[];
-extern const char* inputToOutputOff_xpm[];
-
 extern const char* divideOn_xpm[];
 extern const char* divideOff_xpm[];
 extern const char* multiplyOn_xpm[];
@@ -111,4 +110,5 @@ extern const char* vstLogo_xpm[];
 
 extern const char* giada_icon[];
 
+
 #endif
diff --git a/src/core/idManager.cpp b/src/core/idManager.cpp
new file mode 100644 (file)
index 0000000..58a0fc4
--- /dev/null
@@ -0,0 +1,56 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "idManager.h"
+
+
+namespace giada {
+namespace m 
+{
+IdManager::IdManager() : m_id(0)
+{
+}
+       
+
+/* -------------------------------------------------------------------------- */
+
+
+void IdManager::set(ID id)
+{
+       if (id != 0 && id > m_id)
+               m_id = id;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+ID IdManager::get(ID id)
+{
+       return id != 0 ? id : ++m_id;
+}
+}} // giada::m::
diff --git a/src/core/idManager.h b/src/core/idManager.h
new file mode 100644 (file)
index 0000000..070ad1d
--- /dev/null
@@ -0,0 +1,55 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_ID_MANAGER_H
+#define G_ID_MANAGER_H
+
+
+#include "core/types.h"
+
+
+namespace giada {
+namespace m 
+{
+class IdManager
+{
+public:
+
+       IdManager();
+       
+       void set(ID id);
+       ID   get(ID id=0);
+
+private:
+
+       //static ID m_gen;
+       ID m_id;
+};
+}} // giada::m::
+
+
+#endif
index eb677f71929f4964d69d6526b47e9aea5814a699..320ef9f4ddcca14e1b6252b12cf3920ddd4de511 100644 (file)
 #include <thread>
 #include <atomic>
 #include <ctime>
-#include <atomic>
 #ifdef __APPLE__
        #include <pwd.h>
 #endif
-#if defined(__linux__) && defined(WITH_VST)
+#if (defined(__linux__) || defined(__FreeBSD__)) && defined(WITH_VST)
        #include <X11/Xlib.h> // For XInitThreads
 #endif
 #include <FL/Fl.H>
-#include "../utils/log.h"
-#include "../utils/fs.h"
-#include "../utils/time.h"
-#include "../utils/gui.h"
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/warnings.h"
-#include "../glue/main.h"
-#include "mixer.h"
-#include "wave.h"
-#include "const.h"
-#include "clock.h"
-#include "channel.h"
-#include "mixerHandler.h"
-#include "patch.h"
-#include "conf.h"
-#include "pluginManager.h"
-#include "pluginHost.h"
-#include "recorder.h"
-#include "recManager.h"
-#include "midiMapConf.h"
-#include "kernelMidi.h"
-#include "kernelAudio.h"
+#include "gui/updater.h"
+#include "utils/log.h"
+#include "utils/fs.h"
+#include "utils/time.h"
+#include "utils/gui.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/warnings.h"
+#include "glue/main.h"
+#include "core/channels/channel.h"
+#include "core/channels/channelManager.h"
+#include "core/mixer.h"
+#include "core/wave.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "core/mixerHandler.h"
+#include "core/patch.h"
+#include "core/conf.h"
+#include "core/waveManager.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/midiMapConf.h"
+#include "core/kernelMidi.h"
+#include "core/kernelAudio.h"
 #include "init.h"
 
 
-extern std::atomic<bool> G_quit;
-extern gdMainWindow*     G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -71,39 +73,20 @@ namespace init
 {
 namespace
 {
-std::thread UIThread_;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void UIThreadCallback_()
-{
-       while (G_quit.load() == false) {
-               if (m::kernelAudio::getStatus())
-                       u::gui::refreshUI();
-               u::time::sleep(G_GUI_REFRESH_RATE);
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void initConf_()
 {
        if (!conf::read())
-               gu_log("[init] Can't read configuration file! Using default values\n");
+               u::log::print("[init] Can't read configuration file! Using default values\n");
        
        patch::init();
        midimap::init();
        midimap::setDefault();
        
-       if (!gu_logInit(conf::logMode))
-               gu_log("[init] log init failed! Using default stdout\n");
+       if (!u::log::init(conf::logMode))
+               u::log::print("[init] log init failed! Using default stdout\n");
 
        if (midimap::read(conf::midiMapPath) != MIDIMAP_READ_OK)
-               gu_log("[init] MIDI map read failed!\n");
+               u::log::print("[init] MIDI map read failed!\n");
 }
 
 
@@ -114,21 +97,21 @@ void initAudio_()
 {
        kernelAudio::openDevice();
        clock::init(conf::samplerate, conf::midiTCfps);
-       mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
-       recorder::init(&mixer::mutex);
-       recManager::init(&mixer::mutex);
+       mh::init();
+       recorder::init();
+       recorderHandler::init();
 
 #ifdef WITH_VST
 
        pluginManager::init(conf::samplerate, kernelAudio::getRealBufSize());
-       pluginManager::sortPlugins(static_cast<pluginManager::SortMethod>(conf::pluginSortMethod));
        pluginHost::init(kernelAudio::getRealBufSize());
 
 #endif
 
-       if (!kernelAudio::getStatus())
+       if (!kernelAudio::isReady())
                return;
 
+       mixer::enable();
        kernelAudio::startStream();
 }
 
@@ -149,31 +132,27 @@ void initMIDI_()
 
 void initGUI_(int argc, char** argv)
 {
-       /* This enables the FLTK lock and start the runtime multithreading support. */
-
-       Fl::lock();
-
        /* This is of paramount importance on Linux with VST enabled, otherwise many
        plug-ins go nuts and crash hard. It seems that some plug-ins or our Juce-based
        PluginHost use Xlib concurrently. */
        
-#if defined(__linux__) && defined(WITH_VST)
+#if (defined(__linux__) || defined(__FreeBSD__)) && defined(WITH_VST)
        XInitThreads();
 #endif
 
-       G_MainWin = new gdMainWindow(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv);
+       G_MainWin = new v::gdMainWindow(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv);
        G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW,
                conf::mainWindowH);
 
        u::gui::updateMainWinLabel(patch::name == "" ? G_DEFAULT_PATCH_NAME : patch::name);
-
-       if (!kernelAudio::getStatus())
-               gdAlert("Your soundcard isn't configured correctly.\n"
+       
+       if (!kernelAudio::isReady())
+               v::gdAlert("Your soundcard isn't configured correctly.\n"
                        "Check the configuration and restart Giada.");
 
-       u::gui::updateControls();
-       
-       UIThread_ = std::thread(UIThreadCallback_);
+       u::gui::updateStaticWidgets();
+
+       Fl::add_timeout(G_GUI_REFRESH_RATE, v::updater::update, nullptr);
 }
 
 
@@ -182,20 +161,22 @@ void initGUI_(int argc, char** argv)
 
 void shutdownAudio_()
 {
+       if (kernelAudio::isReady()) {
+               kernelAudio::closeDevice();
+               u::log::print("[init] KernelAudio closed\n");
+               mh::close();
+               u::log::print("[init] Mixer closed\n");
+       }
+
+       /* TODO - why cleaning plug-ins and mixer memory? Just shutdown the audio
+       device and let the OS take care of the rest. */
+
 #ifdef WITH_VST
 
-       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex);
        pluginHost::close();
-       gu_log("[init] PluginHost cleaned up\n");
+       u::log::print("[init] PluginHost cleaned up\n");
 
 #endif
-
-       if (kernelAudio::getStatus()) {
-               kernelAudio::closeDevice();
-               gu_log("[init] KernelAudio closed\n");
-               mixer::close();
-               gu_log("[init] Mixer closed\n");
-       }
 }
 
 
@@ -205,9 +186,8 @@ void shutdownAudio_()
 void shutdownGUI_()
 {
        u::gui::closeAllSubwindows();
-       UIThread_.join();       
 
-       gu_log("[init] All subwindows and UI thread closed\n");
+       u::log::print("[init] All subwindows and UI thread closed\n");
 }
 } // {anonymous}
 
@@ -221,7 +201,7 @@ void startup(int argc, char** argv)
 {
        time_t t;
        time (&t);
-       gu_log("[init] Giada %s - %s", G_VERSION_STR, ctime(&t));
+       u::log::print("[init] Giada %s - %s", G_VERSION_STR, ctime(&t));
 
        initConf_();
        initAudio_();
@@ -235,7 +215,7 @@ void startup(int argc, char** argv)
 
 void closeMainWindow()
 {
-       if (!gdConfirmWin("Warning", "Quit Giada: are you sure?"))
+       if (!v::gdConfirmWin("Warning", "Quit Giada: are you sure?"))
                return;
 
        G_MainWin->hide();
@@ -246,20 +226,46 @@ void closeMainWindow()
 /* -------------------------------------------------------------------------- */
 
 
+void reset()
+{      
+       u::gui::closeAllSubwindows();
+       G_MainWin->clearKeyboard(); 
+
+       mh::close();
+#ifdef WITH_VST
+       pluginHost::close();
+#endif
+
+       channelManager::init();
+       waveManager::init();
+       clock::init(conf::samplerate, conf::midiTCfps);
+       mh::init();
+       recorder::init();
+#ifdef WITH_VST
+       pluginManager::init(conf::samplerate, kernelAudio::getRealBufSize());
+#endif
+
+       
+       u::gui::updateMainWinLabel(G_DEFAULT_PATCH_NAME);
+       u::gui::updateStaticWidgets();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void shutdown()
 {
-       G_quit.store(true);
-
        shutdownGUI_();
 
        if (!conf::write())
-               gu_log("[init] error while saving configuration file!\n");
+               u::log::print("[init] error while saving configuration file!\n");
        else
-               gu_log("[init] configuration saved\n");
+               u::log::print("[init] configuration saved\n");
 
        shutdownAudio_();
 
-       gu_log("[init] Giada %s closed\n\n", G_VERSION_STR);
-       gu_logClose();
+       u::log::print("[init] Giada %s closed\n\n", G_VERSION_STR);
+       u::log::close();
 }
-}}} // giada::m::init
\ No newline at end of file
+}}} // giada::m::init
index b56ace4a53afcf3db863b92f7b339a9bf7a202cf..4972f118e44af89c3d684c80bdcc9649c58e807d 100644 (file)
@@ -34,6 +34,7 @@ namespace m {
 namespace init
 {
 void startup(int argc, char** argv);
+void reset(); 
 void closeMainWindow();
 void shutdown();
 }}} // giada::m::init
index 4d42806b8be2efcaf148b107a005d9bfb562a8e5..6d7be1e18858db91923dde7dbd059a4eac425b85 100644 (file)
 
 
 #include "../deps/rtaudio-mod/RtAudio.h"
-#include "../utils/log.h"
-#include "../glue/main.h"
+#include "utils/log.h"
+#include "glue/main.h"
+#include "core/model/model.h"
 #include "conf.h"
 #include "mixer.h"
 #include "const.h"
 #include "kernelAudio.h"
 
 
-using std::string;
-using std::vector;
-
-
 namespace giada {
 namespace m {
 namespace kernelAudio
@@ -47,13 +44,12 @@ namespace kernelAudio
 namespace
 {
 RtAudio* rtSystem     = nullptr;
-bool     status       = false;
 unsigned numDevs      = 0;
 bool     inputEnabled = false;
-unsigned realBufsize  = 0;             // reale bufsize from the soundcard
+unsigned realBufsize  = 0;     // Real buffer size from the soundcard
 int      api          = 0;
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 
 JackState jackState;
 
@@ -71,9 +67,11 @@ jack_client_t* jackGetHandle()
 /* -------------------------------------------------------------------------- */
 
 
-bool getStatus()
+bool isReady()
 {
-  return status;
+       model::KernelLock lock(model::kernel);
+
+       return model::kernel.get()->audioReady;
 }
 
 
@@ -83,9 +81,9 @@ bool getStatus()
 int openDevice()
 {
        api = conf::soundSystem;
-       gu_log("[KA] using system 0x%x\n", api);
+       u::log::print("[KA] using system 0x%x\n", api);
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 
        if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
                rtSystem = new RtAudio(RtAudio::UNIX_JACK);
@@ -96,6 +94,14 @@ int openDevice()
        if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
                rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
 
+#elif defined(__FreeBSD__)
+
+       if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+               rtSystem = new RtAudio(RtAudio::UNIX_JACK);
+       else
+       if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+               rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
+
 #elif defined(_WIN32)
 
        if (api == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
@@ -115,24 +121,24 @@ int openDevice()
 #endif
 
        else {
-               gu_log("[KA] No API available, nothing to do!\n");
+               u::log::print("[KA] No API available, nothing to do!\n");
                return 0;
        }
 
-       gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n",
+       u::log::print("[KA] Opening devices %d (out), %d (in), f=%d...\n",
     conf::soundDeviceOut, conf::soundDeviceIn, conf::samplerate);
 
        numDevs = rtSystem->getDeviceCount();
 
        if (numDevs < 1) {
-               gu_log("[KA] no devices found with this API\n");
+               u::log::print("[KA] no devices found with this API\n");
                closeDevice();
                return 0;
        }
        else {
-               gu_log("[KA] %d device(s) found\n", numDevs);
+               u::log::print("[KA] %d device(s) found\n", numDevs);
                for (unsigned i=0; i<numDevs; i++)
-                       gu_log("  %d) %s\n", i, getDeviceName(i).c_str());
+                       u::log::print("  %d) %s\n", i, getDeviceName(i).c_str());
        }
 
        RtAudio::StreamParameters outParams;
@@ -153,36 +159,40 @@ int openDevice()
        else
                inputEnabled = false;
 
-  RtAudio::StreamOptions options;
-  options.streamName = G_APP_NAME;
-  options.numberOfBuffers = 4;
+       RtAudio::StreamOptions options;
+       options.streamName = G_APP_NAME;
+       options.numberOfBuffers = 4;
 
        realBufsize = conf::buffersize;
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
 
        if (api == G_SYS_API_JACK) {
                conf::samplerate = getFreq(conf::soundDeviceOut, 0);
-               gu_log("[KA] JACK in use, freq = %d\n", conf::samplerate);
+               u::log::print("[KA] JACK in use, freq = %d\n", conf::samplerate);
        }
 
 #endif
 
        try {
                rtSystem->openStream(
-                       &outParams,                                                   // output params
+                       &outParams,                                       // output params
                        conf::soundDeviceIn != -1 ? &inParams : nullptr,  // input params if inDevice is selected
-                       RTAUDIO_FLOAT32,                                      // audio format
-                       conf::samplerate,                                               // sample rate
-                       &realBufsize,                                         // buffer size in byte
-                       &mixer::masterPlay,                 // audio callback
-                       nullptr,                                                                                  // user data (unused)
+                       RTAUDIO_FLOAT32,                                  // audio format
+                       conf::samplerate,                                 // sample rate
+                       &realBufsize,                                     // buffer size in byte
+                       &mixer::masterPlay,                               // audio callback
+                       nullptr,                                          // user data (unused)
                        &options);
-    status = true;
+               
+               std::unique_ptr<model::Kernel> k = model::kernel.clone();
+               k->audioReady = true;
+               model::kernel.swap(std::move(k));
+               
                return 1;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] rtSystem init error: %s\n", e.getMessage().c_str());
+               u::log::print("[KA] rtSystem init error: %s\n", e.getMessage().c_str());
                closeDevice();
                return 0;
        }
@@ -196,11 +206,11 @@ int startStream()
 {
        try {
                rtSystem->startStream();
-               gu_log("[KA] latency = %lu\n", rtSystem->getStreamLatency());
+               u::log::print("[KA] latency = %lu\n", rtSystem->getStreamLatency());
                return 1;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] Start stream error: %s\n", e.getMessage().c_str());
+               u::log::print("[KA] Start stream error: %s\n", e.getMessage().c_str());
                return 0;
        }
 }
@@ -216,7 +226,7 @@ int stopStream()
                return 1;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] Stop stream error\n");
+               u::log::print("[KA] Stop stream error\n");
                return 0;
        }
 }
@@ -225,13 +235,13 @@ int stopStream()
 /* -------------------------------------------------------------------------- */
 
 
-string getDeviceName(unsigned dev)
+std::string getDeviceName(unsigned dev)
 {
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).name;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] invalid device ID = %d\n", dev);
+               u::log::print("[KA] invalid device ID = %d\n", dev);
                return "";
        }
 }
@@ -243,11 +253,7 @@ string getDeviceName(unsigned dev)
 int closeDevice()
 {
        if (rtSystem->isStreamOpen()) {
-#if defined(__linux__) || defined(__APPLE__)
-               rtSystem->abortStream(); // stopStream seems to lock the thread
-#elif defined(_WIN32)
-               rtSystem->stopStream();  // on Windows it's the opposite
-#endif
+               rtSystem->stopStream();
                rtSystem->closeStream();
                delete rtSystem;
                rtSystem = nullptr;
@@ -267,7 +273,7 @@ unsigned getMaxInChans(int dev)
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).inputChannels;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] Unable to get input channels\n");
+               u::log::print("[KA] Unable to get input channels\n");
                return 0;
        }
 }
@@ -282,7 +288,7 @@ unsigned getMaxOutChans(unsigned dev)
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).outputChannels;
        }
        catch (RtAudioError &e) {
-               gu_log("[KA] Unable to get output channels\n");
+               u::log::print("[KA] Unable to get output channels\n");
                return 0;
        }
 }
@@ -375,28 +381,9 @@ int        getFreq(unsigned dev, int i)
 /* -------------------------------------------------------------------------- */
 
 
-unsigned getRealBufSize()
-{
-  return realBufsize;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool isInputEnabled()
-{
-  return inputEnabled;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-unsigned countDevices()
-{
-  return numDevs;
-}
+unsigned getRealBufSize() { return realBufsize; }
+bool isInputEnabled() { return inputEnabled; }
+unsigned countDevices() { return numDevs; }
 
 
 /* -------------------------------------------------------------------------- */
@@ -430,7 +417,7 @@ int getDeviceByName(const char* name)
 
 bool hasAPI(int API)
 {
-       vector<RtAudio::Api> APIs;
+       std::vector<RtAudio::Api> APIs;
        RtAudio::getCompiledApi(APIs);
        for (unsigned i=0; i<APIs.size(); i++)
                if (APIs.at(i) == API)
@@ -445,19 +432,19 @@ int getAPI() { return api; }
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 
 
 const JackState &jackTransportQuery()
 {
        if (api != G_SYS_API_JACK)
-    return jackState;
-  jack_position_t position;
+               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;
+       jackState.running = ts != JackTransportStopped;
+       jackState.bpm     = position.beats_per_minute;
+       jackState.frame   = position.frame;
+       return jackState;
 }
 
 
@@ -477,11 +464,11 @@ void jackStart()
 void jackSetPosition(uint32_t frame)
 {
        if (api != G_SYS_API_JACK)
-    return;
-  jack_position_t position;
-  jack_transport_query(jackGetHandle(), &position);
-  position.frame = frame;
-  jack_transport_reposition(jackGetHandle(), &position);
+       return;
+       jack_position_t position;
+       jack_transport_query(jackGetHandle(), &position);
+       position.frame = frame;
+       jack_transport_reposition(jackGetHandle(), &position);
 }
 
 
@@ -490,16 +477,16 @@ void jackSetPosition(uint32_t frame)
 
 void jackSetBpm(double bpm)
 {
-  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);
+       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);
 }
 
 
@@ -512,6 +499,6 @@ void jackStop()
                jack_transport_stop(jackGetHandle());
 }
 
-#endif  // #ifdef __linux__
+#endif  // defined(__linux__) || defined(__FreeBSD__)
 
 }}}; // giada::m::kernelAudio
index 7177923cf7da4b97c27408c5226f811a899b6070..8ccb95d5286972af61ad3de6919e6b7139a26b4e 100644 (file)
 
 
 #include <string>
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
        #include <jack/jack.h>
        #include <jack/intclient.h>
        #include <jack/transport.h>
 #endif
 
 
-class RtAudio;
-class Mixer;
-
-
 namespace giada {
 namespace m {
 namespace kernelAudio
 {
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 
 struct JackState
 {
@@ -61,7 +57,7 @@ int closeDevice();
 int startStream();
 int stopStream();
 
-bool getStatus();
+bool isReady();
 bool isProbed(unsigned dev);
 bool isDefaultIn(unsigned dev);
 bool isDefaultOut(unsigned dev);
@@ -80,7 +76,7 @@ int getDefaultIn();
 bool hasAPI(int API);
 int getAPI();
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 
 void jackStart();
 void jackStop();
index cdb47316c67986a1d84806d7cc3e5a76c037cc78..a0b9242023fde09a35748c0f066f5a1dea2dd1f5 100644 (file)
 
 #include "const.h"
 #ifdef G_OS_MAC
-       #include <RtMidi.h>
+#include <RtMidi.h>
 #else
-       #include <rtmidi/RtMidi.h>
+#include <rtmidi/RtMidi.h>
 #endif
-#include "../utils/log.h"
+#include "utils/log.h"
 #include "midiDispatcher.h"
 #include "midiMapConf.h"
 #include "kernelMidi.h"
 
 
-using std::string;
-using std::vector;
-
-
 namespace giada {
 namespace m {
 namespace kernelMidi
@@ -55,13 +51,13 @@ unsigned numOutPorts_ = 0;
 unsigned numInPorts_  = 0;
 
 
-static void callback_(double t, vector<unsigned char>* msg, void* data)
+static void callback_(double t, std::vector<unsigned char>* msg, void* data)
 {
        if (msg->size() < 3) {
-               //gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
+               //u::log::print("[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");
+               //      u::log::print("%X", (int) msg->at(i));
+               //u::log::print("\n");
                return;
        }
        midiDispatcher::dispatch(msg->at(0), msg->at(1), msg->at(2));
@@ -73,11 +69,10 @@ static void callback_(double t, vector<unsigned char>* msg, void* data)
 
 void sendMidiLightningInitMsgs_()
 {
-       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 | G_MIDI_CHANS[msg.channel]);
+       for (const midimap::Message& m : midimap::initCommands) {
+               if (m.value != 0x0 && m.channel != -1) {
+                       u::log::print("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", m.channel, m.value);
+                       send(m.value | G_MIDI_CHANS[m.channel]);
                }
        }
 }
@@ -93,7 +88,7 @@ void sendMidiLightningInitMsgs_()
 void setApi(int api)
 {
        api_ = api;
-       gu_log("[KM] using system 0x%x\n", api_);
+       u::log::print("[KM] using system 0x%x\n", api_);
 }
 
 
@@ -107,7 +102,7 @@ int openOutDevice(int port)
                status_  = true;
        }
        catch (RtMidiError &error) {
-               gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
+               u::log::print("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
                status_ = false;
                return 0;
        }
@@ -115,16 +110,16 @@ int openOutDevice(int port)
        /* print output ports */
 
        numOutPorts_ = midiOut_->getPortCount();
-       gu_log("[KM] %d output MIDI ports found\n", numOutPorts_);
+       u::log::print("[KM] %d output MIDI ports found\n", numOutPorts_);
        for (unsigned i=0; i<numOutPorts_; i++)
-               gu_log("  %d) %s\n", i, getOutPortName(i).c_str());
+               u::log::print("  %d) %s\n", i, getOutPortName(i).c_str());
 
        /* try to open a port, if enabled */
 
        if (port != -1 && numOutPorts_ > 0) {
                try {
                        midiOut_->openPort(port, getOutPortName(port));
-                       gu_log("[KM] MIDI out port %d open\n", port);
+                       u::log::print("[KM] MIDI out port %d open\n", port);
 
                        /* TODO - it shold send midiLightning message only if there is a map loaded
                        and available in midimap:: */
@@ -133,7 +128,7 @@ int openOutDevice(int port)
                        return 1;
                }
                catch (RtMidiError& error) {
-                       gu_log("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
+                       u::log::print("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
                        status_ = false;
                        return 0;
                }
@@ -153,7 +148,7 @@ int openInDevice(int port)
                status_ = true;
        }
        catch (RtMidiError &error) {
-               gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
+               u::log::print("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
                status_ = false;
                return 0;
        }
@@ -161,9 +156,9 @@ int openInDevice(int port)
        /* print input ports */
 
        numInPorts_ = midiIn_->getPortCount();
-       gu_log("[KM] %d input MIDI ports found\n", numInPorts_);
+       u::log::print("[KM] %d input MIDI ports found\n", numInPorts_);
        for (unsigned i=0; i<numInPorts_; i++)
-               gu_log("  %d) %s\n", i, getInPortName(i).c_str());
+               u::log::print("  %d) %s\n", i, getInPortName(i).c_str());
 
        /* try to open a port, if enabled */
 
@@ -171,12 +166,12 @@ int openInDevice(int port)
                try {
                        midiIn_->openPort(port, getInPortName(port));
                        midiIn_->ignoreTypes(true, false, true); // ignore all system/time msgs, for now
-                       gu_log("[KM] MIDI in port %d open\n", port);
+                       u::log::print("[KM] MIDI in port %d open\n", port);
                        midiIn_->setCallback(&callback_);
                        return 1;
                }
                catch (RtMidiError& error) {
-                       gu_log("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
+                       u::log::print("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
                        status_ = false;
                        return 0;
                }
@@ -191,7 +186,7 @@ int openInDevice(int port)
 
 bool hasAPI(int API)
 {
-       vector<RtMidi::Api> APIs;
+       std::vector<RtMidi::Api> APIs;
        RtMidi::getCompiledApi(APIs);
        for (unsigned i=0; i<APIs.size(); i++)
                if (APIs.at(i) == API)
@@ -203,13 +198,13 @@ bool hasAPI(int API)
 /* -------------------------------------------------------------------------- */
 
 
-string getOutPortName(unsigned p)
+std::string getOutPortName(unsigned p)
 {
        try { return midiOut_->getPortName(p); }
        catch (RtMidiError &error) { return ""; }
 }
 
-string getInPortName(unsigned p)
+std::string getInPortName(unsigned p)
 {
        try { return midiIn_->getPortName(p); }
        catch (RtMidiError &error) { return ""; }
@@ -224,12 +219,12 @@ void send(uint32_t data)
        if (!status_)
                return;
 
-       vector<unsigned char> msg(1, getB1(data));
+       std::vector<unsigned char> msg(1, getB1(data));
        msg.push_back(getB2(data));
        msg.push_back(getB3(data));
 
        midiOut_->sendMessage(&msg);
-       gu_log("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
+       u::log::print("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
 }
 
 
@@ -241,7 +236,7 @@ void send(int b1, int b2, int b3)
        if (!status_)
                return;
 
-       vector<unsigned char> msg(1, b1);
+       std::vector<unsigned char> msg(1, b1);
 
        if (b2 != -1)
                msg.push_back(b2);
@@ -249,33 +244,35 @@ void send(int b1, int b2, int b3)
                msg.push_back(b3);
 
        midiOut_->sendMessage(&msg);
-       //gu_log("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
+       //u::log::print("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void sendMidiLightning(uint32_t learn, const midimap::message_t& msg)
+void sendMidiLightning(uint32_t learn, const midimap::Message& m)
 {
        // Skip lightning message if not defined in midi map
 
-       if (!midimap::isDefined(msg))
+       if (!midimap::isDefined(m))
        {
-               gu_log("[KM] message skipped (not defined in midimap)");
+               u::log::print("[KM] message skipped (not defined in midimap)");
                return;
        }
 
-       gu_log("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, msg.channel, 
-               msg.value, msg.offset);
+       u::log::print("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, m.channel, 
+               m.value, m.offset);
 
        /* Isolate 'channel' from learnt message and offset it as requested by 'nn' in 
        the midimap configuration file. */
-       uint32_t out = ((learn & 0x00FF0000) >> 16) << msg.offset;
+
+       uint32_t out = ((learn & 0x00FF0000) >> 16) << m.offset;
 
        /* Merge the previously prepared channel into final message, and finally send 
        it. */
-       out |= msg.value | (msg.channel << 24);
+
+       out |= m.value | (m.channel << 24);
        send(out);
 }
 
index 55a5b7650bc7cc6a48c203aeaa04bf5a853eb515..189f314eea97d247f986e30e45bc2901b886cd77 100644 (file)
@@ -59,10 +59,10 @@ void send(int b1, int b2=-1, int b3=-1);
 /* sendMidiLightning
 Sends a MIDI lightning message defined by 'msg'. */
 
-void sendMidiLightning(uint32_t learn, const midimap::message_t& msg);
+void sendMidiLightning(uint32_t learn, const midimap::Message& msg);
 
 /* setApi
- * set the Api in use for both in & out messages. */
+Sets the Api in use for both in & out messages. */
 
 void setApi(int api);
 
@@ -79,7 +79,7 @@ int closeInDevice();
 int closeOutDevice();
 
 /* getIn/OutPortName
- * return the name of the port 'p'. */
+Returns the name of the port 'p'. */
 
 std::string getInPortName(unsigned p);
 std::string getOutPortName(unsigned p);
diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp
deleted file mode 100644 (file)
index 20ead79..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 "../utils/log.h"
-#include "recorder.h"
-#include "midiChannelProc.h"
-#include "channelManager.h"
-#include "channel.h"
-#include "recorderHandler.h"
-#include "action.h"
-#include "patch.h"
-#include "const.h"
-#include "conf.h"
-#include "mixer.h"
-#include "pluginHost.h"
-#include "kernelMidi.h"
-#include "midiChannel.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace m 
-{
-MidiChannel::MidiChannel(int bufferSize)
-       : Channel      (ChannelType::MIDI, ChannelStatus::OFF, bufferSize),
-               midiOut    (false),
-               midiOutChan(G_MIDI_CHANS[0])
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
-{
-       Channel::copy(src_, pluginMutex);
-       const MidiChannel* src = static_cast<const MidiChannel*>(src_);
-       midiOut     = src->midiOut;
-       midiOutChan = src->midiOutChan;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::parseEvents(mixer::FrameEvents fe)
-{
-       midiChannelProc::parseEvents(this, fe);
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::process(AudioBuffer& out, const AudioBuffer& in, bool audible, 
-       bool running)
-{
-       midiChannelProc::process(this, out, in, audible);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::stopBySeq(bool chansStopOnSeqHalt)
-{
-       midiChannelProc::stopBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::start(int frame, bool doQuantize, int velocity)
-{
-       midiChannelProc::start(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::kill(int localFrame)
-{
-       midiChannelProc::kill(this, localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::rewindBySeq()
-{
-       midiChannelProc::rewindBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::setMute(bool value)
-{
-       midiChannelProc::setMute(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::setSolo(bool value)
-{
-       midiChannelProc::setSolo(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::readPatch(const string& basePath, const patch::channel_t& pch)
-{
-       Channel::readPatch("", pch);
-       channelManager::readPatch(this, pch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::writePatch(int i, bool isProject)
-{
-       Channel::writePatch(i, isProject);
-       channelManager::writePatch(this, isProject, i);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame)
-{
-       juce::MidiMessage message = juce::MidiMessage(
-               kernelMidi::getB1(msg),
-               kernelMidi::getB2(msg),
-               kernelMidi::getB3(msg));
-       midiBuffer.addEvent(message, localFrame);
-}
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::empty()
-{
-       hasActions = false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::sendMidi(const Action* a, int localFrame)
-{
-       if (isPlaying() && !mute) {
-               if (midiOut) {
-                       MidiEvent event = a->event;
-                       event.setChannel(midiOutChan);
-                       kernelMidi::send(event.getRaw());
-               }
-#ifdef WITH_VST
-               addVstMidiEvent(a->event.getRaw(), localFrame);
-#endif
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
-{
-       namespace mrh = m::recorderHandler;
-       namespace mr  = m::recorder;
-
-       if (!armed)
-               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. */
-
-       MidiEvent midiEventFlat(midiEvent);
-       midiEventFlat.setChannel(0);
-
-#ifdef WITH_VST
-
-       pthread_mutex_lock(&pluginHost::mutex);
-       addVstMidiEvent(midiEventFlat.getRaw(), 0);
-       pthread_mutex_unlock(&pluginHost::mutex);
-
-#endif
-
-       if (mr::isActive()) {
-               mrh::liveRec(index, midiEventFlat);
-               hasActions = true;
-       }
-}
-
-}} // giada::m::
diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h
deleted file mode 100644 (file)
index 16de069..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_MIDI_CHANNEL_H
-#define G_MIDI_CHANNEL_H
-
-
-#ifdef WITH_VST
-       #include "../deps/juce-config.h"
-#endif
-#include "channel.h"
-
-
-namespace giada {
-namespace m 
-{
-class MidiChannel : public Channel
-{
-public:
-
-       MidiChannel(int bufferSize);
-
-       void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
-       void parseEvents(mixer::FrameEvents fe) override;
-       void process(AudioBuffer& out, const AudioBuffer& in, bool audible, bool running) override;
-       void start(int frame, bool doQuantize, int velocity) override;
-       void kill(int localFrame) override;
-       void empty() override;
-       void stopBySeq(bool chansStopOnSeqHalt) override;
-       void stop() override {};
-       void rewindBySeq() override;
-       void setMute(bool value) override;
-       void setSolo(bool value) override;
-       void readPatch(const std::string& basePath, const patch::channel_t& pch) override;
-       void writePatch(int i, bool isProject) override;
-       void receiveMidi(const MidiEvent& midiEvent) override;
-
-       /* sendMidi
-       Sends Midi event to the outside world. */
-
-       void sendMidi(const Action* a, int localFrame);
-
-#ifdef WITH_VST
-
-       /* addVstMidiEvent
-       Adds a new Midi event to the midiEvent stack fom a composite uint32_t raw
-       Midi event. LocalFrame is the offset: it tells where to put the event
-       inside the buffer. */
-
-       void addVstMidiEvent(uint32_t msg, int localFrame);
-
-#endif
-
-       bool midiOut;      // enable midi output
-       int  midiOutChan;  // midi output channel
-};
-
-}} // giada::m::
-
-
-#endif
diff --git a/src/core/midiChannelProc.cpp b/src/core/midiChannelProc.cpp
deleted file mode 100644 (file)
index d85ad30..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#include "midiChannel.h"
-#include "pluginHost.h"
-#include "kernelMidi.h"
-#include "const.h"
-#include "action.h"
-#include "midiChannelProc.h"
-#include "mixerHandler.h"
-
-namespace giada {
-namespace m {
-namespace midiChannelProc
-{
-namespace
-{
-void onFirstBeat_(MidiChannel* ch)
-{
-       if (ch->status == ChannelStatus::ENDING) {
-               ch->status = ChannelStatus::OFF;
-               ch->sendMidiLstatus();
-       }
-       else
-       if (ch->status == ChannelStatus::WAIT) {
-               ch->status = ChannelStatus::PLAY;
-               ch->sendMidiLstatus();
-       }
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(MidiChannel* ch, mixer::FrameEvents fe)
-{
-       if (fe.onFirstBeat)
-               onFirstBeat_(ch);
-       for (const Action* action : fe.actions)
-               if (action->channel == ch->index)
-                       ch->sendMidi(action, fe.frameLocal);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible)
-{
-#ifdef WITH_VST
-       pluginHost::processStack(ch->buffer, pluginHost::StackType::CHANNEL, ch);
-#endif
-
-       /* Process the plugin stack first, then quit if the channel is muted/soloed. 
-       This way there's no risk of cutting midi event pairs such as note-on and 
-       note-off while triggering a mute/solo. */
-
-       /* TODO - this is meaningful only if WITH_VST is defined */
-       if (audible)
-               for (int i=0; i<out.countFrames(); i++)
-                       for (int j=0; j<out.countChannels(); j++)
-                               out[i][j] += ch->buffer[i][j] * ch->volume;     
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void start(MidiChannel* ch)
-{
-       switch (ch->status) {
-               case ChannelStatus::PLAY:
-                       ch->status = ChannelStatus::ENDING;
-                       ch->sendMidiLstatus();
-                       break;
-
-               case ChannelStatus::ENDING:
-               case ChannelStatus::WAIT:
-                       ch->status = ChannelStatus::OFF;
-                       ch->sendMidiLstatus();
-                       break;
-
-               case ChannelStatus::OFF:
-                       ch->status = ChannelStatus::WAIT;
-                       ch->sendMidiLstatus();
-                       break;
-
-               default: break;
-       }       
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void kill(MidiChannel* ch, int localFrame)
-{
-       if (ch->isPlaying()) {
-               if (ch->midiOut)
-                       kernelMidi::send(MIDI_ALL_NOTES_OFF);
-#ifdef WITH_VST
-               ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-#endif
-       }
-       ch->status = ChannelStatus::OFF;
-       ch->sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindBySeq(MidiChannel* ch)
-{
-       if (ch->midiOut)
-               kernelMidi::send(MIDI_ALL_NOTES_OFF);
-#ifdef WITH_VST
-               ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-#endif 
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setMute(MidiChannel* ch, bool v)
-{
-       ch->mute = v;
-       if (ch->mute) {
-               if (ch->midiOut)
-                       kernelMidi::send(MIDI_ALL_NOTES_OFF);
-       #ifdef WITH_VST
-                       ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-       #endif          
-               }
-
-       // This is for processing playing_inaudible
-       ch->sendMidiLstatus();  
-
-       ch->sendMidiLmute();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setSolo(MidiChannel* ch, bool v)
-{
-       ch->solo = v;
-       m::mh::updateSoloCount();
-
-       // This is for processing playing_inaudible
-       for (Channel* channel : mixer::channels)                
-               channel->sendMidiLstatus();
-
-       ch->sendMidiLsolo();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopBySeq(MidiChannel* ch)
-{
-       kill(ch, 0);
-}
-}}};
diff --git a/src/core/midiChannelProc.h b/src/core/midiChannelProc.h
deleted file mode 100644 (file)
index 6abd143..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef G_MIDI_CHANNEL_PROC_H
-#define G_MIDI_CHANNEL_PROC_H
-
-
-#include "mixer.h"
-#include "audioBuffer.h"
-
-
-namespace giada {
-namespace m 
-{
-class MidiChannel;
-
-namespace midiChannelProc
-{
-/* parseEvents
-Parses events gathered by Mixer::masterPlay(). */
-
-void parseEvents(MidiChannel* ch, mixer::FrameEvents ev);
-
-/**/
-void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible);
-
-/* kill
-Stops a channel abruptly. */
-
-void kill(MidiChannel* ch, int localFrame);
-
-/* start
-Starts a channel. */
-
-void start(MidiChannel* ch);
-
-/* stopBySeq
-Stops a channel when the stop button on main transport is pressed. */
-
-void stopBySeq(MidiChannel* ch);
-
-/* rewind
-Rewinds channel when rewind button on main transport is pressed. */
-
-void rewindBySeq(MidiChannel* ch);
-
-/* mute|unmute
-Mutes/unmutes a channel. */
-
-void setMute(MidiChannel* ch, bool v);
-void setSolo(MidiChannel* ch, bool v);
-}}};
-
-
-#endif
index 64d40a7117d5a3ae5244f7d78f91985a5e9a527e..78a9afe92bf2b00d05f48a6ae0f7fc40680b9311 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <vector>
-#include "../glue/plugin.h"
-#include "../glue/io.h"
-#include "../glue/channel.h"
-#include "../glue/transport.h"
-#include "../glue/main.h"
-#include "../utils/log.h"
-#include "channel.h"
-#include "sampleChannel.h"
-#include "midiChannel.h"
-#include "conf.h"
-#include "mixer.h"
-#include "pluginHost.h"
-#include "plugin.h"
-#include "midiDispatcher.h"
-
-
-using std::vector;
+#include "glue/plugin.h"
+#include "glue/io.h"
+#include "glue/channel.h"
+#include "glue/main.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/conf.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/pluginHost.h"
+#include "core/plugin.h"
+#include "core/recManager.h"
+#include "core/types.h"
+#include "core/midiDispatcher.h"
 
 
 namespace giada {
@@ -55,10 +57,8 @@ namespace
 Callback prepared by the gdMidiGrabber window and called by midiDispatcher. It 
 contains things to do once the midi message has been stored. */
 
-cb_midiLearn* cb_learn_ = nullptr;
-void* cb_data_ = nullptr;      
-
-std::function<void()> signalCb_ = nullptr;
+std::function<void()>          signalCb_ = nullptr;
+std::function<void(MidiEvent)> learnCb_  = nullptr;
 
 
 /* -------------------------------------------------------------------------- */
@@ -66,26 +66,26 @@ std::function<void()> signalCb_ = nullptr;
 
 #ifdef WITH_VST
 
-void processPlugins_(Channel* ch, const MidiEvent& midiEvent)
+void processPlugins_(const std::vector<ID>& ids, const MidiEvent& midiEvent)
 {
        uint32_t pure = midiEvent.getRawNoVelocity();
+       float    vf   = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, 1.0f);
 
        /* Plugins' parameters layout reflects the structure of the matrix
-       Channel::midiInPlugins. It is safe to assume then that i (i.e. Plugin*) and k 
-       indexes match both the structure of Channel::midiInPlugins and 
-       vector<Plugin*>* plugins. */
+       Channel::midiInPlugins. It is safe to assume then that Plugin 'p' and 
+       k indexes match both the structure of Channel::midiInPlugins and the vector
+       of plugins. */
 
-       std::vector<Plugin*> plugins = pluginHost::getStack(pluginHost::StackType::CHANNEL, ch);
+       m::model::PluginsLock l(m::model::plugins);
 
-       for (Plugin* plugin : plugins) {
-               for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
-                       uint32_t midiInParam = plugin->midiInParams.at(k);
-                       if (pure != midiInParam)
+       for (ID id : ids) {
+               m::Plugin& p = m::model::get(m::model::plugins, id);
+               for (unsigned k = 0; k < p.midiInParams.size(); k++) {
+                       if (pure != p.midiInParams.at(k))
                                continue;
-                       float vf = midiEvent.getVelocity() / 127.0f;
-                       c::plugin::setParameter(plugin, k, vf, false); // false: not from GUI
-                       gu_log("  >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
-                               plugin->getId(), k, ch->index, pure, midiEvent.getVelocity(), vf);
+                       c::plugin::setParameter(id, k, vf, /*gui=*/false);
+                       u::log::print("  >>> [plugin %d parameter %d] (pure=0x%X, value=%d, float=%f)\n",
+                               p.id, k, pure, midiEvent.getVelocity(), vf);
                }
        }
 }
@@ -100,7 +100,14 @@ void processChannels_(const MidiEvent& midiEvent)
 {
        uint32_t pure = midiEvent.getRawNoVelocity();
 
-       for (Channel* ch : mixer::channels) {
+       /* TODO - this is definitely not the best approach but it's necessary as
+       you can't call actions on m::model::channels while locking on a upper
+       level. Let's wait for a better async mechanism... */
+
+       std::vector<std::function<void()>> actions;
+
+       model::channels.lock();
+       for (Channel* ch : model::channels) {
 
                /* Do nothing on this channel if MIDI in is disabled or filtered out for
                the current MIDI channel. */
@@ -108,61 +115,83 @@ void processChannels_(const MidiEvent& midiEvent)
                if (!ch->midiIn || !ch->isMidiInAllowed(midiEvent.getChannel()))
                        continue;
 
-               if      (pure == ch->midiInKeyPress) {
-                       gu_log("  >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::io::keyPress(ch, false, false, midiEvent.getVelocity());
+               if      (pure == ch->midiInKeyPress.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> keyPress, ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::io::keyPress(ch->id, false, false, midiEvent.getVelocity());
+                       });
                }
-               else if (pure == ch->midiInKeyRel) {
-                       gu_log("  >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::io::keyRelease(ch, false, false);
+               else if (pure == ch->midiInKeyRel.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> keyRel ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::io::keyRelease(ch->id, false, false);
+                       });
                }
-               else if (pure == ch->midiInMute) {
-                       gu_log("  >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::channel::toggleMute(ch, false);
+               else if (pure == ch->midiInMute.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> mute ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::channel::toggleMute(ch->id);
+                       });
                }               
-               else if (pure == ch->midiInKill) {
-                       gu_log("  >>> kill ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::channel::kill(ch);
+               else if (pure == ch->midiInKill.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> kill ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::channel::kill(ch->id, /*record=*/false);
+                       });
                }               
-               else if (pure == ch->midiInArm) {
-                       gu_log("  >>> arm ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::channel::toggleArm(ch, false);
+               else if (pure == ch->midiInArm.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> arm ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::channel::toggleArm(ch->id);
+                       });
                }
-               else if (pure == ch->midiInSolo) {
-                       gu_log("  >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
-                       c::channel::toggleSolo(ch, false);
+               else if (pure == ch->midiInSolo.load()) {
+                       actions.push_back([=] {
+                               u::log::print("  >>> solo ch=%d (pure=0x%X)\n", ch->id, pure);
+                               c::channel::toggleSolo(ch->id);
+                       });
                }
-               else if (pure == ch->midiInVolume) {
-                       float vf = midiEvent.getVelocity() / 127.0f; // TODO: u::math::map
-                       gu_log("  >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
-                               ch->index, pure, midiEvent.getVelocity(), vf);
-                       c::channel::setVolume(ch, vf, false);
+               else if (pure == ch->midiInVolume.load()) {
+                       actions.push_back([=] {
+                               float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME); 
+                               u::log::print("  >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+                                       ch->id, pure, midiEvent.getVelocity(), vf);
+                               c::channel::setVolume(ch->id, vf, /*gui=*/false);
+                       });
                }
                else {
-                       SampleChannel* sch = static_cast<SampleChannel*>(ch);
-                       if (pure == sch->midiInPitch) {
-                               float vf = midiEvent.getVelocity() / (127/4.0f); // [0-127] ~> [0.0-4.0] TODO: u::math::map
-                               gu_log("  >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
-                                       sch->index, pure, midiEvent.getVelocity(), vf);
-                               c::channel::setPitch(sch, vf);
+                       const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
+                       if (pure == sch->midiInPitch.load()) {
+                               actions.push_back([=] {
+                                       float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_PITCH); 
+                                       u::log::print("  >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
+                                               sch->id, pure, midiEvent.getVelocity(), vf);
+                                       c::channel::setPitch(sch->id, vf);
+                               });
                        }
                        else 
-                       if (pure == sch->midiInReadActions) {
-                               gu_log("  >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure);
-                               c::channel::toggleReadingActions(sch, false);
+                       if (pure == sch->midiInReadActions.load()) {
+                               actions.push_back([=] {
+                                       u::log::print("  >>> toggle read actions ch=%d (pure=0x%X)\n", sch->id, pure);
+                                       c::channel::toggleReadingActions(sch->id);
+                               });
                        }
                }
-
 #ifdef WITH_VST
 
                /* Process learned plugins parameters. */
-               processPlugins_(ch, midiEvent); 
+               processPlugins_(ch->pluginIds, midiEvent); 
 
 #endif
 
                /* Redirect full midi message (pure + velocity) to plugins. */
                ch->receiveMidi(midiEvent.getRaw());
        }
+       model::channels.unlock();
+
+       /* Apply all the collected actions. */
+       for (auto& action : actions)
+               action();
 }
 
 
@@ -171,46 +200,48 @@ void processChannels_(const MidiEvent& midiEvent)
 
 void processMaster_(const MidiEvent& midiEvent)
 {
+       const bool gui = false;
+
        uint32_t pure = midiEvent.getRawNoVelocity();
 
        if      (pure == conf::midiInRewind) {
-               gu_log("  >>> rewind (master) (pure=0x%X)\n", pure);
-               c::transport::rewindSeq(false);
+               u::log::print("  >>> rewind (master) (pure=0x%X)\n", pure);
+               mh::rewindSequencer();
        }
        else if (pure == conf::midiInStartStop) {
-               gu_log("  >>> startStop (master) (pure=0x%X)\n", pure);
-               c::transport::startStopSeq(false);
+               u::log::print("  >>> startStop (master) (pure=0x%X)\n", pure);
+               mh::toggleSequencer();
        }
        else if (pure == conf::midiInActionRec) {
-               gu_log("  >>> actionRec (master) (pure=0x%X)\n", pure);
-               c::io::toggleActionRec(false);
+               u::log::print("  >>> actionRec (master) (pure=0x%X)\n", pure);
+               recManager::toggleActionRec(static_cast<RecTriggerMode>(conf::recTriggerMode));
        }
        else if (pure == conf::midiInInputRec) {
-               gu_log("  >>> inputRec (master) (pure=0x%X)\n", pure);
-               c::io::toggleInputRec(false);
+               u::log::print("  >>> inputRec (master) (pure=0x%X)\n", pure);
+               c::main::toggleInputRec();
        }
        else if (pure == conf::midiInMetronome) {
-               gu_log("  >>> metronome (master) (pure=0x%X)\n", pure);
-               c::transport::toggleMetronome(false);
+               u::log::print("  >>> metronome (master) (pure=0x%X)\n", pure);
+               m::mixer::toggleMetronome();
        }
        else if (pure == conf::midiInVolumeIn) {
-               float vf = midiEvent.getVelocity() / 127.0f;
-               gu_log("  >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
+               float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME); 
+               u::log::print("  >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
                        pure, midiEvent.getVelocity(), vf);
-               c::main::setInVol(vf, false);
+               c::main::setInVol(vf, gui);
        }
        else if (pure == conf::midiInVolumeOut) {
-               float vf = midiEvent.getVelocity() / 127.0f;
-               gu_log("  >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
+               float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME); 
+               u::log::print("  >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
                        pure, midiEvent.getVelocity(), vf);
-               c::main::setOutVol(vf, false);
+               c::main::setOutVol(vf, gui);
        }
        else if (pure == conf::midiInBeatDouble) {
-               gu_log("  >>> sequencer x2 (master) (pure=0x%X)\n", pure);
+               u::log::print("  >>> sequencer x2 (master) (pure=0x%X)\n", pure);
                c::main::beatsMultiply();
        }
        else if (pure == conf::midiInBeatHalf) {
-               gu_log("  >>> sequencer /2 (master) (pure=0x%X)\n", pure);
+               u::log::print("  >>> sequencer /2 (master) (pure=0x%X)\n", pure);
                c::main::beatsDivide();
        }
 }
@@ -234,10 +265,9 @@ void triggerSignalCb_()
 /* -------------------------------------------------------------------------- */
 
 
-void startMidiLearn(cb_midiLearn* cb, void* data)
+void startMidiLearn(std::function<void(MidiEvent)> f)
 {
-       cb_learn_ = cb;
-       cb_data_  = data;
+       learnCb_ = f;
 }
 
 
@@ -246,8 +276,7 @@ void startMidiLearn(cb_midiLearn* cb, void* data)
 
 void stopMidiLearn()
 {
-       cb_learn_ = nullptr;
-       cb_data_  = nullptr;
+       learnCb_ = nullptr;
 }
 
 
@@ -264,7 +293,7 @@ void dispatch(int byte1, int byte2, int byte3)
        MidiEvent midiEvent(byte1, byte2, byte3);
        midiEvent.fixVelocityZero();
 
-       gu_log("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(), 
+       u::log::print("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(), 
                midiEvent.getChannel());
 
        /* Start dispatcher. If midi learn is on don't parse channels, just learn 
@@ -273,8 +302,9 @@ void dispatch(int byte1, int byte2, int byte3)
        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_(midiEvent.getRawNoVelocity(), cb_data_);
+       if (learnCb_ != nullptr) {
+               learnCb_(midiEvent);
+       }
        else {
                processMaster_(midiEvent);
                processChannels_(midiEvent);
index e8df2f8704cd1eac362c305484896ae6910757f1..1e44777ca75c55abbaa91cce4ff98e244a6d2b18 100644 (file)
 
 #include <functional>
 #include <cstdint>
+#include "core/midiEvent.h"
 
 
 namespace giada {
 namespace m {
 namespace midiDispatcher
 {
-typedef void (cb_midiLearn) (uint32_t, void*);
+/*typedef void (cb_midiLearn) (uint32_t, void*);
 
-void startMidiLearn(cb_midiLearn* cb, void* data);
+void startMidiLearn(cb_midiLearn* cb, void* data);*/
+void startMidiLearn(std::function<void(MidiEvent)> f);
 void stopMidiLearn();
 
 void dispatch(int byte1, int byte2, int byte3);
index db25f766d2b39cded4968be8e2d3a325e2fd8750..826e66e74f8b5cdfea0f76a10095cb7fb6467ac5 100644 (file)
@@ -68,9 +68,9 @@ MidiEvent::MidiEvent(int byte1, int byte2, int byte3)
 /* -------------------------------------------------------------------------- */
 
 
-void MidiEvent::resetDelta()
+void MidiEvent::setDelta(int d)
 {
-       m_delta = 0;
+       m_delta = d;
 }
 
 
@@ -155,4 +155,4 @@ uint32_t MidiEvent::getRawNoVelocity() const
 }
 
 
-}} // giada::m::
\ No newline at end of file
+}} // giada::m::
index 111fceb6fab69708b0fbcfc2bdece6974cf3230f..a481572c2177cf19c57bf5f200d499409636f3cf 100644 (file)
@@ -62,7 +62,7 @@ public:
        uint32_t getRaw() const;
        uint32_t getRawNoVelocity() const;
 
-       void resetDelta();
+       void setDelta(int d);
        void setChannel(int c);
        void setVelocity(int v);
 
@@ -83,8 +83,7 @@ private:
        int m_velocity;
        int m_delta;
 };
-
 }} // giada::m::
 
 
-#endif
\ No newline at end of file
+#endif
index c8a962e77413499d52d71f8a5843c12c85ec45a5..b62094f4533acca1290eac0b40efa3acbd098596 100644 (file)
 #include <string>
 #include <cstring>
 #include <dirent.h>
-#include "../utils/string.h"
-#include "../utils/log.h"
-#include "../utils/fs.h"
+#include "utils/string.h"
+#include "utils/log.h"
+#include "utils/fs.h"
+#include "utils/json.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)
+bool readInitCommands_(json_t* j)
 {
-       json_t *jInitCommands = json_object_get(jContainer, MIDIMAP_KEY_INIT_COMMANDS);
-  if (!storager::checkArray(jInitCommands, MIDIMAP_KEY_INIT_COMMANDS))
-    return 0;
+       namespace uj = u::json;
+
+       json_t* jcs = json_object_get(j, MIDIMAP_KEY_INIT_COMMANDS);
+       if (jcs == nullptr)
+       return false;
 
-       size_t commandIndex;
-       json_t *jInitCommand;
-       json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
+       size_t  i;
+       json_t* jc;
+       json_array_foreach(jcs, i, jc) {
 
-               string indexStr = "init command " + u::string::iToString(commandIndex);
-               if (!storager::checkObject(jInitCommand, indexStr.c_str()))
-                       return 0;
+               if (!uj::isObject(jc))
+                       return false;
 
-               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);
+               Message m;
+               m.channel  = uj::readInt   (jc, MIDIMAP_KEY_CHANNEL);
+               m.valueStr = uj::readString(jc, MIDIMAP_KEY_MESSAGE);
+               m.value    = strtoul(m.valueStr.c_str(), nullptr, 16);
 
-    initCommands.push_back(message);
+               initCommands.push_back(m);
        }
 
-       return 1;
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool readCommand(json_t *jContainer, message_t *msg, const string &key)
+bool readCommand_(json_t* j, Message& m, const std::string& key)
 {
-       json_t *jCommand = json_object_get(jContainer, key.c_str());
-  if (!storager::checkObject(jCommand, key.c_str()))
-    return 0;
+       namespace uj = u::json;
 
-  if (!storager::setInt(jCommand, MIDIMAP_KEY_CHANNEL, msg->channel)) return 0;
-  if (!storager::setString(jCommand, MIDIMAP_KEY_MESSAGE, msg->valueStr)) return 0;
+       json_t* jc = json_object_get(j, key.c_str());
+       if (jc == nullptr)
+               return false;
 
-       return 1;
+       m.channel  = uj::readInt   (jc, MIDIMAP_KEY_CHANNEL);
+       m.valueStr = uj::readString(jc, MIDIMAP_KEY_MESSAGE);
+
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void parse(message_t *message)
+void parse_(Message& message)
 {
        /* Remove '0x' part from the original string. */
 
-       string input = message->valueStr;
+       std::string input = message.valueStr;
 
-       size_t f = input.find("0x");                                    // check if "0x" is there
-       if (f!=std::string::npos)
-               input = message->valueStr.replace(f, 2, "");
+       size_t f = input.find("0x"); // check if "0x" is there
+       if (f != std::string::npos)
+               input = message.valueStr.replace(f, 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. */
+       each char (i.e. nibble) in the original string. Substitute 'n' with
+       zeros. */
 
-       string output;
+       std::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;
+                       if (message.offset == -1) // do it once
+                               message.offset = p;
                }
                else
                        output += input[i];
        }
 
-       /* from string to uint32_t */
+       /* From string to uint32_t */
 
-       message->value = strtoul(output.c_str(), nullptr, 16);
+       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);
+       u::log::print("[parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
+                       message.channel, message.valueStr.c_str(), message.value, message.offset);
 }
 
 }; // {anonymous}
@@ -134,21 +133,21 @@ void parse(message_t *message)
 
 
 
-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;
-message_t playing_inaudible;
+std::string brand;
+std::string device;
+std::vector<Message> initCommands;
+Message muteOn;
+Message muteOff;
+Message soloOn;
+Message soloOff;
+Message waiting;
+Message playing;
+Message stopping;
+Message stopped;
+Message playingInaudible;
 
-string midimapsPath;
-vector<string> maps;
+std::string midimapsPath;
+std::vector<std::string> maps;
 
 
 /* -------------------------------------------------------------------------- */
@@ -156,18 +155,19 @@ vector<string> maps;
 
 void init()
 {
-       midimapsPath = gu_getHomePath() + G_SLASH + "midimaps" + G_SLASH;
+       midimapsPath = u::fs::getHomePath() + G_SLASH + "midimaps" + G_SLASH;
 
        /* scan dir of midi maps and load the filenames into <>maps. */
 
-       gu_log("[init] scanning midimaps directory...\n");
+       u::log::print("[midiMapConf::init] scanning midimaps directory '%s'...\n",
+               midimapsPath.c_str());
 
-  DIR    *dp;
-  dirent *ep;
-  dp = opendir(midimapsPath.c_str());
+       DIR*    dp;
+       dirent* ep;
+       dp = opendir(midimapsPath.c_str());
 
-       if (!dp) {
-               gu_log("[init] unable to scan midimaps directory!\n");
+       if (dp == nullptr) {
+               u::log::print("[midiMapConf::init] unable to scan midimaps directory!\n");
                return;
        }
 
@@ -177,12 +177,12 @@ void init()
 
                // TODO - check if is a valid midimap file (verify headers)
 
-               gu_log("[init] found midimap '%s'\n", ep->d_name);
+               u::log::print("[midiMapConf::init] found midimap '%s'\n", ep->d_name);
 
                maps.push_back(ep->d_name);
        }
 
-       gu_log("[init] total midimaps found: %d\n", maps.size());
+       u::log::print("[midiMapConf::init] total midimaps found: %d\n", maps.size());
        closedir(dp);
 }
 
@@ -226,56 +226,54 @@ void setDefault()
        stopped.valueStr  = "";
        stopped.offset    = -1;
        stopped.value     = 0;
-       playing_inaudible.channel   = 0;
-       playing_inaudible.valueStr  = "";
-       playing_inaudible.offset    = -1;
-       playing_inaudible.value     = 0;
+       playingInaudible.channel   = 0;
+       playingInaudible.valueStr  = "";
+       playingInaudible.offset    = -1;
+       playingInaudible.value     = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool isDefined(message_t msg)
+bool isDefined(const Message& m)
 {
-       return (msg.offset!=-1);
+       return m.offset != -1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int read(const string &file)
+int read(const std::string& file)
 {
+       namespace uj = u::json;
+
        if (file.empty()) {
-               gu_log("[read] midimap not specified, nothing to do\n");
+               u::log::print("[midiMapConf::read] midimap not specified, nothing to do\n");
                return MIDIMAP_NOT_SPECIFIED;
        }
 
-       gu_log("[read] reading midimap file '%s'\n", file.c_str());
-
-  json_error_t jError;
-       string path = midimapsPath + file;
-  json_t *jRoot = json_load_file(path.c_str(), 0, &jError);
-       if (!jRoot) {
-    gu_log("[read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
-    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))  parse(&muteOn);
-       if (readCommand(jRoot, &muteOff,  MIDIMAP_KEY_MUTE_OFF)) parse(&muteOff);
-       if (readCommand(jRoot, &soloOn,   MIDIMAP_KEY_SOLO_ON))  parse(&soloOn);
-       if (readCommand(jRoot, &soloOff,  MIDIMAP_KEY_SOLO_OFF)) parse(&soloOff);
-       if (readCommand(jRoot, &waiting,  MIDIMAP_KEY_WAITING))  parse(&waiting);
-       if (readCommand(jRoot, &playing,  MIDIMAP_KEY_PLAYING))  parse(&playing);
-       if (readCommand(jRoot, &stopping, MIDIMAP_KEY_STOPPING)) parse(&stopping);
-       if (readCommand(jRoot, &stopped,  MIDIMAP_KEY_STOPPED))  parse(&stopped);
-       if (readCommand(jRoot, &playing_inaudible,  MIDIMAP_KEY_PLAYING_INAUDIBLE))  parse(&playing_inaudible);
+       u::log::print("[midiMapConf::read] reading midimap file '%s'\n", file.c_str());
+
+       json_t* j = uj::load(std::string(midimapsPath + file).c_str());
+       if (j == nullptr)
+               return MIDIMAP_UNREADABLE;
+
+       brand  = uj::readString(j, MIDIMAP_KEY_BRAND);
+       device = uj::readString(j, MIDIMAP_KEY_DEVICE);
+       
+       if (!readInitCommands_(j)) return MIDIMAP_UNREADABLE;
+       if (readCommand_(j, muteOn,           MIDIMAP_KEY_MUTE_ON))  parse_(muteOn);
+       if (readCommand_(j, muteOff,          MIDIMAP_KEY_MUTE_OFF)) parse_(muteOff);
+       if (readCommand_(j, soloOn,           MIDIMAP_KEY_SOLO_ON))  parse_(soloOn);
+       if (readCommand_(j, soloOff,          MIDIMAP_KEY_SOLO_OFF)) parse_(soloOff);
+       if (readCommand_(j, waiting,          MIDIMAP_KEY_WAITING))  parse_(waiting);
+       if (readCommand_(j, playing,          MIDIMAP_KEY_PLAYING))  parse_(playing);
+       if (readCommand_(j, stopping,         MIDIMAP_KEY_STOPPING)) parse_(stopping);
+       if (readCommand_(j, stopped,          MIDIMAP_KEY_STOPPED))  parse_(stopped);
+       if (readCommand_(j, playingInaudible, MIDIMAP_KEY_PLAYING_INAUDIBLE))  parse_(playingInaudible);
 
        return MIDIMAP_READ_OK;
 }
-
 }}}; // giada::m::midimap::
index 3a5f9da732a70cbc3ad7824d17967c469a702e54..da24aa317f7b6b90a30dec899941d711e0d37ca9 100644 (file)
@@ -37,7 +37,7 @@ namespace giada {
 namespace m {
 namespace midimap
 {
-struct message_t
+struct Message
 {
     int         channel;
     std::string valueStr;
@@ -47,48 +47,47 @@ struct message_t
 
 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;
-extern message_t playing_inaudible;
+extern std::vector<Message> initCommands;
+extern Message muteOn;
+extern Message muteOff;
+extern Message soloOn;
+extern Message soloOff;
+extern Message waiting;
+extern Message playing;
+extern Message stopping;
+extern Message stopped;
+extern Message playingInaudible;
 
 /* midimapsPath
- * path of midimap files, different between OSes. */
+Path of midimap files, different between OSes. */
 
 extern std::string midimapsPath;
 
 /* maps
- * Maps are the available .giadamap files. Each element of the std::vector
- * represents a .giadamap filename. */
+Maps are the available .giadamap files. Each element of the std::vector 
+represents a .giadamap filename. */
 
 extern std::vector<std::string> maps;
 
 /* init
-Parse the midi maps folders and find the available maps. */
+Parses the midi maps folders and find the available maps. */
 
 void init();
 
 /* setDefault
-Set default values in case no maps are available/chosen. */
+Sets default values in case no maps are available/chosen. */
 
 void setDefault();
 
 /* isDefined
-Check whether a specific message has been defined within midi map file.*/
+Checks whether a specific message has been defined within midi map file. */
 
-bool isDefined(message_t msg);
+bool isDefined(const Message& msg);
 
 /* read
-Read a midi map from file 'file'. */
-
-int read(const std::string &file);
+Reads a midi map from file 'file'. */
 
+int read(const std::string& file);
 }}}; // giada::m::midimap::
 
 #endif
index f6f799637b2ae87be50ef6e3ed0feaea98b7336a..c34a2abdcb4fa3025c4472ef960beb89d972d4ad 100644 (file)
 
 #include <cassert>
 #include <cstring>
-#include "../deps/rtaudio-mod/RtAudio.h"
-#include "../utils/log.h"
-#include "../utils/math.h"
-#include "wave.h"
-#include "kernelAudio.h"
-#include "recorder.h"
-#include "pluginHost.h"
-#include "conf.h"
-#include "mixerHandler.h"
-#include "clock.h"
-#include "const.h"
-#include "channel.h"
-#include "sampleChannel.h"
-#include "midiChannel.h"
-#include "audioBuffer.h"
-#include "action.h"
-#include "mixer.h"
+#include "deps/rtaudio-mod/RtAudio.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/wave.h"
+#include "core/kernelAudio.h"
+#include "core/recorder.h"
+#include "core/recManager.h"
+#include "core/pluginHost.h"
+#include "core/conf.h"
+#include "core/mixerHandler.h"
+#include "core/clock.h"
+#include "core/const.h"
+#include "core/audioBuffer.h"
+#include "core/action.h"
+#include "core/mixer.h"
 
 
 namespace giada {
@@ -93,23 +95,29 @@ struct Metronome
        }
 } metronome_;
 
-/* vChanInput
+/* vChanInput_
 Virtual channel for input recording. */
 
 AudioBuffer vChanInput_;
 
-/* vChanInToOut
+/* vChanInToOut_
 Virtual channel in->out bridge (hear what you're playin). */
 
 AudioBuffer vChanInToOut_;
 
-/* inputTracker
+/* inputTracker_
 Frame position while recording. */
 
 Frame inputTracker_ = 0;
 
+/* signalCb_
+Callback triggered when the input signal level reaches a threshold. */
+
 std::function<void()> signalCb_ = nullptr;
 
+std::atomic<bool> processing_(false);
+std::atomic<bool> active_(false);
+
 
 /* -------------------------------------------------------------------------- */
 
@@ -118,8 +126,8 @@ void computePeak_(const AudioBuffer& buf, std::atomic<float>& peak)
 {
        for (int i=0; i<buf.countFrames(); i++)
                for (int j=0; j<buf.countChannels(); j++)
-                       if (buf[i][j] > peak.load())
-                               peak.store(buf[i][j]);
+                       if (buf[i][j] > peak)
+                               peak = buf[i][j];
 }
 
 
@@ -130,14 +138,14 @@ Records from line in. */
 
 void lineInRec_(const AudioBuffer& inBuf)
 {
-       if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording)
+       if (!recManager::isRecordingInput() || !kernelAudio::isInputEnabled())
                return;
 
        for (int i=0; i<inBuf.countFrames(); i++, inputTracker_++)
                for (int j=0; j<inBuf.countChannels(); j++) {
                        if (inputTracker_ >= clock::getFramesInLoop())
                                inputTracker_ = 0;
-                       vChanInput_[inputTracker_][j] += inBuf[i][j] * inVol.load();  // adding: overdub!
+                       vChanInput_[inputTracker_][j] += inBuf[i][j] * mh::getInVol();  // adding: overdub!
                }
 }
 
@@ -162,31 +170,19 @@ void processLineIn_(const AudioBuffer& inBuf)
        /* "hear what you're playing" - process, copy and paste the input buffer onto 
        the output buffer. */
 
-       if (inToOut)
+       model::MixerLock lock(model::mixer);
+       
+       if (model::mixer.get()->inToOut)
                for (int i=0; i<vChanInToOut_.countFrames(); i++)
                        for (int j=0; j<vChanInToOut_.countChannels(); j++)
-                               vChanInToOut_[i][j] = inBuf[i][j] * inVol.load();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* prepareBuffers
-Cleans up every buffer, both in Mixer and in channels. */
-
-void prepareBuffers_(AudioBuffer& outBuf)
-{
-       outBuf.clear();
-       vChanInToOut_.clear();
-       for (Channel* channel : channels)
-               channel->prepareBuffer(clock::isRunning());
+                               vChanInToOut_[i][j] = inBuf[i][j] * mh::getInVol();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 /* doQuantize
-Computes quantization on 'rewind' button and all channels. */
+Computes quantization on 'rewind' button. */
 
 void doQuantize_(unsigned frame)
 {
@@ -197,26 +193,110 @@ void doQuantize_(unsigned frame)
 
        if (rewindWait) {
                rewindWait = false;
-               rewind();
+               clock::rewind();
+               mh::rewindChannels();
        }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* 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 renderIO_(AudioBuffer& outBuf, const AudioBuffer& inBuf)
+void renderMetronome_(AudioBuffer& outBuf, Frame f)
 {
-       for (Channel* channel : channels)
-               channel->process(outBuf, inBuf, isChannelAudible(channel), clock::isRunning());
+       if (!metronome_.running)
+               return;
 
-#ifdef WITH_VST
-       pluginHost::processStack(outBuf, pluginHost::StackType::MASTER_OUT);
-       pluginHost::processStack(vChanInToOut_, pluginHost::StackType::MASTER_IN);
-#endif
+       if (clock::isOnBar() || metronome_.playBar)
+               metronome_.render(outBuf, metronome_.playBar, metronome_.bar, f);
+       else
+       if (clock::isOnBeat() || metronome_.playBeat)
+               metronome_.render(outBuf, metronome_.playBeat, metronome_.beat, f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents_(Frame f)
+{
+       mixer::FrameEvents fe = {
+               .frameLocal   = f,
+               .frameGlobal  = clock::getCurrentFrame(),
+               .doQuantize   = clock::getQuantize() == 0 || !clock::quantoHasPassed(),
+               .onBar        = clock::isOnBar(),
+               .onFirstBeat  = clock::isOnFirstBeat(),
+               .quantoPassed = clock::quantoHasPassed(),
+               .actions      = recorder::getActionsOnFrame(clock::getCurrentFrame()),
+       };
+
+       model::ChannelsLock lock(model::channels);
+
+       /* TODO - channel->parseEvents alters things in Channel (i.e. it's mutable).
+       Refactoring needed ASAP. */
+
+       for (Channel* ch : model::channels)
+               ch->parseEvents(fe); 
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void render_(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut)
+{
+       bool running = clock::isRunning();
+
+       model::ChannelsLock lock(model::channels);
+
+       /* TODO - channel->render alters things in Channel (i.e. it's mutable).
+       Refactoring needed ASAP. */
+
+       for (const Channel* ch : model::channels) {
+               if (ch == nullptr ||
+                       ch->id == mixer::MASTER_OUT_CHANNEL_ID ||
+                       ch->id == mixer::MASTER_IN_CHANNEL_ID)
+                       continue;
+               const_cast<Channel*>(ch)->render(out, in, inToOut, isChannelAudible(ch), running);
+       }
+
+       assert(model::channels.size() >= 3); // Preview channel included
+
+       /* Master channels are processed at the end, when the buffers have already 
+       been filled. */
+       
+       model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).render(out, in, inToOut, true, true);
+       model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).render(out, in, inToOut, true, true);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processSequencer_(AudioBuffer& out, const AudioBuffer& in)
+{
+       for (int j=0; j<out.countFrames(); j++) {
+               if (clock::isRunning()) {
+                       parseEvents_(j);
+                       doQuantize_(j);
+               }
+               clock::sendMIDIsync();
+               clock::incrCurrentFrame();
+               renderMetronome_(out, j);
+       }
+       lineInRec_(in);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* prepareBuffers
+Cleans up every buffer. */
+
+void prepareBuffers_(AudioBuffer& outBuf)
+{
+       outBuf.clear();
+       vChanInToOut_.clear();
 }
 
 
@@ -244,48 +324,17 @@ output volume. */
 
 void finalizeOutput_(AudioBuffer& outBuf)
 {
+       model::MixerLock lock(model::mixer);
+       
+       //printf("%f\n", mh::getOutVol());
+
        for (int i=0; i<outBuf.countFrames(); i++)
                for (int j=0; j<outBuf.countChannels(); j++) {
-                       if (inToOut) // Merge vChanInToOut_, if enabled
+                       if (model::mixer.get()->inToOut) // Merge vChanInToOut_, if enabled
                                outBuf[i][j] += vChanInToOut_[i][j];
-                       outBuf[i][j] *= outVol.load(); 
+                       outBuf[i][j] *= mh::getOutVol(); 
                }
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void renderMetronome_(AudioBuffer& outBuf, Frame f)
-{
-       if (!metronome_.running)
-               return;
-
-       if (clock::isOnBar() || metronome_.playBar)
-               metronome_.render(outBuf, metronome_.playBar, metronome_.bar, f);
-       else
-       if (clock::isOnBeat() || metronome_.playBeat)
-               metronome_.render(outBuf, metronome_.playBeat, metronome_.beat, f);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents_(Frame f)
-{
-       FrameEvents fe;
-       fe.frameLocal   = f;
-       fe.frameGlobal  = clock::getCurrentFrame();
-       fe.doQuantize   = clock::getQuantize() == 0 || !clock::quantoHasPassed();
-       fe.onBar        = clock::isOnBar();
-       fe.onFirstBeat  = clock::isOnFirstBeat();
-       fe.quantoPassed = clock::quantoHasPassed();
-       fe.actions      = recorder::getActionsOnFrame(clock::getCurrentFrame());
-
-       for (Channel* channel : channels)
-               channel->parseEvents(fe);       
-}
 }; // {anonymous}
 
 
@@ -294,42 +343,45 @@ void parseEvents_(Frame f)
 /* -------------------------------------------------------------------------- */
 
 
-std::vector<Channel*> channels;
-
-bool  recording  = false;
-bool  ready      = true;
-bool  metronome  = false;
-int   waitRec    = 0;
-bool  rewindWait = false;
-bool  hasSolos   = false;
-bool  inToOut    = false;
-std::atomic<float> outVol(G_DEFAULT_OUT_VOL);
-std::atomic<float> inVol(G_DEFAULT_IN_VOL);
+std::atomic<bool>  rewindWait(false);
 std::atomic<float> peakOut(0.0);
 std::atomic<float> peakIn(0.0);
 
-pthread_mutex_t mutex;
-
 
 /* -------------------------------------------------------------------------- */
 
 
 void init(Frame framesInSeq, Frame framesInBuffer)
 {
-       /* Allocate virtual input channels. vChanInput_ has variable size: it depends
+       /* Allocate virtual inputs. vChanInput_ has variable size: it depends
        on how many frames there are in sequencer. */
        
        vChanInput_.alloc(framesInSeq, G_MAX_IO_CHANS);
        vChanInToOut_.alloc(framesInBuffer, G_MAX_IO_CHANS);
 
-       gu_log("[Mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n", 
+       u::log::print("[mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n", 
                framesInSeq, framesInBuffer);   
 
-       hasSolos = false;
+       clock::rewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
 
-       pthread_mutex_init(&mutex, nullptr);
 
-       rewind();
+void enable()
+{ 
+       active_.store(true); 
+       u::log::print("[mixer::enable] enabled\n");
+}
+
+
+void disable() 
+{ 
+       active_.store(false);
+       while (processing_.load() == true) 
+               std::this_thread::sleep_for(std::chrono::milliseconds(50));
+       u::log::print("[mixer::disable] disabled\n");
 }
 
 
@@ -342,17 +394,34 @@ void allocVirtualInput(Frame frames)
 }
 
 
+void clearVirtualInput()
+{
+       vChanInput_.clear();
+}
+
+
+const AudioBuffer& getVirtualInput()
+{
+       return vChanInput_;
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 
 int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, 
        double streamTime, RtAudioStreamStatus status, void* userData)
 {
-       if (!ready)
+       if (!kernelAudio::isReady() || active_.load() == false)
                return 0;
 
-#ifdef __linux__
-       clock::recvJackSync();
+       processing_.store(true);
+
+#if defined(__linux__) || defined(__FreeBSD__)
+
+       if (kernelAudio::getAPI() == G_SYS_API_JACK)
+               clock::recvJackSync();
+
 #endif
 
        AudioBuffer out, in;
@@ -360,32 +429,23 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        if (kernelAudio::isInputEnabled())
                in.setData((float*) inBuf, bufferSize, G_MAX_IO_CHANS);
 
-       peakOut.store(0.0);  // reset peak calculator
-       peakIn.store(0.0);   // reset peak calculator
+       /* Reset peak computation. */
+
+       peakOut = 0.0;
+       peakIn  = 0.0;
 
        prepareBuffers_(out);
        processLineIn_(in);
 
-       pthread_mutex_lock(&mutex);
-
-       if (clock::isActive()) {
-               for (unsigned j=0; j<bufferSize; j++) {
-                       if (clock::isRunning()) {
-                               parseEvents_(j);
-                               doQuantize_(j);
-                       }
-                       clock::sendMIDIsync();
-                       clock::incrCurrentFrame();
-                       renderMetronome_(out, j);
-               }
-               lineInRec_(in);
-       }
+       /* Process model. */
 
-       renderIO_(out, in);
+//out[0][0] = 3.0f;
 
-       pthread_mutex_unlock(&mutex);
+       if (clock::isActive()) 
+               processSequencer_(out, in);
+       render_(out, in, vChanInToOut_);
 
-       /* Post processing */
+       /* Post processing. */
 
        finalizeOutput_(out);
        limitOutput_(out);
@@ -396,6 +456,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        out.setData(nullptr, 0, 0);
        in.setData (nullptr, 0, 0);
 
+       processing_.store(false);
+
        return 0;
 }
 
@@ -406,29 +468,17 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
 void close()
 {
        clock::setStatus(ClockStatus::STOPPED);
-       while (channels.size() > 0)
-               mh::deleteChannel(channels.at(0));
-       pthread_mutex_destroy(&mutex);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool isSilent()
+bool isChannelAudible(const Channel* ch)
 {
-       for (const Channel* ch : channels)
-               if (ch->isPlaying())
-                       return false;
-       return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
+       model::MixerLock l(model::mixer);
 
-bool isChannelAudible(Channel* ch)
-{
+       bool hasSolos = model::mixer.get()->hasSolos;
        return !hasSolos || (hasSolos && ch->solo);
 }
 
@@ -438,25 +488,19 @@ bool isMetronomeOn() { return metronome_.running; }
 /* -------------------------------------------------------------------------- */
 
 
-void rewind()
+void startInputRec()
 {
-       clock::rewind();
-       if (clock::isRunning())
-               for (Channel* ch : channels)
-                       ch->rewindBySeq();
+       /* Start inputTracker_ from the current frame, not the beginning. */
+       inputTracker_ = clock::getCurrentFrame();
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void startInputRec()
+void stopInputRec()
 {
-       /* Start inputTracker_ from the current frame, not the beginning. */
-       recording     = true;
-       inputTracker_ = clock::getCurrentFrame();
+       inputTracker_ = 0;
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -479,21 +523,4 @@ void setSignalCallback(std::function<void()> f)
 {
        signalCb_ = f;
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void mergeVirtualInput()
-{
-       for (Channel* ch : channels) {
-               /* TODO - move this to audioProc::*/
-               if (ch->type == ChannelType::MIDI)
-                       continue;
-               SampleChannel* sch = static_cast<SampleChannel*>(ch);
-               if (sch->armed)
-                       sch->wave->copyData(vChanInput_[0], vChanInput_.countFrames());
-       }
-       vChanInput_.clear();
-}
 }}}; // giada::m::mixer::
index 10b18dc28fe06a257f0225b520f6c07628a674e2..12a704d52a1949305be59ab5ee48b8a7053b7bed 100644 (file)
 
 
 #include <atomic>
-#include <pthread.h>
 #include <functional>
 #include <vector>
-#include "recorder.h"
-#include "types.h"
-#include "../deps/rtaudio-mod/RtAudio.h"
+#include "deps/rtaudio-mod/RtAudio.h"
+#include "core/recorder.h"
+#include "core/types.h"
 
 
 namespace giada {
@@ -43,6 +42,7 @@ namespace m
 {
 struct Action;
 class Channel;
+class AudioBuffer;
 
 namespace mixer
 {
@@ -54,29 +54,26 @@ struct FrameEvents
        bool  onBar;
        bool  onFirstBeat;
        bool  quantoPassed;
-       std::vector<const Action*> actions;
+       const std::vector<Action>* actions;
 };
 
-extern std::vector<Channel*> channels;
+constexpr int MASTER_OUT_CHANNEL_ID = 1;
+constexpr int MASTER_IN_CHANNEL_ID  = 2;
+constexpr int PREVIEW_CHANNEL_ID    = 3;
 
-extern bool   recording;     // is recording something?
-extern bool   ready;
-extern bool   rewindWait;    // rewind guard, if quantized
-extern bool   hasSolos;      // more than 0 channels soloed
-extern std::atomic<float> outVol;
-extern std::atomic<float> inVol;
+extern std::atomic<bool>  rewindWait;    // rewind guard, if quantized
 extern std::atomic<float> peakOut;
 extern std::atomic<float> peakIn;
 
-/* inToOut
-Copy, process and paste the input into the output, in order to obtain a "hear 
-what you're playing" feature. */
-
-extern bool inToOut;
+void init(Frame framesInSeq, Frame framesInBuffer);
 
-extern pthread_mutex_t mutex;
+/* enable, disable
+Toggles master callback processing. Useful when loading a new patch. Mixer
+will flush itself to wait for a processing cycle to finish when disable() is
+called. */
 
-void init(Frame framesInSeq, Frame framesInBuffer);
+void enable();
+void disable();
 
 /* allocVirtualInput
 Allocates new memory for the virtual input channel. Call this whenever you 
@@ -84,6 +81,17 @@ shrink or resize the sequencer. */
 
 void allocVirtualInput(Frame frames);
 
+/* clearVirtualInput
+Clears internal virtual channel. */
+
+void clearVirtualInput();
+
+/* getVirtualInput
+Returns a read-only reference to the internal virtual channel. Use this to
+merge data into channel after an input recording session. */
+
+const AudioBuffer& getVirtualInput(); 
+
 void close();
 
 /* masterPlay
@@ -92,28 +100,13 @@ Core method (callback) */
 int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime,
        RtAudioStreamStatus status, void* userData);
 
-/* isSilent
-Is mixer silent? */
+bool isChannelAudible(const Channel* ch);
 
-bool isSilent();
-
-bool isChannelAudible(Channel* ch);
-
-/* rewind
-Rewinds sequencer to frame 0. */
-
-void rewind();
-
-/* startInputRec
-Starts input recording on frame clock::getCurrentFrame(). */
+/* startInputRec, stopInputRec
+Starts/stops input recording on frame clock::getCurrentFrame(). */
 
 void startInputRec();
-
-/* mergeVirtualInput
-Copies the virtual channel input in the channels designed for input recording. 
-Called by mixerHandler on stopInputRec(). */
-
-void mergeVirtualInput();
+void stopInputRec();
 
 void toggleMetronome();
 bool isMetronomeOn();
index 4864a24936bc7f353f610146802b884de21e003a..2b0f9fb6f5e1d795ffd209ef0105edcca2ae5055 100644 (file)
 #include <cassert>
 #include <vector>
 #include <algorithm>
-#include "../utils/fs.h"
-#include "../utils/string.h"
-#include "../utils/log.h"
-#include "../utils/vector.h"
-#include "../glue/main.h"
-#include "../glue/channel.h"
-#include "kernelMidi.h"
-#include "mixer.h"
-#include "const.h"
-#include "init.h"
-#include "pluginHost.h"
-#include "pluginManager.h"
-#include "plugin.h"
-#include "waveFx.h"
-#include "conf.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"
-#include "waveManager.h"
-#include "channelManager.h"
-#include "mixerHandler.h"
-
-
-using std::vector;
-using std::string;
+#include "utils/fs.h"
+#include "utils/string.h"
+#include "utils/log.h"
+#include "utils/vector.h"
+#include "glue/main.h"
+#include "glue/channel.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/channels/channelManager.h"
+#include "core/kernelMidi.h"
+#include "core/mixer.h"
+#include "core/const.h"
+#include "core/init.h"
+#include "core/pluginHost.h"
+#include "core/pluginManager.h"
+#include "core/plugin.h"
+#include "core/waveFx.h"
+#include "core/conf.h"
+#include "core/patch.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/clock.h"
+#include "core/kernelAudio.h"
+#include "core/midiMapConf.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include "core/mixerHandler.h"
 
 
 namespace giada {
@@ -67,49 +66,76 @@ namespace mh
 {
 namespace
 {
-#ifdef WITH_VST
+std::unique_ptr<Channel> createChannel_(ChannelType type, ID columnId, ID channelId=0)
+{
+       std::unique_ptr<Channel> ch = channelManager::create(type, 
+               kernelAudio::getRealBufSize(), conf::inputMonitorDefaultOn, columnId);
 
-int readPatchPlugins_(const vector<patch::plugin_t>& list, pluginHost::StackType t)
-{
-       int ret = 1;
-       for (const patch::plugin_t& ppl : list) {
-               std::unique_ptr<Plugin> p = pluginManager::makePlugin(ppl.path);
-               if (p != nullptr) {
-                       p->setBypass(ppl.bypass);
-                       for (unsigned j=0; j<ppl.params.size(); j++)
-                               p->setParameter(j, ppl.params.at(j));
-                       pluginHost::addPlugin(std::move(p), t, &mixer::mutex, nullptr);
-                       ret &= 1;
-               }
-               else
-                       ret &= 0;
+       if (type == ChannelType::MASTER) {
+               assert(channelId != 0);
+               ch->id = channelId;
        }
-       return ret;
+       
+       return ch;      
 }
 
-#endif
+
+/* -------------------------------------------------------------------------- */
+
+
+waveManager::Result createWave_(const std::string& fname)
+{
+       waveManager::Result res = waveManager::createFromFile(fname); 
+       if (res.status != G_RES_OK)
+               return res;
+       if (res.wave->getRate() != conf::samplerate) {
+               u::log::print("[mh::createWave_] input rate (%d) != system rate (%d), conversion needed\n",
+                       res.wave->getRate(), conf::samplerate);
+               res.status = waveManager::resample(*res.wave.get(), conf::rsmpQuality, conf::samplerate); 
+               if (res.status != G_RES_OK)
+                       return res;
+       }
+       return res;
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int getNewChanIndex()
+bool channelHas_(std::function<bool(const Channel*)> f)
 {
-       /* always skip last channel: it's the last one just added */
+       model::ChannelsLock lock(model::channels);
+       return std::any_of(model::channels.begin(), model::channels.end(), f);
+}
 
-       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;
+/* -------------------------------------------------------------------------- */
+
+
+bool canInputRec_(size_t chanIndex)
+{
+       model::ChannelsLock l(model::channels);
+       return model::channels.get(chanIndex)->canInputRec();
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+/* pushWave_
+Pushes a new wave into Sample Channel 'ch' and into the corresponding Wave list.
+Use this when modifying a local model, before swapping it. */
+
+void pushWave_(SampleChannel& ch, std::unique_ptr<Wave>&& w, bool clone)
+{
+       if (ch.hasWave && !clone) // Don't pop if cloning a channel
+               model::waves.pop(model::getIndex(model::waves, ch.waveId));
+       
+       ID    id   = w->id;
+       Frame size = w->getSize();
+
+       model::waves.push(std::move(w));
+       ch.pushWave(id, size);  
+}
 }; // {anonymous}
 
 
@@ -118,13 +144,44 @@ int getNewChanIndex()
 /* -------------------------------------------------------------------------- */
 
 
-bool uniqueSamplePath(const SampleChannel* skip, const string& path)
+void init()
+{
+       mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
+       
+       model::channels.push(createChannel_(ChannelType::MASTER, /*column=*/0, 
+               mixer::MASTER_OUT_CHANNEL_ID));
+       model::channels.push(createChannel_(ChannelType::MASTER, /*column=*/0, 
+               mixer::MASTER_IN_CHANNEL_ID));
+       model::channels.push(createChannel_(ChannelType::PREVIEW, /*column=*/0, 
+               mixer::PREVIEW_CHANNEL_ID));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void close()
+{
+       mixer::disable();
+       model::channels.clear();
+       model::waves.clear();
+       mixer::close();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool uniqueSamplePath(ID channelToSkip, const std::string& path)
 {
-       for (const Channel* ch : mixer::channels) {
-               if (skip == ch || ch->type != ChannelType::SAMPLE) // skip itself and MIDI channels
+       model::ChannelsLock cl(model::channels);
+       model::WavesLock    wl(model::waves);
+
+       for (const Channel* c : model::channels) {
+               if (c->id == channelToSkip || c->type != ChannelType::SAMPLE)
                        continue;
-               const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
-               if (sch->wave != nullptr && path == sch->wave->getPath())
+               const SampleChannel* sc = static_cast<const SampleChannel*>(c);
+               if (sc->hasWave && model::get(model::waves, sc->waveId).getPath() == path)
                        return false;
        }
        return true;
@@ -134,205 +191,405 @@ bool uniqueSamplePath(const SampleChannel* skip, const string& path)
 /* -------------------------------------------------------------------------- */
 
 
-Channel* addChannel(ChannelType type)
+ID addChannel(ChannelType type, ID columnId)
 {
-       Channel* ch = channelManager::create(type, kernelAudio::getRealBufSize(), 
-               conf::inputMonitorDefaultOn);
-
-       pthread_mutex_lock(&mixer::mutex);
-       mixer::channels.push_back(ch);
-       pthread_mutex_unlock(&mixer::mutex);
-
-       ch->index = getNewChanIndex();
-       gu_log("[addChannel] channel index=%d added, type=%d, total=%d\n",
-               ch->index, ch->type, mixer::channels.size());
-       return ch;
+       std::unique_ptr<Channel> c  = createChannel_(type, columnId);
+       ID                       id = c->id;
+       model::channels.push(std::move(c));
+       return id;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void deleteChannel(Channel* target)
+int loadChannel(ID channelId, const std::string& fname)
 {
-       int index = u::vector::indexOf(mixer::channels, target);
-       assert(index != -1);
-       
-       pthread_mutex_lock(&mixer::mutex);
-       delete mixer::channels.at(index);
-       mixer::channels.erase(mixer::channels.begin() + index);
-       pthread_mutex_unlock(&mixer::mutex);
+       waveManager::Result res = createWave_(fname); 
+
+       if (res.status != G_RES_OK) 
+               return res.status;
+
+       model::onSwap(model::channels, channelId, [&](Channel& c)
+       {
+               pushWave_(static_cast<SampleChannel&>(c), std::move(res.wave), /*clone=*/false);
+       });
+
+       return res.status;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-Channel* getChannelByIndex(int index)
+int addAndLoadChannel(ID columnId, const std::string& fname)
 {
-       for (Channel* ch : mixer::channels)
-               if (ch->index == index)
-                       return ch;
-       gu_log("[getChannelByIndex] channel at index %d not found!\n", index);
-       return nullptr;
+       waveManager::Result res = createWave_(fname);
+       if (res.status == G_RES_OK)
+               addAndLoadChannel(columnId, std::move(res.wave));
+       return res.status;
+}
+
+
+void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w)
+{
+       std::unique_ptr<Channel> ch = createChannel_(ChannelType::SAMPLE, 
+               columnId);
+
+       pushWave_(static_cast<SampleChannel&>(*ch.get()), std::move(w), /*clone=*/false);
+
+       /* Then add new channel to Channel list. */
+
+       model::channels.push(std::move(ch));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void stopSequencer()
+void cloneChannel(ID channelId)
 {
-       clock::setStatus(ClockStatus::STOPPED);
-       for (Channel* ch : mixer::channels)
-               ch->stopBySeq(conf::chansStopOnSeqHalt);
+       model::ChannelsLock cl(model::channels);
+       model::WavesLock    wl(model::waves);
+
+       const Channel&           oldChannel = model::get(model::channels, channelId);
+       std::unique_ptr<Channel> newChannel = channelManager::create(oldChannel);
+
+       /* Clone plugins, actions and wave first in their own lists. */
+       
+#ifdef WITH_VST
+       pluginHost::clonePlugins(oldChannel, *newChannel.get());
+#endif
+       recorderHandler::cloneActions(channelId, newChannel->id);
+       
+       if (newChannel->hasData()) {
+               SampleChannel* sch  = static_cast<SampleChannel*>(newChannel.get());
+               Wave&          wave = model::get(model::waves, sch->waveId);
+               pushWave_(*sch, waveManager::createFromWave(wave, 0, wave.getSize()), /*clone=*/true);
+       }
+
+       /* Then push the new channel in the channels list. */
+
+       model::channels.push(std::move(newChannel));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateSoloCount()
+void freeChannel(ID channelId)
 {
-       for (const Channel* ch : mixer::channels)
-               if (ch->solo) {
-                       mixer::hasSolos = true;
-                       return;
-               }
-       mixer::hasSolos = false;
+       bool hasWave;   
+       ID   waveId;
+       
+       /* Remove Wave reference from Channel. */
+       
+       model::onSwap(model::channels, channelId, [&](Channel& c)
+       {
+               SampleChannel& sc = static_cast<SampleChannel&>(c);
+               hasWave = sc.hasWave;
+               waveId  = sc.waveId;
+               sc.empty();
+       });
+
+       /* Then remove the actual Wave, if any. */
+       
+       if (hasWave)
+               model::waves.pop(model::getIndex(model::waves, waveId)); 
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void readPatch()
+void freeAllChannels()
 {
-       mixer::ready = false;
+       for (size_t i = 0; i < model::channels.size(); i++)
+               model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c) { c.empty(); });
+       model::waves.clear();
+}
 
-       mixer::outVol.store(patch::masterVolOut);
-       mixer::inVol.store(patch::masterVolIn);
-       clock::setBpm(patch::bpm);
-       clock::setBars(patch::bars);
-       clock::setBeats(patch::beats);
-       clock::setQuantize(patch::quantize);
-       clock::updateFrameBars();
-       mixer::setMetronome(patch::metronome);
 
-#ifdef WITH_VST
+/* -------------------------------------------------------------------------- */
 
-       readPatchPlugins_(patch::masterInPlugins, pluginHost::StackType::MASTER_IN);
-       readPatchPlugins_(patch::masterOutPlugins, pluginHost::StackType::MASTER_OUT);
 
+void deleteChannel(ID channelId)
+{
+       bool            hasWave = false;
+       ID              waveId;
+#ifdef WITH_VST
+       std::vector<ID> pluginIds;
 #endif
 
-       /* Rewind and update frames in Mixer. Also alloc new space in the virtual
-       input buffer, in case the patch has a sequencer size != default one (which is
-       very likely). */
+       model::onGet(model::channels, channelId, [&](Channel& c)
+       {
+#ifdef WITH_VST
+               pluginIds = c.pluginIds;
+#endif
+               if (c.type != ChannelType::SAMPLE)
+                       return;
+               SampleChannel& sc = static_cast<SampleChannel&>(c);
+               hasWave   = sc.hasWave;
+               waveId    = sc.waveId;
+       });
+       
+       model::channels.pop(model::getIndex(model::channels, channelId));
 
-       mixer::rewind();
-       mixer::allocVirtualInput(clock::getFramesInLoop());
-       mixer::ready = true;
+       if (hasWave)
+               model::waves.pop(model::getIndex(model::waves, waveId)); 
+
+#ifdef WITH_VST
+       pluginHost::freePlugins(pluginIds);
+#endif
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void rewindSequencer()
+void renameChannel(ID channelId, const std::string& name)
 {
-       if (clock::getQuantize() > 0 && clock::isRunning())   // quantize rewind
-               mixer::rewindWait = true;
-       else
-               mixer::rewind();
+       model::onSwap(model::channels, channelId, [&](Channel& c) { c.name = name; });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool startInputRec()
+void startSequencer()
 {
-       if (!hasRecordableSampleChannels())
-               return false;
+       switch (clock::getStatus()) {
+               case ClockStatus::STOPPED:
+                       clock::setStatus(ClockStatus::RUNNING); 
+                       break;
+               case ClockStatus::WAITING:
+                       clock::setStatus(ClockStatus::RUNNING); 
+                       recManager::stopActionRec();
+                       break;
+               default: 
+                       break;
+       }
 
-       for (Channel* ch : mixer::channels) {
+#ifdef __linux__
+       kernelAudio::jackStart();
+#endif
+}
 
-               if (!ch->canInputRec())
-                       continue;
 
-               SampleChannel* sch = static_cast<SampleChannel*>(ch);
+/* -------------------------------------------------------------------------- */
 
-               /* Allocate empty sample for the current channel. */
 
-               string name    = string("TAKE-" + u::string::iToString(patch::lastTakeId++));
-               string nameExt = name + ".wav";
+void stopSequencer()
+{
+       clock::setStatus(ClockStatus::STOPPED);
 
-               sch->pushWave(waveManager::createEmpty(clock::getFramesInLoop(), 
-                       G_MAX_IO_CHANS, conf::samplerate, nameExt));
-               sch->name = name; 
+       /* Stop channels with explicit locks. The RAII version would trigger a
+       deadlock if recManager::stopInputRec() is called down below. */
 
-               gu_log("[startInputRec] start input recs using Channel %d with size %d "
-                       "on frame=%d\n", sch->index, clock::getFramesInLoop(), clock::getCurrentFrame());
-       }
+       model::channels.lock();
+       for (Channel* c : model::channels)
+               c->stopBySeq(conf::chansStopOnSeqHalt);
+       model::channels.unlock();
 
-       mixer::startInputRec();
-       return true;
+#ifdef __linux__
+       kernelAudio::jackStop();
+#endif
+
+       /* If recordings (both input and action) are active deactivate them, but 
+       store the takes. RecManager takes care of it. */
+
+       if (recManager::isRecordingAction())
+               recManager::stopActionRec();
+       else
+       if (recManager::isRecordingInput())
+               recManager::stopInputRec();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void stopInputRec()
+void toggleSequencer()
 {
-       mixer::mergeVirtualInput();
-       mixer::recording = false;
+       clock::isRunning() ? stopSequencer() : startSequencer();
+}
+
+
+/* -------------------------------------------------------------------------- */
 
-       for (Channel* ch : mixer::channels)
-               ch->stopInputRec(clock::getCurrentFrame());
 
-       gu_log("[mh] stop input recs\n");
+void updateSoloCount()
+{
+       model::onSwap(model::mixer, [&](model::Mixer& m)
+       {
+               m.hasSolos = channelHas_([](const Channel* ch) { return ch->solo; });
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool hasArmedSampleChannels()
+void setInVol(float v)
 {
-       return std::any_of(mixer::channels.begin(), mixer::channels.end(), [](const Channel* ch)
+       model::onGet(model::channels, mixer::MASTER_IN_CHANNEL_ID, [&](Channel& c)
        {
-               return ch->type == ChannelType::SAMPLE && ch->armed;
+               c.volume = v;
        });
 }
 
 
-bool hasRecordableSampleChannels()
+void setOutVol(float v)
 {
-       return std::any_of(mixer::channels.begin(), mixer::channels.end(), [](const Channel* ch)
+       model::onGet(model::channels, mixer::MASTER_OUT_CHANNEL_ID, [&](Channel& c)
        {
-               return ch->canInputRec();
+               c.volume = v;
        });
 }
 
 
-bool hasLogicalSamples()
+void setInToOut(bool v)
 {
-       return std::any_of(mixer::channels.begin(), mixer::channels.end(), [](const Channel* ch)
+       model::onSwap(model::mixer, [&](model::Mixer& m)
        {
-               return ch->hasLogicalData();
+               m.inToOut = v;
        });
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+
+float getInVol()
+{
+       model::ChannelsLock l(model::channels); 
+       return model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).volume;
+}
+
+
+float getOutVol()
+{
+       model::ChannelsLock l(model::channels); 
+       return model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).volume;
+}
+
+
+bool getInToOut()
+{
+       model::MixerLock lock(model::mixer); return model::mixer.get()->inToOut;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindSequencer()
+{
+       if (clock::getQuantize() > 0 && clock::isRunning())   // quantize rewind
+               mixer::rewindWait = true;
+       else {
+               clock::rewind();
+               rewindChannels();
+       }
+
+       /* 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__
+       kernelAudio::jackSetPosition(0);
+#endif
+
+       if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+               kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindChannels()
+{
+       for (size_t i = 3; i < model::channels.size(); i++)
+               model::onSwap(model::channels, model::getId(model::channels, i), [&](Channel& c) { c.rewindBySeq();     });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* Push a new Wave into each recordable channel. Warning: this algorithm will 
+require some changes when we will allow overdubbing (the previous existing Wave
+has to be overwritten somehow). */
+
+void finalizeInputRec()
+{
+       const AudioBuffer& virtualInput = mixer::getVirtualInput();
+
+       /* Can't loop with foreach, as it would require a lock on model::channels
+       list which would deadlock during the model::channels::swap() call below. 
+       Also skip channels 0, 1 and 2: they are MASTER_IN, MASTER_OUT and PREVIEW. */
+
+       for (size_t i = 3; i < model::channels.size(); i++) {
+
+               if (!canInputRec_(i))
+                       continue;
+
+               /* Create a new Wave with audio coming from Mixer's virtual input. */
+
+               std::string filename = "TAKE-" + std::to_string(patch::lastTakeId++) + ".wav";
+       
+               std::unique_ptr<Wave> wave = waveManager::createEmpty(clock::getFramesInLoop(), 
+                       G_MAX_IO_CHANS, conf::samplerate, filename);
+
+               wave->copyData(virtualInput[0], virtualInput.countFrames());
+
+               /* Update Channel with the new Wave. The function pushWave_ will take
+               take of pushing it into the stack first. Also start all channels in
+               LOOP mode. */
+
+               model::onSwap(model::channels, model::getId(model::channels, i), [&](Channel& c)
+               {
+                       SampleChannel& sc = static_cast<SampleChannel&>(c);
+                       pushWave_(sc, std::move(wave), /*clone=*/false);
+                       if (sc.isAnyLoopMode())
+                               sc.playStatus = ChannelStatus::PLAY;
+               });
+       }
+
+       mixer::clearVirtualInput();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasRecordableSampleChannels()
+{
+       return channelHas_([](const Channel* ch) { return ch->canInputRec(); });
+}
+
+
+bool hasLogicalSamples()
+{
+       return channelHas_([](const Channel* ch) { return ch->hasLogicalData(); });
+}
+
+
 bool hasEditedSamples()
 {
-       return std::any_of(mixer::channels.begin(), mixer::channels.end(), [](const Channel* ch)
-       {
-               return ch->hasEditedData();
-       });
+       return channelHas_([](const Channel* ch) { return ch->hasEditedData(); });
 }
 
 
+bool hasActions()
+{
+       return channelHas_([](const Channel* ch) { return ch->hasActions; });
+}
+
+
+bool hasAudioData()
+{
+       return channelHas_([](const Channel* ch) { return ch->hasData(); });
+}
 }}}; // giada::m::mh::
index 19e514e2a5ebf74fff143915f6ad99f43d6020c5..64d064ba15aeddcc7dbac430c2af4fc295b19cae 100644 (file)
 #define G_MIXER_HANDLER_H
 
 
+#include <memory>
 #include <string>
 #include "types.h"
 
 
-
-
 namespace giada {
 namespace m 
 {
+class Wave;
 class Channel;
 class SampleChannel;
 
 namespace mh
 {
+/* init
+Initializes mixer. */
+
+void init();
+
+/* close
+Closes mixer and frees resources. */
+
+void close();
+
 /* addChannel
-Adds a new channel of type 'type' into mixer's stack. */
+Adds a new channel of type 'type' into the channels stack. Returns the new
+channel ID. */
 
-Channel* addChannel(ChannelType type);
+ID addChannel(ChannelType type, ID columnId);
 
-/* deleteChannel
-Completely removes a channel from the stack. */
+/* loadChannel
+Loads a new Wave inside a Sample Channel. */
 
-void deleteChannel(Channel* ch);
+int loadChannel(ID channelId, const std::string& fname);
 
-/* getChannelByIndex
-Returns channel with given index 'i'. */
+/* addAndLoadChannel (1)
+Creates a new channels, fills it with a Wave and then add it to the stack. */
 
-Channel* getChannelByIndex(int i);
+int addAndLoadChannel(ID columnId, const std::string& fname); 
 
-/* stopSequencer
-Stops the sequencer, with special case if samplesStopOnSeqHalt is true. */
+/* addAndLoadChannel (2)
+Same as (1), but Wave is already provided. */
 
-void stopSequencer();
+void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w); 
+
+/* freeChannel
+Unloads existing Wave from a Sample Channel. */
+
+void freeChannel(ID channelId);
+
+/* deleteChannel
+Completely removes a channel from the stack. */
 
+void deleteChannel(ID channelId);
+
+void cloneChannel(ID channelId);
+void renameChannel(ID channelId, const std::string& name);
+void freeAllChannels();
+
+void startSequencer();
+void stopSequencer();
+void toggleSequencer();
 void rewindSequencer();
+void rewindChannels();
+
+void setInToOut(bool v);
+void setInVol(float f);
+void setOutVol(float f);
 
 /* updateSoloCount
 Updates the number of solo-ed channels in mixer. */
 
 void updateSoloCount();
 
-/* loadPatch
-Loads a path or a project (if isProject) into Mixer. If isProject, path must 
-contain the address of the project folder. */
-
-void readPatch();
-
-/* startInputRec - record from line in
-Creates a new empty wave in the first available channels. Returns false if
-there are no available channels. */
-
-bool startInputRec();
+/* finalizeInputRec
+Fills armed Sample Channels with audio data coming from an input recording
+session. */
 
-void stopInputRec();
+void finalizeInputRec();
 
 /* uniqueSamplePath
 Returns true if path 'p' is unique. Requires SampleChannel 'skip' in order
 to skip check against itself. */
 
-bool uniqueSamplePath(const SampleChannel* skip, const std::string& p);
+bool uniqueSamplePath(ID channelToSkip, const std::string& p);
 
 /* hasLogicalSamples
 True if 1 or more samples are logical (memory only, such as takes) */
@@ -100,16 +125,25 @@ True if 1 or more samples was edited via gEditor */
 
 bool hasEditedSamples();
 
-/* hasArmedSampleChannels
-Tells whether Mixer has one or more Sample Channels armed for input recording. */
-
-bool hasArmedSampleChannels();
-
 /* hasRecordableSampleChannels
 Tells whether Mixer has one or more recordable Sample Channels, that is: 
 a) armed; b) empty (no Wave). */
 
 bool hasRecordableSampleChannels();
+
+/* hasActions
+True if at least one Channel has actions recorded in it. */
+
+bool hasActions();
+
+/* hasAudioData
+True if at least one Sample Channel has some audio recorded in it. */
+
+bool hasAudioData();
+
+float getInVol();
+float getOutVol();
+bool getInToOut();
 }}}  // giada::m::mh::
 
 
diff --git a/src/core/model/model.cpp b/src/core/model/model.cpp
new file mode 100644 (file)
index 0000000..d40c333
--- /dev/null
@@ -0,0 +1,131 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 "core/model/model.h"
+#ifndef NDEBUG
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/channelManager.h"
+#endif
+
+
+namespace giada {
+namespace m {
+namespace model
+{
+RCUList<Clock>    clock(std::make_unique<Clock>());
+RCUList<Mixer>    mixer(std::make_unique<Mixer>());
+RCUList<Kernel>   kernel(std::make_unique<Kernel>());
+RCUList<Recorder> recorder(std::make_unique<Recorder>());
+RCUList<Actions>  actions(std::make_unique<Actions>());
+RCUList<Channel>  channels;
+RCUList<Wave>     waves;
+#ifdef WITH_VST
+RCUList<Plugin>   plugins;
+#endif
+
+
+Actions::Actions(const Actions& o) : map(o.map)
+{
+       /* Needs to update all pointers of prev and next actions with addresses 
+       coming from the new 'actions' map.  */
+
+       recorder::updateMapPointers(map);
+}
+
+
+#ifndef NDEBUG
+
+void debug()
+{
+       ChannelsLock chl(channels);
+       ClockLock    cl(clock);
+       WavesLock    wl(waves);
+       ActionsLock  al(actions);
+#ifdef WITH_VST
+       PluginsLock  pl(plugins);
+#endif
+
+       puts("======== SYSTEM STATUS ========");
+       
+       puts("model::channels");
+
+       int i = 0;
+       for (const Channel* c : channels) {
+               printf("    %d) %p - ID=%d name='%s' columnID=%d\n", i++, (void*)c, c->id, c->name.c_str(), c->columnId);
+               if (c->hasData())
+                       printf("        wave: ID=%d\n", static_cast<const SampleChannel*>(c)->waveId);
+#ifdef WITH_VST
+               if (c->pluginIds.size() > 0) {
+                       puts("        plugins:");
+                       for (ID id : c->pluginIds)
+                               printf("            ID=%d\n", id);
+               }
+#endif
+       }
+
+       puts("model::waves");
+
+       i = 0;
+       for (const Wave* w : waves) 
+               printf("    %d) %p - ID=%d name='%s'\n", i++, (void*)w, w->id, w->getPath().c_str());
+               
+#ifdef WITH_VST
+       puts("model::plugins");
+
+       i = 0;
+       for (const Plugin* p : plugins) {
+               if (p->valid)
+                       printf("    %d) %p - ID=%d name='%s'\n", i++, (void*)p, p->id, p->getName().c_str());
+               else
+                       printf("    %d) %p - ID=%d INVALID\n", i++, (void*)p, p->id); 
+       }
+#endif
+
+       puts("model::clock");
+
+       printf("    clock.status   = %d\n", static_cast<int>(clock.get()->status));
+       printf("    clock.bars     = %d\n", clock.get()->bars);
+       printf("    clock.beats    = %d\n", clock.get()->beats);
+       printf("    clock.bpm      = %f\n", clock.get()->bpm);
+       printf("    clock.quantize = %d\n", clock.get()->quantize);
+
+       puts("model::actions");
+
+       for (auto& kv : actions.get()->map) {
+               printf("    frame: %d\n", kv.first);
+               for (const Action& a : kv.second)
+                       printf("        (%p) - ID=%d, frame=%d, channel=%d, value=0x%X, prevId=%d, prev=%p, nextId=%d, next=%p\n", 
+                               (void*) &a, a.id, a.frame, a.channelId, a.event.getRaw(), a.prevId, (void*) a.prev, a.nextId, (void*) a.next);  
+       }
+       
+       puts("===============================");
+}
+
+#endif
+}}} // giada::m::model::
diff --git a/src/core/model/model.h b/src/core/model/model.h
new file mode 100644 (file)
index 0000000..7f07916
--- /dev/null
@@ -0,0 +1,283 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_RENDER_MODEL_H
+#define G_RENDER_MODEL_H
+
+
+#include <algorithm>
+#include <type_traits>
+#include "core/channels/channel.h"
+#include "core/const.h"
+#include "core/wave.h"
+#include "core/plugin.h"
+#include "core/rcuList.h"
+#include "core/recorder.h"
+
+
+namespace giada {
+namespace m {
+namespace model
+{
+struct Clock
+{      
+       ClockStatus status       = ClockStatus::STOPPED;
+       int         framesInLoop = 0;
+       int         framesInBar  = 0;
+       int         framesInBeat = 0;
+       int         framesInSeq  = 0;
+       int         bars         = G_DEFAULT_BARS;
+       int         beats        = G_DEFAULT_BEATS;
+       float       bpm          = G_DEFAULT_BPM;
+       int         quantize     = G_DEFAULT_QUANTIZE;
+};
+
+struct Mixer
+{
+       bool hasSolos = false;    
+       bool inToOut  = false;
+};
+
+
+struct Kernel
+{
+       bool audioReady = false;
+       bool midiReady  = false;
+};
+
+
+struct Recorder
+{
+       bool isRecordingAction = false;
+       bool isRecordingInput  = false;
+};
+
+
+struct Actions
+{
+       Actions() = default;
+       Actions(const Actions& o);
+
+       recorder::ActionMap map;
+};
+
+
+using ClockLock    = RCUList<Clock>::Lock;
+using MixerLock    = RCUList<Mixer>::Lock;
+using KernelLock   = RCUList<Kernel>::Lock;
+using RecorderLock = RCUList<Recorder>::Lock;
+using ActionsLock  = RCUList<Actions>::Lock;
+using ChannelsLock = RCUList<Channel>::Lock;
+using WavesLock    = RCUList<Wave>::Lock;
+#ifdef WITH_VST
+using PluginsLock  = RCUList<Plugin>::Lock;
+#endif
+
+extern RCUList<Clock>    clock;
+extern RCUList<Mixer>    mixer;
+extern RCUList<Kernel>   kernel;
+extern RCUList<Recorder> recorder;
+extern RCUList<Actions>  actions;
+extern RCUList<Channel>  channels;
+extern RCUList<Wave>     waves;
+#ifdef WITH_VST
+extern RCUList<Plugin>   plugins;
+#endif
+
+
+/* ---------------------------------------------------------------------------*/ 
+
+
+template <typename T> struct has_id : std::false_type {};
+template <> struct has_id<Channel>  : std::true_type {};
+template <> struct has_id<Wave>     : std::true_type {};
+#ifdef WITH_VST
+template <> struct has_id<Plugin>   : std::true_type {};
+#endif
+
+template <typename T> struct is_copyable : std::true_type {};
+template <> struct is_copyable<Channel>  : std::false_type {};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename L>
+auto getIter(L& list, ID id)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
+       {
+               return t->id == id;
+       });
+       assert(it != list.end());
+       return it;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename L>
+size_t getIndex(L& list, ID id)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       typename L::Lock l(list);
+       return std::distance(list.begin(), getIter(list, id));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename L>
+ID getId(L& list, size_t i)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       typename L::Lock l(list);
+       return list.get(i)->id;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename L>
+typename L::value_type& get(L& list, ID id)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       return **getIter(list, id);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* onGet (1)
+Utility function for reading ID-based things from a RCUList. */
+
+template<typename L>
+void onGet(L& list, ID id, std::function<void(typename L::value_type&)> f)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       typename L::Lock l(list);
+       f(**getIter(list, id));
+}
+
+
+/* onGet (2)
+Same as (1), for non-ID-based things. */
+
+template<typename L>
+void onGet(L& list, std::function<void(typename L::value_type&)> f)
+{
+       static_assert(!has_id<typename L::value_type>(), "This type has ID");
+       typename L::Lock l(list);
+       f(*list.get());
+}
+
+
+/* ---------------------------------------------------------------------------*/ 
+
+
+template<typename L>
+void onSwapByIndex_(L& list, size_t i, std::function<void(typename L::value_type&)> f)
+{
+       std::unique_ptr<typename L::value_type> o = list.clone(i);
+       f(*o.get());
+       list.swap(std::move(o), i);
+}
+
+/* onSwapById_ (1)
+Regular version for copyable types. */
+
+template<typename L>
+void onSwapById_(L& list, ID id, std::function<void(typename L::value_type&)> f, 
+       const std::true_type& /*is_copyable=true*/)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       onSwapByIndex_(list, getIndex(list, id), f); 
+}
+
+
+/* onSwapById_ (2)
+Custom version for non-copyable types, e.g. Channel types. Let's wait for the
+no-virtual channel refactoring... */
+
+template<typename L>
+void onSwapById_(L& list, ID id, std::function<void(typename L::value_type&)> f,
+       const std::false_type& /*is_copyable=false*/)
+{      
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       
+       size_t i = getIndex(list, id);
+       
+       list.lock();
+       std::unique_ptr<typename L::value_type> o(list.get(i)->clone());
+       list.unlock();
+
+       f(*o.get());
+
+       channels.swap(std::move(o), i);
+}
+
+
+/* onSwap (1)
+Utility function for swapping things in a RCUList. */
+
+template<typename L>
+void onSwap(L& list, ID id, std::function<void(typename L::value_type&)> f)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       onSwapById_(list, id, f, is_copyable<typename L::value_type>());
+}
+
+
+/* onSwap (2)
+Utility function for swapping things in a RCUList when the list contains only
+a single element (and so with no ID). */
+
+template<typename L>
+void onSwap(L& list, std::function<void(typename L::value_type&)> f)
+{
+       static_assert(!has_id<typename L::value_type>(), "This type has ID");
+       onSwapByIndex_(list, 0, f); 
+}
+
+
+/* ---------------------------------------------------------------------------*/ 
+
+
+#ifndef NDEBUG
+
+void debug();
+
+#endif
+}}} // giada::m::model::
+
+
+#endif
index 5f61b17147e9e208d70d2199769719bb92d2db68..a7a4cfbeb9453e9f7c152c2a29a47cbbea0d6727 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../utils/log.h"
-#include "../utils/string.h"
-#include "../utils/ver.h"
-#include "../utils/math.h"
-#include "const.h"
-#include "types.h"
-#include "storager.h"
-#include "midiEvent.h"
-#include "conf.h"
-#include "mixer.h"
+#include <jansson.h>
+#include "utils/log.h"
+#include "utils/string.h"
+#include "utils/ver.h"
+#include "utils/math.h"
+#include "utils/fs.h"
+#include "utils/json.h"
+#include "gui/elems/mainWindow/keyboard/column.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/dialogs/mainWindow.h"
+#include "core/model/model.h"
+#include "core/channels/channelManager.h"
+#include "core/channels/channel.h"
+#include "core/channels/midiChannel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/pluginManager.h"
+#include "core/waveManager.h"
+#include "core/const.h"
+#include "core/kernelAudio.h"
+#include "core/clock.h"
+#include "core/types.h"
+#include "core/midiEvent.h"
+#include "core/recorderHandler.h"
+#include "core/conf.h"
+#include "core/mixer.h"
 #include "patch.h"
 
 
-using std::string;
-using std::vector;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -48,452 +63,504 @@ namespace patch
 {
 namespace
 {
-/* sanitize
-Internal sanity check. */
-
-void sanitize()
+void sanitize_()
 {
        namespace um = u::math;
+       samplerate = um::bound(samplerate, 0, G_DEFAULT_SAMPLERATE);
+}
 
-       bpm          = um::bound(bpm, G_MIN_BPM, G_MAX_BPM, G_DEFAULT_BPM);
-       bars         = um::bound(bars, 1, G_MAX_BARS, G_DEFAULT_BARS);
-       beats        = um::bound(beats, 1, G_MAX_BEATS, G_DEFAULT_BEATS);
-       quantize     = um::bound(quantize, 0, G_MAX_QUANTIZE, G_DEFAULT_QUANTIZE);
-       masterVolIn  = um::bound(masterVolIn, 0.0f, 1.0f, G_DEFAULT_VOL);
-       masterVolOut = um::bound(masterVolOut, 0.0f, 1.0f, G_DEFAULT_VOL);
-       samplerate   = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate;
-
-       for (column_t& col : columns) {
-               col.index = col.index < 0 ? 0 : col.index;
-               col.width = col.width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col.width;
-       }
 
-       for (channel_t& ch : channels) {
-               ch.size        = um::bound(ch.size, G_GUI_CHANNEL_H_1, G_GUI_CHANNEL_H_4, G_GUI_CHANNEL_H_1);
-               ch.volume      = um::bound(ch.volume, 0.0f, 1.0f, G_DEFAULT_VOL);
-               ch.pan         = um::bound(ch.pan, 0.0f, 1.0f, 1.0f);
-               ch.boost       = um::bound(ch.boost, 1.0f, G_MAX_BOOST_DB, G_DEFAULT_BOOST);
-               ch.pitch       = um::bound(ch.pitch, 0.1f, G_MAX_PITCH, G_DEFAULT_PITCH);
-               ch.midiOutChan = um::bound(ch.midiOutChan, 0, G_MAX_MIDI_CHANS - 1, 0);
-       }
+void sanitize_(Channel& c)
+{
+       namespace um = u::math;
+       c.size        = um::bound(c.size, G_GUI_CHANNEL_H_1, G_GUI_CHANNEL_H_4);
+       c.volume      = um::bound(c.volume, 0.0f, G_DEFAULT_VOL);
+       c.pan         = um::bound(c.pan, 0.0f, 1.0f);
+       c.pitch       = um::bound(c.pitch, 0.1f, G_MAX_PITCH);
+       c.midiOutChan = um::bound(c.midiOutChan, 0, G_MAX_MIDI_CHANS - 1);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* modernize
-Makes sure an older patch is compatible with the current version. */
 
-void modernize()
+void readCommons_(json_t* j)
 {
-       /* Starting from 0.15.0 actions are recorded on frames, not samples. */
-       if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 0)) {
-               for (channel_t& ch : channels)
-                       for (action_t& a : ch.actions)
-                               a.frame /= 2;
-       }
+       namespace uj = u::json;
 
-       /* Starting from 0.15.1 Channel Modes have different values. */
-       if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 1)) {
-               for (channel_t& ch : channels) {
-                       if      (ch.mode == 0x04) ch.mode = static_cast<int>(ChannelMode::SINGLE_BASIC);
-                       else if (ch.mode == 0x08) ch.mode = static_cast<int>(ChannelMode::SINGLE_PRESS);
-                       else if (ch.mode == 0x10) ch.mode = static_cast<int>(ChannelMode::SINGLE_RETRIG);
-                       else if (ch.mode == 0x20) ch.mode = static_cast<int>(ChannelMode::LOOP_REPEAT);
-                       else if (ch.mode == 0x40) ch.mode = static_cast<int>(ChannelMode::SINGLE_ENDLESS);
-                       else if (ch.mode == 0x80) ch.mode = static_cast<int>(ChannelMode::LOOP_ONCE_BAR);
-               }
-       }
+       name         = uj::readString(j, PATCH_KEY_NAME);
+       samplerate   = uj::readInt(j, PATCH_KEY_SAMPLERATE);
+       lastTakeId   = uj::readInt(j, PATCH_KEY_LAST_TAKE_ID);
+       metronome    = uj::readBool(j, PATCH_KEY_METRONOME);
+
+       clock::setBpm     (uj::readFloat(j, PATCH_KEY_BPM));
+       clock::setBeats   (uj::readInt(j, PATCH_KEY_BEATS), uj::readInt(j, PATCH_KEY_BARS));
+       clock::setQuantize(uj::readInt(j, PATCH_KEY_QUANTIZE));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* setInvalid
-Helper function used to return invalid status while reading. */
 
-int setInvalid(json_t* jRoot)
+void readColumns_(json_t* j)
 {
-       json_decref(jRoot);
-       return PATCH_INVALID;
+       namespace uj = u::json;
+
+       json_t* jcs = json_object_get(j, PATCH_KEY_COLUMNS);
+       if (jcs == nullptr)
+               return;
+       
+       G_MainWin->keyboard->deleteAllColumns();
+
+       size_t  i;
+       json_t* jc;
+       json_array_foreach(jcs, i, jc) {
+               G_MainWin->keyboard->addColumn(
+                       uj::readInt(jc, PATCH_KEY_COLUMN_WIDTH),
+                       uj::readInt(jc, PATCH_KEY_COLUMN_ID));
+       };
 }
 
 
 /* -------------------------------------------------------------------------- */
 
+#ifdef WITH_VST
 
-bool readCommons(json_t* jContainer)
+void readPluginParams_(json_t* j, std::vector<float>& params)
 {
-       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;
+       json_t* jps = json_object_get(j, PATCH_KEY_PLUGIN_PARAMS);
+       if (jps == nullptr)
+               return;
+
+       size_t  i;
+       json_t* jp;
+       json_array_foreach(jps, i, jp)
+               params.push_back(json_real_value(jp));
 }
 
 
+void readMidiInPluginParams_(json_t* j, std::vector<uint32_t>& params)
+{
+       json_t* jps = json_object_get(j, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
+       if (jps == nullptr)
+               return;
+
+       size_t  i;
+       json_t* jp;
+       json_array_foreach(jps, i, jp)
+               params.push_back(json_integer_value(jp));
+}
+
 
 /* -------------------------------------------------------------------------- */
 
-#ifdef WITH_VST
 
-bool readPlugins(json_t* jContainer, vector<plugin_t>* container, const char* key)
+void readPlugins_(json_t* j)
 {
-       json_t* jPlugins = json_object_get(jContainer, key);
-       if (!storager::checkArray(jPlugins, key))
-               return 0;
+       namespace uj = u::json;
+
+       json_t* jps = json_object_get(j, PATCH_KEY_PLUGINS);
+       if (jps == nullptr)
+               return;
 
-       size_t pluginIndex;
-       json_t* jPlugin;
-       json_array_foreach(jPlugins, pluginIndex, jPlugin) {
+       size_t  i;
+       json_t* jp;
+       json_array_foreach(jps, i, jp) {
+               
+               if (!uj::isObject(jp))
+                       continue;
 
-               if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string
-                       return 0;
+               Plugin p;
+               p.id     = uj::readInt   (jp, PATCH_KEY_PLUGIN_ID);
+               p.path   = uj::readString(jp, PATCH_KEY_PLUGIN_PATH);
+               p.bypass = uj::readBool  (jp, PATCH_KEY_PLUGIN_BYPASS);
+
+               readPluginParams_(jp, p.params);
+               readMidiInPluginParams_(jp, p.midiInParams);
+
+               model::plugins.push(pluginManager::makePlugin(p));
+       }
+}
 
-               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;
+#endif
 
-               /* 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));
+void readWaves_(json_t* j, const std::string& basePath)
+{
+       namespace uj = u::json;
 
-               /* read midiIn params (midi learning on plugins' parameters) */
+       json_t* jws = json_object_get(j, PATCH_KEY_WAVES);
+       if (jws == nullptr)
+               return;
 
-               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  i;
+       json_t* jw;
+       json_array_foreach(jws, i, jw) {
+               
+               if (!uj::isObject(jw))
+                       continue;
 
-               size_t midiInParamIndex;
-               json_t* jMidiInParam;
-               json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
-                       plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
+               Wave w;
+               w.id   = uj::readInt(jw, PATCH_KEY_WAVE_ID);
+               w.path = basePath + uj::readString(jw, PATCH_KEY_WAVE_PATH);
 
-               container->push_back(plugin);
+               model::waves.push(std::move(waveManager::createFromPatch(w)));
        }
-       return 1;
+       return;
 }
 
-#endif
 
 /* -------------------------------------------------------------------------- */
 
 
-bool readActions(json_t* jContainer, channel_t* channel)
+void readActions_(json_t* j)
 {
-       json_t* jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
-       if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
-               return false;
-
-       size_t actionIndex;
-       json_t* jAction;
-       json_array_foreach(jActions, actionIndex, jAction) {
-
-               if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string
-                       return false;
-
-               action_t action;
-
-               /* TODO - temporary code for backward compatibility with old actions. 
-               To be removed in 0.16.0. */
-               if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 3)) {
-
-                       action.id = -1;
-                       action.channel = channel->index;
-                       if (!storager::setInt   (jAction, "frame", action.frame)) return 0;                     
-                       if (!storager::setUint32(jAction, "type",  action.event)) return 0;
-                       action.prev = -1;
-                       action.next = -1;
-
-                       if      (action.event == 0x01)   // KEY_PRESS
-                               action.event = MidiEvent(MidiEvent::NOTE_ON, 0, 0).getRaw();
-                       else if (action.event == 0x02)   // KEY_REL
-                               action.event = MidiEvent(MidiEvent::NOTE_OFF, 0, 0).getRaw();
-                       else if (action.event == 0x04)   // KEY_KILL
-                               action.event = MidiEvent(MidiEvent::NOTE_KILL, 0, 0).getRaw();
-                       else if (action.event == 0x20)   // VOLUME not supported, sorry :)
-                               continue;
-                       else if (action.event == 0x40)   // MIDI EVENT
-                               if (!storager::setUint32(jAction, "i_value", action.event)) return 0;
-               }
-               else {
-                       if (!storager::setInt   (jAction, G_PATCH_KEY_ACTION_ID,      action.id)) return 0;
-                       if (!storager::setInt   (jAction, G_PATCH_KEY_ACTION_CHANNEL, action.channel)) return 0;
-                       if (!storager::setInt   (jAction, G_PATCH_KEY_ACTION_FRAME,   action.frame)) return 0;
-                       if (!storager::setUint32(jAction, G_PATCH_KEY_ACTION_EVENT,   action.event)) return 0;
-                       if (!storager::setInt   (jAction, G_PATCH_KEY_ACTION_PREV,    action.prev)) return 0;
-                       if (!storager::setInt   (jAction, G_PATCH_KEY_ACTION_NEXT,    action.next)) return 0;
-               }
-               channel->actions.push_back(action);
+       namespace uj = u::json;
+
+       json_t* jas = json_object_get(j, PATCH_KEY_ACTIONS);
+       if (jas == nullptr)
+               return;
+
+       std::vector<Action> actions;
+       size_t  i;
+       json_t* ja;
+       json_array_foreach(jas, i, ja) {
+               
+               if (!uj::isObject(ja))
+                       continue;
+
+               Action a;
+               a.id        = uj::readInt(ja, G_PATCH_KEY_ACTION_ID);
+               a.channelId = uj::readInt(ja, G_PATCH_KEY_ACTION_CHANNEL);
+               a.frame     = uj::readInt(ja, G_PATCH_KEY_ACTION_FRAME);
+               a.event     = uj::readInt(ja, G_PATCH_KEY_ACTION_EVENT);
+               a.prevId    = uj::readInt(ja, G_PATCH_KEY_ACTION_PREV);
+               a.nextId    = uj::readInt(ja, G_PATCH_KEY_ACTION_NEXT);
+
+               actions.push_back(a);
        }
-       return true;
+
+       model::onSwap(model::actions, [&](model::Actions& a)
+       {
+               a.map = std::move(recorderHandler::makeActionsFromPatch(actions));
+       });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readChannelPlugins_(json_t* j, std::vector<ID>& pluginIds)
+{
+       json_t* jps = json_object_get(j, PATCH_KEY_CHANNEL_PLUGINS);
+       if (jps == nullptr)
+               return;
+
+       size_t  i;
+       json_t* jp;
+       json_array_foreach(jps, i, jp)
+               pluginIds.push_back(json_integer_value(jp));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool readChannels(json_t* jContainer)
+void readChannels_(json_t* j)
 {
-       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 " + u::string::iToString(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_SIZE,                 channel.size)) return 0;
-               if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_NAME,                 channel.name)) 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_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::setBool  (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL,  channel.midiInVeloAsVol)) 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::setInt   (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER,       channel.midiInFilter)) 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_READ_ACTIONS,         channel.readActions)) 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::setInt   (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT,             channel.midiOut)) return 0;
-               if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN,        channel.midiOutChan)) return 0;
-               if (!storager::setBool  (jChannel, PATCH_KEY_CHANNEL_ARMED,                channel.armed)) return 0;
-
-               readActions(jChannel, &channel);
+       namespace uj = u::json;
+
+       json_t* jcs = json_object_get(j, PATCH_KEY_CHANNELS);
+       if (jcs == nullptr)
+               return;
+
+       size_t  i;
+       json_t* jc;
+       json_array_foreach(jcs, i, jc) {
+
+               if (!uj::isObject(jc))
+                       continue;
+
+               Channel c;
+               c.id     = uj::readInt  (jc, PATCH_KEY_CHANNEL_ID);
+               c.type   = static_cast<ChannelType>(uj::readInt(jc, PATCH_KEY_CHANNEL_TYPE));
+               c.volume = uj::readFloat(jc, PATCH_KEY_CHANNEL_VOLUME);
+               
+               if (c.type != ChannelType::MASTER) {
+                       c.size              = G_GUI_CHANNEL_H_1; // TODO temporarily disabled - uj::readInt   (jc, PATCH_KEY_CHANNEL_SIZE);
+                       c.name              = uj::readString(jc, PATCH_KEY_CHANNEL_NAME);
+                       c.columnId          = uj::readInt   (jc, PATCH_KEY_CHANNEL_COLUMN);
+                       c.key               = uj::readInt   (jc, PATCH_KEY_CHANNEL_KEY);
+                       c.mute              = uj::readInt   (jc, PATCH_KEY_CHANNEL_MUTE);
+                       c.solo              = uj::readInt   (jc, PATCH_KEY_CHANNEL_SOLO);
+                       c.pan               = uj::readFloat (jc, PATCH_KEY_CHANNEL_PAN);
+                       c.hasActions        = uj::readBool  (jc, PATCH_KEY_CHANNEL_HAS_ACTIONS);
+                       c.midiIn            = uj::readBool  (jc, PATCH_KEY_CHANNEL_MIDI_IN);
+                       c.midiInKeyPress    = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS);
+                       c.midiInKeyRel      = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL);
+                       c.midiInKill        = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_KILL);
+                       c.midiInArm         = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_ARM);
+                       c.midiInVolume      = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME);
+                       c.midiInMute        = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_MUTE);
+                       c.midiInSolo        = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_SOLO);
+                       c.midiInFilter      = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_IN_FILTER);
+                       c.midiOutL          = uj::readBool  (jc, PATCH_KEY_CHANNEL_MIDI_OUT_L);
+                       c.midiOutLplaying   = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING);
+                       c.midiOutLmute      = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE);
+                       c.midiOutLsolo      = uj::readInt   (jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO);
+                       c.armed             = uj::readBool  (jc, PATCH_KEY_CHANNEL_ARMED);
+               }
 
 #ifdef WITH_VST
-               readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+               readChannelPlugins_(jc, c.pluginIds);
 #endif
-               channels.push_back(channel);
+
+               if (c.type == ChannelType::SAMPLE) {
+                       c.waveId            = uj::readInt  (jc, PATCH_KEY_CHANNEL_WAVE_ID);
+                       c.mode              = static_cast<ChannelMode>(uj::readInt(jc, PATCH_KEY_CHANNEL_MODE));
+                       c.begin             = uj::readInt  (jc, PATCH_KEY_CHANNEL_BEGIN);
+                       c.end               = uj::readInt  (jc, PATCH_KEY_CHANNEL_END);
+                       c.readActions       = uj::readBool (jc, PATCH_KEY_CHANNEL_READ_ACTIONS);
+                       c.pitch             = uj::readFloat(jc, PATCH_KEY_CHANNEL_PITCH);
+                       c.inputMonitor      = uj::readBool (jc, PATCH_KEY_CHANNEL_INPUT_MONITOR);
+                       c.midiInVeloAsVol   = uj::readBool (jc, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL);
+                       c.midiInReadActions = uj::readInt  (jc, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS);
+                       c.midiInPitch       = uj::readInt  (jc, PATCH_KEY_CHANNEL_MIDI_IN_PITCH);
+               }
+               else
+               if (c.type == ChannelType::MIDI) {
+                       c.midiOut     = uj::readInt(jc, PATCH_KEY_CHANNEL_MIDI_OUT);
+                       c.midiOutChan = uj::readInt(jc, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN);
+               }
+
+               sanitize_(c);
+               
+               if (c.type == ChannelType::MASTER || c.type == ChannelType::PREVIEW) {
+                       if (c.id == mixer::MASTER_OUT_CHANNEL_ID)
+                               model::onSwap(model::channels, mixer::MASTER_OUT_CHANNEL_ID, [&](m::Channel& ch) { ch.load(c); });
+                       else
+                       if (c.id == mixer::MASTER_IN_CHANNEL_ID)
+                               model::onSwap(model::channels, mixer::MASTER_IN_CHANNEL_ID, [&](m::Channel& ch) { ch.load(c); });
+               }
+               else
+                       model::channels.push(channelManager::create(c, kernelAudio::getRealBufSize()));
        }
-       return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool readColumns(json_t* jContainer)
+#ifdef WITH_VST
+
+void writePlugins_(json_t* j)
 {
-       json_t* jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
-       if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS))
-               return 0;
+       model::PluginsLock pl(model::plugins);
+
+       json_t* jps = json_array();
 
-       size_t columnIndex;
-       json_t* jColumn;
-       json_array_foreach(jColumns, columnIndex, jColumn) {
+       for (const m::Plugin* p : model::plugins) {
+               
+               /* Plugin. */
 
-               string columnIndexStr = "column " + u::string::iToString(columnIndex);
-               if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
-                       return 0;
+               json_t* jp = json_object();
+               json_object_set_new(jp, PATCH_KEY_PLUGIN_ID,     json_integer(p->id));
+               json_object_set_new(jp, PATCH_KEY_PLUGIN_PATH,   json_string(p->getUniqueId().c_str()));
+               json_object_set_new(jp, PATCH_KEY_PLUGIN_BYPASS, json_boolean(p->isBypassed()));
+               json_array_append_new(jps, jp);
 
-               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;
+               /* Plugin parameters. */
 
-               columns.push_back(column);
+               json_t* jparams = json_array();
+               for (int k = 0; k < p->getNumParameters(); k++)
+                       json_array_append_new(jparams, json_real(p->getParameter(k)));
+               json_object_set_new(jp, PATCH_KEY_PLUGIN_PARAMS, jparams);
+
+               /* MidiIn params (midi learning on plugins' parameters). */
+
+               json_t* jmidiparams = json_array();
+               for (uint32_t param : p->midiInParams)
+                       json_array_append_new(jmidiparams, json_integer(param));
+               json_object_set_new(jp, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS, jmidiparams);
        }
-       return 1;
+       json_object_set_new(j, PATCH_KEY_PLUGINS, jps);
 }
 
+#endif
+
 
 /* -------------------------------------------------------------------------- */
 
-#ifdef WITH_VST
 
-void writePlugins(json_t* jContainer, vector<plugin_t>* plugins, const char* key)
+void writeColumns_(json_t* j)
 {
-       json_t* jPlugins = json_array();
-       for (unsigned j=0; j<plugins->size(); j++) {
-               json_t*  jPlugin = json_object();
-               plugin_t plugin  = plugins->at(j);
-               json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PATH,   json_string(plugin.path.c_str()));
-               json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_BYPASS, json_boolean(plugin.bypass));
-               json_array_append_new(jPlugins, jPlugin);
-
-               /* plugin params */
-
-               json_t* jPluginParams = json_array();
-               for (unsigned z=0; z<plugin.params.size(); z++)
-                       json_array_append_new(jPluginParams, json_real(plugin.params.at(z)));
-               json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PARAMS, jPluginParams);
-
-               /* midiIn params (midi learning on plugins' parameters) */
-
-               json_t* jPluginMidiInParams = json_array();
-               for (unsigned z=0; z<plugin.midiInParams.size(); z++)
-                       json_array_append_new(jPluginMidiInParams, json_integer(plugin.midiInParams.at(z)));
-               json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS, jPluginMidiInParams);
-       }
-       json_object_set_new(jContainer, key, jPlugins);
-}
+       json_t* jcs = json_array();
 
-#endif
+       G_MainWin->keyboard->forEachColumn([&](const v::geColumn& c)
+       {
+               json_t* jc = json_object();
+               json_object_set_new(jc, PATCH_KEY_COLUMN_ID, json_integer(c.id));
+               json_object_set_new(jc, PATCH_KEY_COLUMN_WIDTH, json_integer(c.w()));
+
+               json_t* jchans = json_array();
+               c.forEachChannel([&](v::geChannel* c)
+               {
+                       json_array_append_new(jchans, json_integer(c->channelId));
+               });
+               json_object_set_new(jc, PATCH_KEY_COLUMN_CHANNELS, jchans);
+
+               json_array_append_new(jcs, jc);
+
+       });
+       json_object_set_new(j, PATCH_KEY_COLUMNS, jcs);
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void writeColumns(json_t* jContainer, vector<column_t>* columns)
+void writeActions_(json_t* j)
 {
-       json_t* jColumns = json_array();
-       for (unsigned i=0; i<columns->size(); i++) {
-               json_t*  jColumn = json_object();
-               column_t column  = columns->at(i);
-               json_object_set_new(jColumn, PATCH_KEY_COLUMN_INDEX, json_integer(column.index));
-               json_object_set_new(jColumn, PATCH_KEY_COLUMN_WIDTH, json_integer(column.width));
-               json_array_append_new(jColumns, jColumn);
+       model::ActionsLock l(model::actions);
+
+       json_t* jas = json_array();
+
+       for (auto& kv : model::actions.get()->map) {
+               for (m::Action& a : kv.second) {
+                       json_t* ja = json_object();
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_ID,      json_integer(a.id));
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_CHANNEL, json_integer(a.channelId));
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_FRAME,   json_integer(a.frame));
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_EVENT,   json_integer(a.event.getRaw()));
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_PREV,    json_integer(a.prevId));
+                       json_object_set_new(ja, G_PATCH_KEY_ACTION_NEXT,    json_integer(a.nextId));
+                       json_array_append_new(jas, ja);
+               }
        }
-       json_object_set_new(jContainer, PATCH_KEY_COLUMNS, jColumns);
+       json_object_set_new(j, PATCH_KEY_ACTIONS, jas);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void writeActions(json_t* jContainer, vector<action_t>& actions)
+void writeWaves_(json_t* j, bool isProject)
 {
-       json_t* jActions = json_array();
-       for (action_t action : actions) {
-               json_t* jAction = json_object();
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_ID,      json_integer(action.id));
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_CHANNEL, json_integer(action.channel));
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_FRAME,   json_integer(action.frame));
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_EVENT,   json_integer(action.event));
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_PREV,    json_integer(action.prev));
-               json_object_set_new(jAction, G_PATCH_KEY_ACTION_NEXT,    json_integer(action.next));
-               json_array_append_new(jActions, jAction);
+       model::WavesLock l(model::waves);
+
+       json_t* jws = json_array();
+
+       for (const m::Wave* w : model::waves) {
+       
+               std::string path = isProject ? u::fs::basename(w->getPath()) : w->getPath();
+
+               json_t* jw = json_object();
+               json_object_set_new(jw, PATCH_KEY_WAVE_ID,   json_integer(w->id));
+               json_object_set_new(jw, PATCH_KEY_WAVE_PATH, json_string(path.c_str()));
+               json_array_append_new(jws, jw);
        }
-       json_object_set_new(jContainer, PATCH_KEY_CHANNEL_ACTIONS, jActions);
+       json_object_set_new(j, PATCH_KEY_WAVES, jws);
 }
 
-
 /* -------------------------------------------------------------------------- */
 
 
-void writeCommons(json_t* jContainer)
+void writeCommons_(json_t* j, const std::string& name)
 {
-       json_object_set_new(jContainer, PATCH_KEY_HEADER,         json_string(header.c_str()));
-       json_object_set_new(jContainer, PATCH_KEY_VERSION,        json_string(version.c_str()));
-       json_object_set_new(jContainer, PATCH_KEY_VERSION_MAJOR,  json_integer(versionMajor));
-       json_object_set_new(jContainer, PATCH_KEY_VERSION_MINOR,  json_integer(versionMinor));
-       json_object_set_new(jContainer, PATCH_KEY_VERSION_PATCH,  json_integer(versionPatch));
-       json_object_set_new(jContainer, PATCH_KEY_NAME,           json_string(name.c_str()));
-       json_object_set_new(jContainer, PATCH_KEY_BPM,            json_real(bpm));
-       json_object_set_new(jContainer, PATCH_KEY_BARS,           json_integer(bars));
-       json_object_set_new(jContainer, PATCH_KEY_BEATS,          json_integer(beats));
-       json_object_set_new(jContainer, PATCH_KEY_QUANTIZE,       json_integer(quantize));
-       json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_IN,  json_real(masterVolIn));
-       json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_OUT, json_real(masterVolOut));
-       json_object_set_new(jContainer, PATCH_KEY_METRONOME,      json_integer(metronome));
-       json_object_set_new(jContainer, PATCH_KEY_LAST_TAKE_ID,   json_integer(lastTakeId));
-       json_object_set_new(jContainer, PATCH_KEY_SAMPLERATE,     json_integer(samplerate));
+       model::ClockLock cl(model::clock);
+       model::MixerLock ml(model::mixer);
+
+       json_object_set_new(j, PATCH_KEY_HEADER,         json_string("GIADAPTC"));
+       json_object_set_new(j, PATCH_KEY_VERSION_MAJOR,  json_integer(G_VERSION_MAJOR));
+       json_object_set_new(j, PATCH_KEY_VERSION_MINOR,  json_integer(G_VERSION_MINOR));
+       json_object_set_new(j, PATCH_KEY_VERSION_PATCH,  json_integer(G_VERSION_PATCH));
+       json_object_set_new(j, PATCH_KEY_NAME,           json_string(name.c_str()));
+       json_object_set_new(j, PATCH_KEY_BARS,           json_integer(model::clock.get()->bars));
+       json_object_set_new(j, PATCH_KEY_BEATS,          json_integer(model::clock.get()->beats));
+       json_object_set_new(j, PATCH_KEY_BPM,            json_real(model::clock.get()->bpm));
+       json_object_set_new(j, PATCH_KEY_QUANTIZE,       json_integer(model::clock.get()->quantize));
+       json_object_set_new(j, PATCH_KEY_LAST_TAKE_ID,   json_integer(lastTakeId));
+       json_object_set_new(j, PATCH_KEY_SAMPLERATE,     json_integer(samplerate));
+       json_object_set_new(j, PATCH_KEY_METRONOME,      json_boolean(mixer::isMetronomeOn()));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void writeChannels(json_t* jContainer, vector<channel_t>* channels)
+void writeChannels_(json_t* j)
 {
-       json_t* jChannels = json_array();
-       for (unsigned i=0; i<channels->size(); i++) {
-               json_t*   jChannel = json_object();
-               channel_t channel  = channels->at(i);
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE,                 json_integer(channel.type));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX,                json_integer(channel.index));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SIZE,                 json_integer(channel.size));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_NAME,                 json_string(channel.name.c_str()));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN,               json_integer(channel.column));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE,                 json_integer(channel.mute));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_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,                  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_VELO_AS_VOL,  json_boolean(channel.midiInVeloAsVol));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS,     json_integer(channel.midiInKeyPress));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL,       json_integer(channel.midiInKeyRel));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL,         json_integer(channel.midiInKill));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM,          json_integer(channel.midiInArm));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME,       json_integer(channel.midiInVolume));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE,         json_integer(channel.midiInMute));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER,       json_integer(channel.midiInFilter));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO,         json_integer(channel.midiInSolo));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L,           json_boolean(channel.midiOutL));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING,   json_integer(channel.midiOutLplaying));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE,      json_integer(channel.midiOutLmute));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO,      json_integer(channel.midiOutLsolo));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH,          json_string(channel.samplePath.c_str()));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_KEY,                  json_integer(channel.key));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MODE,                 json_integer(channel.mode));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BEGIN,                json_integer(channel.begin));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_END,                  json_integer(channel.end));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BOOST,                json_real(channel.boost));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_READ_ACTIONS,         json_integer(channel.readActions));
-               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));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN,        json_integer(channel.midiOutChan));
-               json_object_set_new(jChannel, PATCH_KEY_CHANNEL_ARMED,                json_boolean(channel.armed));
-               json_array_append_new(jChannels, jChannel);
-
-               writeActions(jChannel, channel.actions);
+       model::ChannelsLock l(model::channels);
+
+       json_t* jcs = json_array();
+
+       for (m::Channel* c : model::channels) {
+
+               json_t* jc = json_object();
+
+               json_object_set_new(jc, PATCH_KEY_CHANNEL_ID,     json_integer(c->id));
+               json_object_set_new(jc, PATCH_KEY_CHANNEL_TYPE,   json_integer(static_cast<int>(c->type)));
+               json_object_set_new(jc, PATCH_KEY_CHANNEL_VOLUME, json_real(c->volume));
+
+               if (c->type != ChannelType::MASTER) {
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_SIZE,               json_integer(G_MainWin->keyboard->getChannel(c->id)->getSize()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_NAME,               json_string(c->name.c_str()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_COLUMN,             json_integer(c->columnId));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MUTE,               json_integer(c->mute));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_SOLO,               json_integer(c->solo));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_PAN,                json_real(c->pan));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_ARMED,              json_boolean(c->armed));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_HAS_ACTIONS,        json_boolean(c->hasActions));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN,            json_boolean(c->midiIn.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL,     json_integer(c->midiInKeyRel.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS,   json_integer(c->midiInKeyPress.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_KILL,       json_integer(c->midiInKill.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_ARM,        json_integer(c->midiInArm.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME,     json_integer(c->midiInVolume.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_MUTE,       json_integer(c->midiInMute.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_SOLO,       json_integer(c->midiInSolo.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_FILTER,     json_integer(c->midiInFilter.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT_L,         json_boolean(c->midiOutL.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, json_integer(c->midiOutLplaying.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE,    json_integer(c->midiOutLmute.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO,    json_integer(c->midiOutLsolo.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_KEY,                json_integer(c->key));
+               }       
 
 #ifdef WITH_VST
+               json_t* jplugins = json_array();
+               for (ID pid : c->pluginIds)
+                       json_array_append_new(jplugins, json_integer(pid));
+               json_object_set_new(jc, PATCH_KEY_CHANNEL_PLUGINS, jplugins);
+#endif
 
-               writePlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+               if (c->type == ChannelType::SAMPLE) {
+                       SampleChannel* sc = static_cast<SampleChannel*>(c);
+
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_WAVE_ID,              json_integer(sc->waveId));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MODE,                 json_integer(static_cast<int>(sc->mode)));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_BEGIN,                json_integer(sc->begin));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_END,                  json_integer(sc->end));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_READ_ACTIONS,         json_boolean(sc->readActions));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_PITCH,                json_real(sc->pitch));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_INPUT_MONITOR,        json_boolean(sc->inputMonitor));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL,  json_boolean(sc->midiInVeloAsVol));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, json_integer(sc->midiInReadActions.load()));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_IN_PITCH,        json_integer(sc->midiInPitch.load()));
+               }
+               else
+               if (c->type == ChannelType::MIDI) {
+                       MidiChannel* mc = static_cast<MidiChannel*>(c);
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT,      json_integer(mc->midiOut));
+                       json_object_set_new(jc, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, json_integer(mc->midiOutChan));
+               }
 
-#endif
+               json_array_append_new(jcs, jc);
        }
-       json_object_set_new(jContainer, PATCH_KEY_CHANNELS, jChannels);
+       json_object_set_new(j, PATCH_KEY_CHANNELS, jcs);
 }
-
 }; // {anonymous}
 
 
@@ -502,29 +569,28 @@ void writeChannels(json_t* jContainer, vector<channel_t>* channels)
 /* -------------------------------------------------------------------------- */
 
 
-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;
+int         samplerate;
+int         lastTakeId;
+bool        metronome;
+
+
+/* -------------------------------------------------------------------------- */
 
-#ifdef WITH_VST
-std::vector<plugin_t> masterInPlugins;
-std::vector<plugin_t> masterOutPlugins;
-#endif
+
+bool Version::operator ==(const Version& o) const
+{
+       return major == o.major && minor == o.minor && patch == o.patch;        
+}
+
+
+bool Version::operator <(const Version& o) const
+{
+       if (major < o.major) return true;
+       if (minor < o.minor) return true;
+       if (patch < o.patch) return true;
+       return false;
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -532,13 +598,6 @@ std::vector<plugin_t> masterOutPlugins;
 
 void init()
 {
-       columns.clear();
-       channels.clear();
-#ifdef WITH_VST
-       masterInPlugins.clear();
-       masterOutPlugins.clear();
-#endif
-       header     = "GIADAPTC";
        lastTakeId = 0;
        samplerate = G_DEFAULT_SAMPLERATE;
 }
@@ -547,61 +606,78 @@ void init()
 /* -------------------------------------------------------------------------- */
 
 
-int write(const string& file)
+int verify(const std::string& file)
+{
+       namespace uj = u::json;
+
+       json_t* j = uj::load(file);
+       if (j == nullptr)
+               return G_PATCH_UNREADABLE;
+
+       if (uj::readString(j, PATCH_KEY_HEADER) != "GIADAPTC")
+               return G_PATCH_INVALID;
+       
+       Version version = {
+               static_cast<int>(uj::readInt(j, PATCH_KEY_VERSION_MAJOR)),
+               static_cast<int>(uj::readInt(j, PATCH_KEY_VERSION_MINOR)),
+               static_cast<int>(uj::readInt(j, PATCH_KEY_VERSION_PATCH))
+       };
+       if (version < Version{0, 16, 0})
+               return G_PATCH_UNSUPPORTED;
+       
+       return G_PATCH_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool write(const std::string& name, const std::string& file, bool isProject)
 {
-       json_t* jRoot = json_object();
+       json_t* j = json_object();
 
-       writeCommons(jRoot);
-       writeColumns(jRoot, &columns);
-       writeChannels(jRoot, &channels);
+       writeCommons_(j, name);
+       writeColumns_(j);
+       writeChannels_(j);
+       writeActions_(j);
+       writeWaves_(j, isProject);
 #ifdef WITH_VST
-       writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
-       writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
+       writePlugins_(j);
 #endif
 
-       if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
-               gu_log("[patch::write] unable to write patch file!\n");
-               return 0;
+       if (json_dump_file(j, file.c_str(), JSON_COMPACT) != 0) {
+               u::log::print("[patch::write] unable to write patch file!\n");
+               return false;
        }
-       return 1;
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int read(const string& file)
+int read(const std::string& file, const std::string& basePath)
 {
-       json_error_t jError;
-       json_t* 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;
-       }
-
-       if (!storager::checkObject(jRoot, "root element"))
-               return PATCH_INVALID;
+       namespace uj = u::json;
 
+       json_t* j = uj::load(file);
+       if (j == nullptr) 
+               return G_PATCH_UNREADABLE;
+       
        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);
+       readCommons_(j);
+       readColumns_(j);
 #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);
+       readPlugins_(j);
 #endif
+       readWaves_(j, basePath);
+       readActions_(j);
+       readChannels_(j);
 
-       json_decref(jRoot);
+       json_decref(j);
 
-       sanitize();
-       modernize();
+       sanitize_();
 
-       return PATCH_READ_OK;
+       return G_PATCH_OK;
 }
-
-
 }}}; // giada::m::patch::
index 833ade2332e94b9e7e0e86cdb0e22524e72c88e7..e741c27cd15c9f4d9919b874c9874d2490f9056e 100644 (file)
 #include <string>
 #include <vector>
 #include <cstdint>
+#include "core/types.h"
+#include "core/const.h"
 
 
 namespace giada {
 namespace m {
 namespace patch
 {
-struct action_t
+struct Version
 {
-       int      id;
-       int      channel;
-       int      frame;
+       int major = G_VERSION_MAJOR;
+       int minor = G_VERSION_MINOR;
+       int patch = G_VERSION_PATCH;
+
+       bool operator ==(const Version& o) const;
+       bool operator <(const Version& o) const;
+};
+
+
+struct Action
+{
+       ID       id;
+       ID       channelId;
+       Frame    frame;
        uint32_t event;
-       int      prev;
-       int      next;
+       ID       prevId;
+       ID       nextId;
 };
 
+
 #ifdef WITH_VST
-struct plugin_t
+struct Plugin
 {
+       ID                    id;
        std::string           path;
        bool                  bypass;
        std::vector<float>    params;
@@ -58,19 +73,28 @@ struct plugin_t
 };
 #endif
 
-struct channel_t
+
+struct Wave
 {
-       int         type;
-       int         index;
+       ID          id;
+       std::string path;
+};
+
+
+struct Channel
+{
+       ID          id;
+       ChannelType type;
        int         size;
        std::string name;
-       int         column;
-       int         mute;
-       int         solo;
-       float       volume;
+       ID          columnId;
+       int         key;
+       bool        mute;
+       bool        solo;
+       float       volume = G_DEFAULT_VOL;
        float       pan;
+       bool        hasActions;
        bool        midiIn;
-       bool        midiInVeloAsVol;
        uint32_t    midiInKeyPress;
        uint32_t    midiInKeyRel;
        uint32_t    midiInKill;
@@ -85,69 +109,51 @@ struct channel_t
        uint32_t    midiOutLsolo;
        bool        armed;
        // sample channel
-       std::string samplePath;
-       int         key;
-       int         mode;
-       int         begin;
-       int         end;
-       float       boost;
-       int         readActions; // TODO - should be bool
-       float       pitch;
+       ID          waveId;
+       ChannelMode mode;
+       Frame       begin;
+       Frame       end;
+       // TODO - shift
+       bool        readActions;
+       float       pitch = G_DEFAULT_PITCH;
        bool        inputMonitor;
+       bool        midiInVeloAsVol;
        uint32_t    midiInReadActions;
        uint32_t    midiInPitch;
        // midi channel
-       int         midiOut;   // TODO - should be bool
+       bool        midiOut;
        int         midiOutChan;
-
-       std::vector<action_t> actions;
-
 #ifdef WITH_VST
-       std::vector<plugin_t> plugins;
+       std::vector<ID> pluginIds;
 #endif
 };
 
-struct column_t
-{
-       int index;
-       int width;
-       std::vector<int> channels;
-};
-
-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         samplerate;   // Original samplerate when the patch was saved
 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;
-
-#ifdef WITH_VST
-extern std::vector<plugin_t> masterInPlugins;
-extern std::vector<plugin_t> masterOutPlugins;
-#endif
+extern bool        metronome;
 
 /* init
- * Init Patch with default values. */
+Initializes the patch with default values. */
 
 void init();
 
-/* read/write
- * Read/write patch to/from file. */
+/* verify
+Checks if the patch is valid. */
+
+int verify(const std::string& file);
+
+/* read
+Reads patch from file. Always call verify() first in order to see if the patch
+format is valid. It takes 'basePath' as parameter for Wave reading.*/
 
-int write(const std::string& file);
-int read (const std::string& file);
+int read(const std::string& file, const std::string& basePath);
+
+/* write
+Writes patch to file. */
+
+bool write(const std::string& name, const std::string& file, bool isProject);
 }}};  // giada::m::patch::
 
+
 #endif
index 32e9b888d57fde04417514812b039a4e2a003705..5bb9a1749cb52eab9177391a61c53bfe9fd91429 100644 (file)
@@ -30,8 +30,8 @@
 
 #include <cassert>
 #include <FL/Fl.H>
-#include "../utils/log.h"
-#include "../utils/time.h"
+#include "utils/log.h"
+#include "utils/time.h"
 #include "const.h"
 #include "plugin.h"
 
@@ -42,26 +42,29 @@ using std::string;
 namespace giada {
 namespace m 
 {
-int Plugin::m_idGenerator = 1;
+Plugin::Plugin(ID id, const std::string& UID)
+: id      (id),
+  valid   (false),
+  m_plugin(nullptr),
+  m_UID   (UID)
+{
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, int buffersize)
-: m_ui    (nullptr),
+Plugin::Plugin(ID id, juce::AudioPluginInstance* plugin, double samplerate,
+       int buffersize)
+: id      (id),
+  valid   (true),
   m_plugin(plugin),
-  m_id    (m_idGenerator++),
   m_bypass(false)
 {
-       using namespace juce;
-       
        /* Init midiInParams. All values are empty (0x0): they will be filled during
        midi learning process. */
 
-       const OwnedArray<AudioProcessorParameter>& params = m_plugin->getParameters();
-       for (int i=0; i<params.size(); i++)
-               midiInParams.push_back(0x0);
+       midiInParams = std::deque<std::atomic<uint32_t>>(m_plugin->getParameters().size());
        
        m_buffer.setSize(G_MAX_IO_CHANS, buffersize);
 
@@ -75,7 +78,7 @@ Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, int buffers
 
        m_plugin->prepareToPlay(samplerate, buffersize);
 
-       gu_log("[Plugin] plugin initialized and ready. MIDI input params: %lu\n", 
+       u::log::print("[Plugin] plugin initialized and ready. MIDI input params: %lu\n", 
                midiInParams.size());
 }
 
@@ -83,9 +86,24 @@ Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, int buffers
 /* -------------------------------------------------------------------------- */
 
 
+Plugin::Plugin(const Plugin& o)
+: id      (o.id),
+  valid   (true),
+  m_plugin(o.m_plugin),
+  m_bypass(o.m_bypass.load())
+{
+       for (const std::atomic<uint32_t>& p : o.midiInParams)
+               midiInParams.emplace_back(p.load());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 Plugin::~Plugin()
 {
-       closeEditor();
+       if (!valid)
+               return;
        m_plugin->suspendProcessing(true);
        m_plugin->releaseResources();
 }
@@ -115,24 +133,9 @@ int Plugin::countMainOutChannels() const
 /* -------------------------------------------------------------------------- */
 
 
-void Plugin::showEditor(void* parent)
+juce::AudioProcessorEditor* Plugin::createEditor() const
 {
-       m_ui = m_plugin->createEditorIfNeeded();
-       if (m_ui == nullptr) {
-               gu_log("[Plugin::showEditor] unable to create editor!\n");
-               return;
-       }
-       m_ui->setOpaque(true);
-       m_ui->addToDesktop(0, parent);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Plugin::isEditorOpen() const
-{
-       return m_ui != nullptr && m_ui->isVisible() && m_ui->isOnDesktop();
+       return m_plugin->createEditorIfNeeded();
 }
 
 
@@ -141,6 +144,8 @@ bool Plugin::isEditorOpen() const
 
 string Plugin::getUniqueId() const
 {
+       if (!valid)
+               return m_UID;
        return m_plugin->getPluginDescription().createIdentifierString().toStdString();
 }
 
@@ -150,7 +155,7 @@ string Plugin::getUniqueId() const
 
 int Plugin::getNumParameters() const
 {
-       return m_plugin->getParameters().size();
+       return valid ? m_plugin->getParameters().size() : 0;
 }
 
 
@@ -202,22 +207,8 @@ bool Plugin::acceptsMidi() const
 /* -------------------------------------------------------------------------- */
 
 
-bool Plugin::isBypassed() const { return m_bypass; }
-void Plugin::toggleBypass() { m_bypass = !m_bypass; }
-void Plugin::setBypass(bool b) { m_bypass = b; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Plugin::getId() const { return m_id; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Plugin::getEditorW() const { assert(m_ui != nullptr); return m_ui->getWidth(); }
-int Plugin::getEditorH() const { assert(m_ui != nullptr); return m_ui->getHeight(); }
+bool Plugin::isBypassed() const { return m_bypass.load(); }
+void Plugin::setBypass(bool b) { m_bypass.store(b); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -306,7 +297,8 @@ string Plugin::getProgramName(int index) const
 
 string Plugin::getParameterName(int index) const
 {
-       return m_plugin->getParameters()[index]->getName(MAX_LABEL_SIZE).toStdString();
+       const int labelSize = 64;
+       return m_plugin->getParameters()[index]->getName(labelSize).toStdString();
 }
 
 
@@ -327,16 +319,6 @@ string Plugin::getParameterLabel(int index) const
        return m_plugin->getParameters()[index]->getLabel().toStdString();
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-void Plugin::closeEditor()
-{
-       delete m_ui;
-       m_ui = nullptr;
-}
-
 }} // giada::m::
 
 
index a7cca047a7ffcbc2cc152a903afad386e2c1c719..93db880828b1ea8624eba7dbd6fd6478800be5e0 100644 (file)
 #define G_PLUGIN_H
 
 
-#include "../deps/juce-config.h"
+#include <deque>
+#include "deps/juce-config.h"
+#include "pluginHost.h"
+#include "const.h"
 
 
 namespace giada {
@@ -41,16 +44,16 @@ class Plugin
 {
 public:
 
-       Plugin(juce::AudioPluginInstance* p, double samplerate, int buffersize);
+       Plugin(ID id, const std::string& UID);
+       Plugin(ID id, juce::AudioPluginInstance* p, double samplerate, int buffersize);
+       Plugin(const Plugin& o);
        ~Plugin();
 
        /* getUniqueId
        Returns a string-based UID. */
 
        std::string getUniqueId() const;
-
        std::string getName() const;
-       bool isEditorOpen() const;
        bool hasEditor() const;
        int getNumParameters() const;
        float getParameter(int index) const;
@@ -62,13 +65,12 @@ public:
        int getNumPrograms() const;
        int getCurrentProgram() const;
        std::string getProgramName(int index) const;
-       int getId() const;
-       int getEditorW() const;
-       int getEditorH() const;
        void setParameter(int index, float value) const;
        void setCurrentProgram(int index) const;
        bool acceptsMidi() const;
 
+       juce::AudioProcessorEditor* createEditor() const;
+
        /* process
        Process the plug-in with audio and MIDI data. The audio buffer is a reference:
        it has to be altered by the plug-in itself. Conversely, the MIDI buffer must
@@ -78,35 +80,37 @@ public:
 
        void process(juce::AudioBuffer<float>& b, juce::MidiBuffer m);
 
-       void showEditor(void* parent);
-
-       /* closeEditor
-       Shuts down plugin GUI. */
+       void setBypass(bool b);
 
-       void closeEditor();
+       /* id
+       Unique identifier. */
 
-       void toggleBypass();
-       void setBypass(bool b);
+       ID id;
 
        /* midiInParams
-       A list of midiIn hex values for parameter automation. */
-
-       std::vector<uint32_t> midiInParams;
+       A list of midiIn hex values for parameter automation. Why not a vector? 
+       Unfortunately std::atomic types are not copy-constructible, nor 
+       copy-assignable: such type won't suit a std::vector. */
 
-private:
+       std::deque<std::atomic<uint32_t>> midiInParams;
+       
+       /* valid
+       A missing plug-in is loaded anyway, yet marked as 'invalid'. */
 
-       enum class BusType { IN = true, OUT = false };
+       bool valid;
 
-       static const int MAX_LABEL_SIZE = 64;
-       
-       static int m_idGenerator;
+private:
 
-       juce::AudioProcessorEditor* m_ui;     // gui
-       juce::AudioPluginInstance*  m_plugin; // core
-       juce::AudioBuffer<float>    m_buffer;
+#ifdef G_OS_WINDOWS
+       /* Fuck... */
+       #undef IN
+       #undef OUT
+#endif
 
-       int m_id;
-       bool m_bypass;
+       enum class BusType 
+       { 
+               IN = true, OUT = false 
+       };
 
        juce::AudioProcessor::Bus* getMainBus(BusType b) const;
 
@@ -114,8 +118,17 @@ private:
        Returns the current channel layout for the main output bus. */
 
        int countMainOutChannels() const;
-};
 
+       juce::AudioPluginInstance* m_plugin;
+       juce::AudioBuffer<float>   m_buffer;
+
+       std::atomic<bool> m_bypass;
+
+       /* UID
+       The original UID, used for missing plugins. */
+
+       std::string m_UID;
+};
 }} // giada::m::
 
 #endif
index 98f09d8db3c6e9986c06d321f6d8c64adddc459e..f072097015093eb152f9652b8f3a4036da4607ae 100644 (file)
 
 #ifdef WITH_VST
 
-
 #include <cassert>
-#include "../utils/log.h"
-#include "../utils/vector.h"
-#include "const.h"
-#include "channel.h"
-#include "plugin.h"
-#include "pluginHost.h"
+#include "utils/log.h"
+#include "utils/vector.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/const.h"
+#include "core/plugin.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
 
 
 namespace giada {
@@ -45,65 +46,73 @@ namespace
 {
 juce::MessageManager* messageManager_;
 juce::AudioBuffer<float> audioBuffer_;
-
-std::vector<std::unique_ptr<Plugin>> masterOut_;
-std::vector<std::unique_ptr<Plugin>> masterIn_;
+ID pluginId_;
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void processPlugin_(Plugin& p, Channel* ch)
+void giadaToJuceTempBuf_(const AudioBuffer& outBuf)
 {
-       if (p.isSuspended() || p.isBypassed())
-               return;
+       for (int i=0; i<outBuf.countFrames(); i++)
+               for (int j=0; j<outBuf.countChannels(); j++)
+                       audioBuffer_.setSample(j, i, outBuf[i][j]);
+}
 
-       juce::MidiBuffer events;
-       if (ch != nullptr)
-               events = ch->getPluginMidiEvents();
 
-       p.process(audioBuffer_, events);
+/* juceToGiadaOutBuf_
+Converts buffer from Juce to Giada. A note for the future: if we overwrite (=) 
+(as we do now) it's SEND, if we add (+) it's INSERT. */
+
+void juceToGiadaOutBuf_(AudioBuffer& outBuf)
+{
+       for (int i=0; i<outBuf.countFrames(); i++)
+               for (int j=0; j<outBuf.countChannels(); j++)    
+                       outBuf[i][j] = audioBuffer_.getSample(j, i);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-/* getStack_
-Returns a vector of unique_ptr's given the stackType. If stackType == CHANNEL
-a pointer to Channel is also required. */
 
-std::vector<std::unique_ptr<Plugin>>& getStack_(StackType t, Channel* ch=nullptr)
+void processPlugins_(const std::vector<ID>& pluginIds, juce::MidiBuffer& events)
 {
-       switch(t) {
-               case StackType::MASTER_OUT:
-                       return masterOut_;
-               case StackType::MASTER_IN:
-                       return masterIn_;
-               case StackType::CHANNEL:
-                       return ch->plugins;
-               default:
-                       assert(false);
+       model::PluginsLock l(model::plugins);
+
+       for (ID id : pluginIds) {
+               Plugin& p = model::get(model::plugins, id);
+               if (!p.valid || p.isSuspended() || p.isBypassed())
+                       continue;
+               p.process(audioBuffer_, events);
+               events.clear();
        }
 }
 
-}; // {anonymous}
 
+ID clonePlugin_(ID pluginId)
+{
+       model::PluginsLock l(model::plugins);
 
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
+       const Plugin&           original = model::get(model::plugins, pluginId);
+       std::unique_ptr<Plugin> clone    = pluginManager::makePlugin(original);
+       ID                      newId    = clone->id;
 
+       model::plugins.push(std::move(clone));
 
-pthread_mutex_t mutex;
+       return newId;
+}
+}; // {anonymous}
 
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
 void close()
 {
        messageManager_->deleteInstance();
-       pthread_mutex_destroy(&mutex);
+       model::plugins.clear();
 }
 
 
@@ -114,143 +123,108 @@ void init(int buffersize)
 {
        messageManager_ = juce::MessageManager::getInstance();
        audioBuffer_.setSize(G_MAX_IO_CHANS, buffersize);
-
-       pthread_mutex_init(&mutex, nullptr);
+       pluginId_ = 0;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void addPlugin(std::unique_ptr<Plugin> p, StackType t, pthread_mutex_t* mixerMutex
-       Channel* ch)
+void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds
+       juce::MidiBuffer* events)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
+       assert(outBuf.countFrames() == audioBuffer_.getNumSamples());
 
-       gu_log("[pluginHost::addPlugin] load plugin (%s), stack type=%d, stack size=%d\n",
-               p->getName().c_str(), t, stack.size());
+       /* If events are null: Audio stack processing (master in, master out or
+       sample channels. No need for MIDI events. 
+       If events are not null: MIDI stack (MIDI channels). MIDI channels must not 
+       process the current buffer: give them an empty and clean one. */
+       
+       if (events == nullptr) {
+               giadaToJuceTempBuf_(outBuf);
+               juce::MidiBuffer events; // empty
+               processPlugins_(pluginIds, events);
+       }
+       else {
+               audioBuffer_.clear();
+               processPlugins_(pluginIds, *events);
 
-       pthread_mutex_lock(mixerMutex);
-       stack.push_back(std::move(p));
-       pthread_mutex_unlock(mixerMutex);
+       }
+       juceToGiadaOutBuf_(outBuf);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-std::vector<Plugin*> getStack(StackType t, Channel* ch)
+void addPlugin(std::unique_ptr<Plugin> p, ID channelId)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-
-       std::vector<Plugin*> out;
-       for (const std::unique_ptr<Plugin>& p : stack)
-               out.push_back(p.get());
+       ID pluginId = p->id;
+       
+       model::plugins.push(std::move(p));
 
-       return out;
+       model::onSwap(model::channels, channelId, [&](Channel& c)
+       {
+               c.pluginIds.push_back(pluginId);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int countPlugins(StackType t, Channel* ch)
+void swapPlugin(ID pluginId1, ID pluginId2, ID channelId)
 {
-       return getStack_(t, ch).size();
+       model::onSwap(model::channels, channelId, [&](Channel& c)
+       {
+               auto a = u::vector::indexOf(c.pluginIds, pluginId1); 
+               auto b = u::vector::indexOf(c.pluginIds, pluginId2); 
+       
+               std::swap(c.pluginIds.at(a), c.pluginIds.at(b));
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void freeStack(StackType t, pthread_mutex_t* mixerMutex, Channel* ch)
+void freePlugin(ID pluginId, ID channelId)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-
-       if (stack.size() == 0)
-               return;
-
-       pthread_mutex_lock(mixerMutex);
-       stack.clear();
-       pthread_mutex_unlock(mixerMutex);
+       model::onSwap(model::channels, channelId, [&](Channel& c)
+       {
+               u::vector::remove(c.pluginIds, pluginId);
+       });
 
-       gu_log("[pluginHost::freeStack] stack type=%d freed\n", t);
+       model::plugins.pop(model::getIndex(model::plugins, pluginId));
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void processStack(AudioBuffer& outBuf, StackType t, Channel* ch)
+void freePlugins(const std::vector<ID>& pluginIds)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-
-       if (stack.size() == 0)
-               return;
-
-       assert(outBuf.countFrames() == audioBuffer_.getNumSamples());
-
-       /* MIDI channels must not process the current buffer: give them an empty one. 
-       Sample channels and Master in/out want audio data instead: let's convert the 
-       internal buffer from Giada to Juce. */
-
-       if (ch != nullptr && ch->type == ChannelType::MIDI) 
-               audioBuffer_.clear();
-       else
-               for (int i=0; i<outBuf.countFrames(); i++)
-                       for (int j=0; j<outBuf.countChannels(); j++)
-                               audioBuffer_.setSample(j, i, outBuf[i][j]);
-
-       /* Hardcore processing. Part of this loop must be guarded by mutexes, i.e. 
-       the MIDI process part. You definitely don't want a situation like the 
-       following one:
-               1. this::processStack()
-               2. [a new midi event comes in from kernelMidi thread]
-               3. channel::clearMidiBuffer()
-       The midi event in between would be surely lost, deleted by the last call to
-       channel::clearMidiBuffer()! 
-       TODO - that's why we need a proper queue for MIDI events in input... */
-
-       if (ch != nullptr)
-               pthread_mutex_lock(&mutex);
-
-       for (std::unique_ptr<Plugin>& plugin : stack)
-               processPlugin_(*plugin.get(), ch);
-
-       if (ch != nullptr) {
-               ch->clearMidiBuffer();
-               pthread_mutex_unlock(&mutex);
-       }
-
-       /* Converting buffer from Juce to Giada. A note for the future: if we 
-       overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */
-
-       for (int i=0; i<outBuf.countFrames(); i++)
-               for (int j=0; j<outBuf.countChannels(); j++)    
-                       outBuf[i][j] = audioBuffer_.getSample(j, i);
+       for (ID id : pluginIds)
+               model::plugins.pop(model::getIndex(model::plugins, id));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-Plugin* getPluginByIndex(int index, StackType t, Channel* ch)
+void clonePlugins(const Channel& oldChannel, Channel& newChannel)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-       assert((size_t) index < stack.size());
-       return stack.at(index).get();
+       newChannel.pluginIds.clear();
+       for (ID id : oldChannel.pluginIds)
+               newChannel.pluginIds.push_back(clonePlugin_(id));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int getPluginIndex(int id, StackType t, Channel* ch)
+void setPluginParameter(ID pluginId, int paramIndex, float value)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-       return u::vector::indexOf(stack, [&](const std::unique_ptr<Plugin>& p) 
-       { 
-               return p->getId() == id;
+       model::onGet(model::plugins, pluginId, [&](Plugin& p)
+       {
+               p.setParameter(paramIndex, value);
        });
 }
 
@@ -258,38 +232,24 @@ int getPluginIndex(int id, StackType t, Channel* ch)
 /* -------------------------------------------------------------------------- */
 
 
-void swapPlugin(int indexA, int indexB, StackType t, pthread_mutex_t* mixerMutex, 
-       Channel* ch)
+void setPluginProgram(ID pluginId, int programIndex)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-
-       pthread_mutex_lock(mixerMutex);
-       std::swap(stack.at(indexA), stack.at(indexB));
-       pthread_mutex_unlock(mixerMutex);
-
-       gu_log("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB);
+       model::onGet(model::plugins, pluginId, [&](Plugin& p)
+       {
+               p.setCurrentProgram(programIndex);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int freePlugin(int id, StackType t, pthread_mutex_t* mixerMutex, Channel* ch)
+void toggleBypass(ID pluginId)
 {
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, ch);
-
-       int index = u::vector::indexOf(stack, [&](const std::unique_ptr<Plugin>& p) 
-       { 
-               return p->getId() == id; 
+       model::onGet(model::plugins, pluginId, [&](Plugin& p)
+       {
+               p.setBypass(!p.isBypassed());
        });
-       assert(index != -1);
-
-       pthread_mutex_lock(mixerMutex);
-       stack.erase(stack.begin() + index);
-       pthread_mutex_unlock(mixerMutex);       
-
-       gu_log("[pluginHost::freePlugin] plugin id=%d removed\n", id);
-       return index;
 }
 
 
@@ -301,29 +261,6 @@ void runDispatchLoop()
        messageManager_->runDispatchLoopUntil(10);
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-void freeAllStacks(std::vector<Channel*>* channels, pthread_mutex_t* mixerMutex)
-{
-       freeStack(StackType::MASTER_OUT, mixerMutex);
-       freeStack(StackType::MASTER_IN, mixerMutex);
-       for (Channel* c : *channels)
-               freeStack(StackType::CHANNEL, mixerMutex, c);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void forEachPlugin(StackType t, const Channel* ch, std::function<void(const Plugin* p)> f)
-{
-       std::vector<std::unique_ptr<Plugin>>& stack = getStack_(t, const_cast<Channel*>(ch));
-       for (const std::unique_ptr<Plugin>& p : stack)
-               f(p.get());
-}
-
 }}}; // giada::m::pluginHost::
 
 
index 4852d4ac3c892560b894dc7f9db01e75a60a867c..b5c8ce12ceb992ea5d9133bb3030666d453707ca 100644 (file)
@@ -33,8 +33,8 @@
 
 
 #include <functional>
-#include <pthread.h>
-#include "../deps/juce-config.h"
+#include "deps/juce-config.h"
+#include "core/types.h"
 
 
 namespace giada {
@@ -46,70 +46,52 @@ class AudioBuffer;
 
 namespace pluginHost
 {
-enum class StackType { MASTER_OUT, MASTER_IN, CHANNEL };
-
-extern pthread_mutex_t mutex;
+using Stack = std::vector<std::shared_ptr<Plugin>>;
 
 void init(int buffersize);
 void close();
 
 /* addPlugin
-Adds a new plugin to 'stackType'. */
-
-void addPlugin(std::unique_ptr<Plugin> p, StackType t, pthread_mutex_t* mutex, 
-    Channel* ch=nullptr);
-
-/* countPlugins
-Returns the size of 'stackType'. */
+Adds a new plugin to channel 'channelId'. */
 
-int countPlugins(StackType t, Channel* ch=nullptr);
-
-/* freeStack
-Frees plugin stack of type 'stackType'. */
-
-void freeStack(StackType t, pthread_mutex_t* mutex, Channel* ch=nullptr);
+void addPlugin(std::unique_ptr<Plugin> p, ID channelId);
 
 /* processStack
 Applies the fx list to the buffer. */
 
-void processStack(AudioBuffer& outBuf, StackType t, Channel* ch=nullptr);
+void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds, 
+       juce::MidiBuffer* events=nullptr);
+
+/* swapPlugin 
+Swaps plug-in with ID 1 with plug-in with ID 2 in Channel 'channelId'. */
 
-/* getStack
-Returns a vector of Plugin pointers given the stackType. If stackType == CHANNEL
-a pointer to Channel is also required. */
+void swapPlugin(ID pluginId1, ID pluginId2, ID channelId);
 
-std::vector<Plugin*> getStack(StackType t, Channel* ch=nullptr);
+/* freePlugin.
+Unloads plugin from channel 'channelId'. */
 
-/* getPluginByIndex */
+void freePlugin(ID pluginId, ID channelId);
 
-Plugin* getPluginByIndex(int index, StackType t, Channel* ch=nullptr);
+/* freePlugins
+Unloads multiple plugins. Useful when freeing or deleting a channel. */
 
-/* getPluginIndex */
+void freePlugins(const std::vector<ID>& pluginIds);
 
-int getPluginIndex(int id, StackType t, Channel* ch=nullptr);
+/* clonePlugins
+Clones all the plug-ins from the current channel to the new one. */
 
-/* swapPlugin */
+void clonePlugins(const Channel& oldChannel, Channel& newChannel);
 
-void swapPlugin(int indexA, int indexB, StackType t, pthread_mutex_t* mutex, 
-    Channel* ch=nullptr);
+void setPluginParameter(ID pluginId, int paramIndex, float value);
 
-/* freePlugin.
-Returns the internal stack index of the deleted plugin. */
+void setPluginProgram(ID pluginId, int programIndex); 
 
-int freePlugin(int id, StackType t, pthread_mutex_t* mutex, Channel* ch=nullptr);
+void toggleBypass(ID pluginId);
 
 /* runDispatchLoop
 Wakes up plugins' GUI manager for N milliseconds. */
 
 void runDispatchLoop();
-
-/* freeAllStacks
-Frees everything. */
-
-void freeAllStacks(std::vector<Channel*>* channels, pthread_mutex_t* mutex);
-
-void forEachPlugin(StackType t, const Channel* ch, std::function<void(const Plugin* p)> f);
-
 }}}; // giada::m::pluginHost::
 
 
index 4b6d1a1e4a78730e5bb32365a447a834d6825e5e..98302bcf55fd113a0df735c6234e7eba3bf278a9 100644 (file)
 
 
 #include <cassert>
-#include "../utils/log.h"
-#include "../utils/fs.h"
-#include "../utils/string.h"
-#include "const.h"
-#include "plugin.h"
+#include "utils/log.h"
+#include "utils/fs.h"
+#include "utils/string.h"
+#include "core/const.h"
+#include "core/idManager.h"
+#include "core/patch.h"
+#include "core/conf.h"
+#include "core/plugin.h"
 #include "pluginManager.h"
 
 
-using std::vector;
-using std::string;
-
-
 namespace giada {
 namespace m {
 namespace pluginManager
 {
 namespace
 {
+IdManager pluginId_;
+
 int samplerate_;
 int buffersize_;
 
@@ -63,21 +64,21 @@ juce::KnownPluginList knownPluginList_;
 /* unknownPluginList
 List of unrecognized plugins found in a patch. */
 
-vector<string> unknownPluginList_;
+std::vector<std::string> unknownPluginList_;
 
 /* missingPlugins
 If some plugins from any stack are missing. */
 
 bool missingPlugins_;
 
-vector<string> splitPluginDescription_(const string& descr)
+std::vector<std::string> splitPluginDescription_(const std::string& descr)
 {
        // input:  VST-mda-Ambience-18fae2d2-6d646141  string
        // output: [2-------------] [1-----] [0-----]  vector.size() == 3
        
-       vector<string> out;
+       std::vector<std::string> out;
 
-       string chunk = "";
+       std::string chunk = "";
        int count = 2;
        for (int i=descr.length()-1; i >= 0; i--) {
                if (descr[i] == '-' && count != 0) {
@@ -104,12 +105,12 @@ VSTs: their ID is based on the plug-in file location. E.g.:
 
 The following function simply drops the first hash code during comparison. */
 
-const juce::PluginDescription* findPluginDescription_(const string& id)
+const juce::PluginDescription* findPluginDescription_(const std::string& id)
 {
-       vector<string> idParts = splitPluginDescription_(id);
+       std::vector<std::string> idParts = splitPluginDescription_(id);
 
        for (const juce::PluginDescription* pd : knownPluginList_) {
-               vector<string> tmpIdParts = splitPluginDescription_(pd->createIdentifierString().toStdString());
+               std::vector<std::string> tmpIdParts = splitPluginDescription_(pd->createIdentifierString().toStdString());
                if (idParts[0] == tmpIdParts[0] && idParts[2] == tmpIdParts[2])
                        return pd;
        }
@@ -125,29 +126,31 @@ const juce::PluginDescription* findPluginDescription_(const string& id)
 
 void init(int samplerate, int buffersize)
 {
+       pluginId_       = IdManager();
        samplerate_     = samplerate;
     buffersize_     = buffersize;
        missingPlugins_ = false;
        unknownPluginList_.clear();
-       loadList(gu_getHomePath() + G_SLASH + "plugins.xml");
+       loadList(u::fs::getHomePath() + G_SLASH + "plugins.xml");
+       sortPlugins(static_cast<pluginManager::SortMethod>(conf::pluginSortMethod));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int scanDirs(const string& dirs, const std::function<void(float)>& cb)
+int scanDirs(const std::string& dirs, const std::function<void(float)>& cb)
 {
-       gu_log("[pluginManager::scanDir] requested directories: '%s'\n", dirs.c_str());
-       gu_log("[pluginManager::scanDir] current plugins: %d\n", knownPluginList_.getNumTypes());
+       u::log::print("[pluginManager::scanDir] requested directories: '%s'\n", dirs.c_str());
+       u::log::print("[pluginManager::scanDir] current plugins: %d\n", knownPluginList_.getNumTypes());
 
        knownPluginList_.clear();   // clear up previous plugins
 
-       vector<string> dirVec = u::string::split(dirs, ";");
+       std::vector<std::string> dirVec = u::string::split(dirs, ";");
 
        juce::VSTPluginFormat format;
        juce::FileSearchPath searchPath;
-       for (const string& dir : dirVec)
+       for (const std::string& dir : dirVec)
                searchPath.add(juce::File(dir));
 
        juce::PluginDirectoryScanner scanner(knownPluginList_, format, searchPath, 
@@ -155,11 +158,11 @@ int scanDirs(const string& dirs, const std::function<void(float)>& cb)
 
        juce::String name;
        while (scanner.scanNextFile(false, name)) {
-               gu_log("[pluginManager::scanDir]   scanning '%s'\n", name.toRawUTF8());
+               u::log::print("[pluginManager::scanDir]   scanning '%s'\n", name.toRawUTF8());
                cb(scanner.getProgress());
        }
 
-       gu_log("[pluginManager::scanDir] %d plugin(s) found\n", knownPluginList_.getNumTypes());
+       u::log::print("[pluginManager::scanDir] %d plugin(s) found\n", knownPluginList_.getNumTypes());
        return knownPluginList_.getNumTypes();
 }
 
@@ -167,11 +170,11 @@ int scanDirs(const string& dirs, const std::function<void(float)>& cb)
 /* -------------------------------------------------------------------------- */
 
 
-int saveList(const string& filepath)
+int saveList(const std::string& filepath)
 {
        int out = knownPluginList_.createXml()->writeToFile(juce::File(filepath), "");
        if (!out)
-               gu_log("[pluginManager::saveList] unable to save plugin list to %s\n", filepath.c_str());
+               u::log::print("[pluginManager::saveList] unable to save plugin list to %s\n", filepath.c_str());
        return out;
 }
 
@@ -179,48 +182,44 @@ int saveList(const string& filepath)
 /* -------------------------------------------------------------------------- */
 
 
-int loadList(const string& filepath)
+int loadList(const std::string& filepath)
 {
-       juce::XmlElement* elem = juce::XmlDocument::parse(juce::File(filepath));
-       if (elem != nullptr) {
-               knownPluginList_.recreateFromXml(*elem);
-               delete elem;
-               return 1;
-       }
-       return 0;
+       std::unique_ptr<juce::XmlElement> elem(juce::XmlDocument::parse(juce::File(filepath)));
+       if (elem == nullptr)
+               return 0;
+       knownPluginList_.recreateFromXml(*elem);
+       return 1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-std::unique_ptr<Plugin> makePlugin(const string& fid)
+std::unique_ptr<Plugin> makePlugin(const std::string& fid, ID id)
 {
-       /* Initialize plugin. The default mode uses getTypeForIdentifierString, 
-       falling back to  getTypeForFile (deprecated) for old patches (< 0.14.4). */
+       /* Plug-in ID generator is updated anyway, as we store Plugin objects also
+       if they are in an invalid state. */
+       
+       pluginId_.set(id);
 
        const juce::PluginDescription* pd = findPluginDescription_(fid);
        if (pd == nullptr) {
-               gu_log("[pluginManager::makePlugin] no plugin found with fid=%s! Trying with "
-                       "deprecated mode...\n", fid.c_str());
-               pd = knownPluginList_.getTypeForFile(fid);
-               if (pd == nullptr) {
-                       gu_log("[pluginManager::makePlugin] still nothing to do, returning unknown plugin\n");
-                       missingPlugins_ = true;
-                       unknownPluginList_.push_back(fid);
-                       return {};
-               }
+               u::log::print("[pluginManager::makePlugin] no plugin found with fid=%s!\n", fid.c_str());
+               missingPlugins_ = true;
+               unknownPluginList_.push_back(fid);
+               return std::make_unique<Plugin>(pluginId_.get(id), fid); // Invalid plug-in
        }
 
        juce::AudioPluginInstance* pi = pluginFormat_.createInstanceFromDescription(*pd, samplerate_, buffersize_);
-       if (!pi) {
-               gu_log("[pluginManager::makePlugin] unable to create instance with fid=%s!\n", fid.c_str());
+       if (pi == nullptr) {
+               u::log::print("[pluginManager::makePlugin] unable to create instance with fid=%s!\n", fid.c_str());
                missingPlugins_ = true;
-               return {};
+               unknownPluginList_.push_back(fid);
+               return std::make_unique<Plugin>(pluginId_.get(id), fid); // Invalid plug-in
        }
-       gu_log("[pluginManager::makePlugin] plugin instance with fid=%s created\n", fid.c_str());
+       u::log::print("[pluginManager::makePlugin] plugin instance with fid=%s created\n", fid.c_str());
 
-       return std::make_unique<Plugin>(pi, samplerate_, buffersize_);
+       return std::make_unique<Plugin>(pluginId_.get(id), pi, samplerate_, buffersize_);
 }
 
 
@@ -234,7 +233,7 @@ std::unique_ptr<Plugin> makePlugin(int index)
        if (pd == nullptr) 
                return {};
        
-       gu_log("[pluginManager::makePlugin] plugin found, uid=%s, name=%s...\n",
+       u::log::print("[pluginManager::makePlugin] plugin found, uid=%s, name=%s...\n",
                pd->createIdentifierString().toRawUTF8(), pd->name.toRawUTF8());
        
        return makePlugin(pd->createIdentifierString().toStdString());
@@ -259,6 +258,35 @@ std::unique_ptr<Plugin> makePlugin(const Plugin& src)
 /* -------------------------------------------------------------------------- */
 
 
+std::unique_ptr<Plugin> makePlugin(const patch::Plugin& p)
+{
+       std::unique_ptr<Plugin> plugin = makePlugin(p.path, p.id);
+       if (!plugin->valid)
+               return plugin; // Return invalid version
+       
+       /* Fill plug-in parameters. */
+
+       plugin->setBypass(p.bypass);
+       for (unsigned j=0; j<p.params.size(); j++)
+               plugin->setParameter(j, p.params.at(j));
+
+       /* Fill plug-in MidiIn parameters. Don't fill Channel::midiInParam if 
+       Plugin::midiInParams are zero: it would wipe out the current default 0x0
+       values. */
+       
+       if (!p.midiInParams.empty()) {
+               plugin->midiInParams.clear();
+               for (uint32_t midiInParam : p.midiInParams)
+                       plugin->midiInParams.emplace_back(midiInParam);
+       }
+
+       return plugin;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 int countAvailablePlugins()
 {
        return knownPluginList_.getNumTypes();
@@ -303,7 +331,7 @@ bool hasMissingPlugins()
 /* -------------------------------------------------------------------------- */
 
 
-string getUnknownPluginInfo(int i)
+std::string getUnknownPluginInfo(int i)
 {
        return unknownPluginList_.at(i);
 }
@@ -312,7 +340,7 @@ string getUnknownPluginInfo(int i)
 /* -------------------------------------------------------------------------- */
 
 
-bool doesPluginExist(const string& fid)
+bool doesPluginExist(const std::string& fid)
 {
        return pluginFormat_.doesPluginStillExist(*knownPluginList_.getTypeForFile(fid));
 }
@@ -338,7 +366,6 @@ void sortPlugins(SortMethod method)
                        break;
        }
 }
-
 }}}; // giada::m::pluginManager::
 
 
index 1e345b18f53c3c511a99545fe950ee962e5a369c..d5c2afdc3d41ac7ed88f378ae8e8c33a46137e46 100644 (file)
 #define G_PLUGIN_MANAGER_H
 
 
-#include "../deps/juce-config.h"
+#include "deps/juce-config.h"
 #include "plugin.h"
 
 
 namespace giada {
 namespace m
 {
+namespace patch
+{
+struct Plugin;
+}
 namespace pluginManager
 {
 enum class SortMethod : int
@@ -80,9 +84,10 @@ Returns how many plug-ins are in a unknown/not-found state. */
 
 unsigned countUnknownPlugins();
 
-std::unique_ptr<Plugin> makePlugin(const std::string& fid);
+std::unique_ptr<Plugin> makePlugin(const std::string& fid, ID id=0);
 std::unique_ptr<Plugin> makePlugin(int index);
 std::unique_ptr<Plugin> makePlugin(const Plugin& other);
+std::unique_ptr<Plugin> makePlugin(const patch::Plugin& p);
 
 /* getAvailablePluginInfo
 Returns the available plugin information (name, type, ...) given a plug-in
diff --git a/src/core/queue.h b/src/core/queue.h
new file mode 100644 (file)
index 0000000..d3f54d5
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+
+#include <array>
+#include <atomic>
+
+
+namespace giada {
+namespace m
+{
+template<typename T, size_t size>
+class Queue
+{
+public:
+
+    Queue() : m_head(0), m_tail(0)
+    {
+    }
+
+
+    Queue(const Queue&) = delete;
+
+
+    bool pop(T& item)
+    {
+        size_t curr = m_head.load();
+        if (curr == m_tail.load())  // Queue empty, nothing to do
+            return false;
+
+        item = m_data[curr];
+        m_head.store(increment(curr));
+        return true;
+    }
+
+
+    bool push(const T& item)
+    {
+        size_t curr = m_tail.load();
+        size_t next = increment(curr);
+
+        if (next == m_head.load()) // Queue full, nothing to do
+            return false;
+
+        m_data[curr] = item;
+        m_tail.store(next);
+        return true;
+    }
+
+private:
+
+    size_t increment(size_t i) const
+    {
+        return (i + 1) % size;
+    }
+
+
+    std::array<T, size> m_data;
+    std::atomic<size_t> m_head;
+    std::atomic<size_t> m_tail;
+};
+}} // giada::m::
+
+
+#endif
diff --git a/src/core/rcuList.h b/src/core/rcuList.h
new file mode 100644 (file)
index 0000000..4abf459
--- /dev/null
@@ -0,0 +1,512 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_RCU_LIST_H
+#define G_RCU_LIST_H
+
+
+#include <array>
+#include <cassert>
+#include <thread>
+#include <atomic>
+#include <iterator>
+
+
+namespace giada {
+namespace m
+{
+template<typename T>
+class RCUList
+{
+public:
+
+       /* Lock
+       Scoped lock structure. */
+
+       struct Lock
+       {
+               Lock(RCUList<T>& r) : rcu(r) 
+               { 
+                       rcu.lock(); 
+               }
+
+               ~Lock() 
+               { 
+                       rcu.unlock();
+               }
+
+               RCUList<T>& rcu;
+       };
+
+       /* Node
+       Element of the linked list. */
+       
+       struct Node
+       {
+               std::unique_ptr<T> data;
+               std::atomic<Node*> next;
+
+               Node(std::unique_ptr<T> data, Node* next=nullptr)
+               : data(std::move(data)), 
+                 next(next) 
+               {}
+       };
+
+       /* Iterator (const)
+       This is based on simple, non-atomic pointers: you must always lock the RCU
+       list before looping over it! */
+
+       class Iterator : public std::iterator<std::forward_iterator_tag, Node*>
+       {
+       public:
+
+               Iterator(Node* n) : m_curr(n) {}
+
+               bool operator!= (const Iterator& o) const
+               {
+                       return m_curr != o.m_curr;
+               }
+
+               bool operator== (const Iterator& o) const
+               {
+                       return m_curr == o.m_curr;
+               }
+
+               const T* operator* () const
+               {
+                       return m_curr->data.get();
+               }
+
+               // TODO - this non-const will go away with the non-virtual Channel
+               // refactoring. 
+               T* operator* ()
+               {
+                       return m_curr->data.get();
+               }
+
+               const Iterator& operator++ ()  // Prefix operator (++x)
+               {
+                       if (m_curr != nullptr)
+                               m_curr = m_curr->next;
+                       return *this;
+               }
+       
+       private:
+       
+               const Node* m_curr;
+       };
+
+       /* RCUList
+       Singly linked list protected by a Read-Copy-Update (RCU) mechanism. */
+
+       RCUList()
+               : changed  (false),
+                 m_grace  (0), 
+                 m_size   (0), 
+                 m_writing(false),
+                 m_head   (nullptr),
+                 m_tail   (nullptr)
+       {
+               m_readers[0].store(0);
+               m_readers[1].store(0);
+       }
+
+       RCUList(std::unique_ptr<T> data) : RCUList()
+       {
+               push(std::move(data));
+       }
+
+       RCUList(const RCUList&) = delete;
+       RCUList(RCUList&&)      = delete;
+
+       ~RCUList()
+       {
+               clear();
+       }
+
+       Iterator begin()
+       { 
+               assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
+               return Iterator(m_head.load());
+       }
+
+       Iterator end()
+       { 
+               assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
+               return Iterator(nullptr);
+       }
+
+       /* unlock
+       Increases current readers count. Always call lock()/unlock() when reading
+       data from the list. Or use the scoped version Lock above. */
+
+       void lock()
+       {
+               t_grace = m_grace.load();
+               m_readers[t_grace]++;
+       }
+
+       /* unlock
+       Releases current readers count. */
+
+       void unlock()
+       {
+               m_readers[t_grace]--;
+       }
+
+       /* get
+       Returns a reference to the data held by node 'i'. */
+       // TODO - this will return a const ref with the non-virtual Channel
+       // refactoring. 
+
+       T* get(size_t i=0) const
+       {
+               assert(i < size() && "Index overflow");
+               assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
+               return getNode(i)->data.get();
+       }
+
+       /* Subscript operator []
+       Same as above for the [] syntax. */
+       // TODO - this will return a const ref with the non-virtual Channel
+       // refactoring. 
+
+       T* operator[] (size_t i) const
+       {
+       return get(i);
+    }
+
+       /* back
+       Return data held by the last node. */
+       // TODO - this will return a const ref with the non-virtual Channel
+       // refactoring. 
+
+       T* back() const
+       {
+               assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
+               return m_tail.load()->data.get();
+       }
+
+       /* clone
+       Returns a new copy of the data held by node 'i'. The template machinery
+       is required for when you declare a RCUList<Base> and later on want to clone
+       a derived object. Usage:
+       
+       RCUList<Base> list;
+       ...
+       std::unique_ptr<Derived> d = list.clone<Derived>(i);
+       */
+
+       template<typename C=T>
+       std::unique_ptr<C> clone(size_t i=0) const
+    {
+               return std::make_unique<C>(*static_cast<C*>(getNode(i)->data.get()));
+    }
+
+       /* swap
+       Exchanges data contained in node 'i' with new data 'data'. New data must
+       always come from a call to clone(). There is a natural protection against 
+       multiple calls to swap() made by the same thread: the caller is blocked by 
+       the spinlock below: no progress is made until m_readers[oldgrace] > 0. */
+
+       void swap(std::unique_ptr<T> data, size_t i=0)
+       {
+               /* Never start two overlapping writing sessions. */
+
+               if (m_writing.load() == true)
+                       return;
+               
+               /* Begin of writing session. */
+
+               m_writing.store(true);
+
+               /* Flip the current grace bit. Now we have entered a new grace period
+               with a different number from the previous one. */
+
+               std::int8_t oldgrace = m_grace.fetch_xor(1);
+
+               /* Prepare useful node pointers: current, next and previous. Fetching
+               from the current list with getNode() is safe here: we are just reading. */
+
+               Node* curr = getNode(i);
+               Node* prev = curr == m_head.load() ? nullptr : getNode(i - 1);
+               Node* next = curr == m_tail.load() ? nullptr : getNode(i + 1);
+               
+               /* Prepare a new node holding the new data in input. */
+
+               Node* n = new Node(std::move(data), next);
+       
+               /* Make the previous node point to the new one just created. New
+               readers will read the new one from now on. The only write operation 
+               performed here is the atomic store. */
+
+               if (prev != nullptr)
+                       prev->next.store(n);
+               else
+                       m_head.store(n);
+
+               if (next == nullptr)
+                       m_tail.store(n);
+
+               /* Wait until no readers from the previous grace period are reading the 
+               list. Avoid brutal spinlock with a tiny sleep. */
+
+               while (m_readers[oldgrace] > 0)
+                       std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+               /* Delete old node. Node destructor makes sure data is deleted. */
+
+               delete curr;
+
+               /* End of writing session. */
+
+               m_writing.store(false);
+               changed.store(true);
+       }
+
+       /* push
+       Adds a new element to the list containing 'data'. */
+
+       void push(std::unique_ptr<T> data)
+       {
+               /* Never start two overlapping writing sessions. */
+
+               if (m_writing.load() == true)
+                       return;
+
+               /* Begin of writing session. */
+
+               m_writing.store(true);  
+               
+               /* Create new node. */
+
+               Node* n = new Node(std::move(data));
+               
+               /* Update the current tail->next pointer to this node, if a tail exists.
+               I.e., grab the current last node and append it the new one. */
+
+               if (m_tail.load() != nullptr)
+                       m_tail.load()->next.store(n);
+               
+               /* Update tail pointer to point to the new node. */
+
+               m_tail.store(n);
+
+               /* Head is null when the list is empty. If so, set head to this new
+               node too. A list with only one node has both head and tail pointing
+               to the same node. */
+
+               if (m_head.load() == nullptr)
+                       m_head.store(n);
+
+               /* Upgrade static size. Last thing to do, so that other threads won't
+               read a false size. */
+
+               m_size++;
+
+               /* End of writing session. Data has changed, set the flag. */
+
+               m_writing.store(false);
+               changed.store(true);
+       }
+
+       /* pop
+       Removes the i-th element. There is a natural protection against multiple
+       calls to pop() made by the same thread: the caller is blocked by the
+       spinlock below: no progress is made while m_readers[oldgrace] > 0. */
+
+       void pop(size_t i)
+       {
+               /* Never start two overlapping writing sessions. */
+
+               if (m_writing.load() == true)
+                       return;
+
+               /* Begin of writing session. */
+
+               m_writing.store(true);
+
+               /* Flip the current grace bit. Now we have entered a new grace period
+               with a different number from the previous one. */
+
+               std::int8_t oldgrace = m_grace.fetch_xor(1);
+               
+               /* Prepare useful node pointers: current, next and previous. Fetching
+               from the current list with getNode() is safe here: we are just reading. */
+
+               Node* curr = getNode(i);
+               Node* prev = curr == m_head.load() ? nullptr : getNode(i - 1);
+               Node* next = curr == m_tail.load() ? nullptr : getNode(i + 1);
+               
+               /* Disconnect the previous node from the current one to be deleted:
+               prev->curr->next becomes prev->next, with (curr) pending in the void 
+               for the remaining readers. Special care is needed if we are removing an 
+               edge node. */
+
+               if (prev != nullptr)
+                       prev->next.store(next);
+               else 
+                       m_head.store(next);
+
+               if (next == nullptr)
+                       m_tail.store(prev);
+
+               /* Size can be updated at this point. */
+
+               m_size--;
+               
+               /* Wait until no readers from the previous grace period are reading the 
+               list. Avoid brutal spinlock with a tiny sleep. */
+
+               while (m_readers[oldgrace] > 0)
+                       std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+               /* Delete old node. Node destructor makes sure data is deleted. */
+
+               delete curr;
+               
+               /* End of writing session. Data has changed, set the flag. */
+
+               m_writing.store(false);
+               changed.store(true);
+       }
+
+       /* clear
+       Removes all nodes. */
+
+       void clear()
+       {
+               /* Never start two overlapping writing sessions. */
+
+               if (m_writing.load() == true)
+                       return;
+
+               /* Begin of writing session. */
+
+               m_writing.store(true);
+
+               /* Flip the current grace bit. Now we have entered a new grace period
+               with a different number from the previous one. */
+
+               std::int8_t oldgrace = m_grace.fetch_xor(1);
+       
+               /* Store the first node locally. We will need it later on. */
+
+               Node* current = m_head.load();
+
+               /* Block any other reader by setting the size to 0 in advance and
+               cleaning up head/tail pointers. */
+
+               m_size.store(0);
+               m_head.store(nullptr);
+               m_tail.store(nullptr);
+
+               while (current != nullptr) {
+
+                       /* Wait until no readers from the previous grace period are reading the 
+                       list. Avoid brutal spinlock with a tiny sleep. */
+
+                       while (m_readers[oldgrace] > 0)
+                               std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+                       /* Delete old node. Node destructor makes sure data is deleted. */
+
+               Node* next = current->next;
+               delete current;
+               current = next;
+               }
+
+               /* End of writing session. Data has changed, set the flag. */
+
+               m_writing.store(false);
+               changed.store(true);
+       }
+
+       /* size
+       Returns the number of nodes in the list. */
+
+       size_t size() const
+       {
+               return m_size.load();
+       }
+
+       /* changed
+       Tells whether the list has been altered with a swap, a push or a pop. */
+
+       std::atomic<bool> changed;
+       
+       /* value_type
+       A variable that holds the type of data contained in the list. Used for
+       metaprogramming stuff. */
+
+       using value_type = T;
+
+private:
+
+       Node* getNode(size_t i) const
+       {
+               size_t p    = 0;
+               Node*  curr = m_head.load();
+               
+               while (curr != nullptr && p < i) {
+                       p++;
+                       curr = curr->next.load();
+               }
+               assert(curr != nullptr);
+               return curr;
+       }
+
+       std::array<std::atomic<int>, 2> m_readers;
+       std::atomic<std::int8_t>        m_grace;
+       std::atomic<size_t>             m_size;
+       std::atomic<bool>               m_writing;
+
+       /* m_head
+       Pointer to the first node. Used when reading and writing: always update
+       this one first, as it is read by reader threads. */
+
+       std::atomic<Node*> m_head;
+
+       /* m_tail
+       Pointer to the last node. Used only when reading: unlike m_head, updating it
+       the right time is not that critical. */
+
+       std::atomic<Node*> m_tail;
+
+       /* t_grace
+       Current grace flag. Each thread has its own copy of it (thread_local). */
+
+       thread_local static int t_grace;
+};
+
+
+template<typename T>
+thread_local int RCUList<T>::t_grace = 0;
+}} // giada::m::
+
+
+#endif
index 57bb28307678bd0e0addb70d705b143fb181c80f..7ca9372cd68cac4645617ff8276a69572fa48ea3 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../gui/dispatcher.h"
-#include "../glue/transport.h"
-#include "types.h"
-#include "clock.h"
-#include "kernelAudio.h"
-#include "conf.h"
-#include "channel.h"
-#include "mixer.h"
-#include "mixerHandler.h"
-#include "midiDispatcher.h"
-#include "recorder.h"
-#include "recorderHandler.h"
-#include "recManager.h"
+#include "gui/dispatcher.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/types.h"
+#include "core/clock.h"
+#include "core/kernelAudio.h"
+#include "core/conf.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/midiDispatcher.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
 
 
 namespace giada {
@@ -46,8 +46,22 @@ namespace recManager
 {
 namespace
 {
-pthread_mutex_t* mixerMutex_ = nullptr;
-bool isActive_ = false;
+void setRecordingAction_(bool v)
+{
+       model::onSwap(model::recorder, [&](model::Recorder& r)
+       {
+               r.isRecordingAction = v;
+       });
+}
+
+
+void setRecordingInput_(bool v)
+{
+       model::onSwap(model::recorder, [&](model::Recorder& r)
+       {
+               r.isRecordingInput = v;
+       });
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -55,12 +69,11 @@ bool isActive_ = false;
 
 bool startActionRec_()
 {
-       if (!kernelAudio::getStatus())
+       if (!kernelAudio::isReady())
                return false;
        clock::setStatus(ClockStatus::RUNNING);
-       recorder::enable();
-       c::transport::startSeq(/*gui=*/false);
-       return true;    
+       m::mh::startSequencer();
+       return true;
 }
 
 
@@ -69,9 +82,10 @@ bool startActionRec_()
 
 bool startInputRec_()
 {
-       if (!kernelAudio::getStatus() || !mh::startInputRec())
+       if (!kernelAudio::isReady() || !mh::hasRecordableSampleChannels())
                return false;
-       c::transport::startSeq(/*gui=*/false);
+       mixer::startInputRec();
+       mh::startSequencer();
        return true;
 }
 } // {anonymous}
@@ -82,34 +96,42 @@ bool startInputRec_()
 /* -------------------------------------------------------------------------- */
 
 
-void init(pthread_mutex_t* mixerMutex)
-{
-       mixerMutex_ = mixerMutex;
-       isActive_   = false;
+bool isRecording()
+{ 
+       return isRecordingAction() || isRecordingInput();
 }
 
 
-/* -------------------------------------------------------------------------- */
+bool isRecordingAction()
+{ 
+       model::RecorderLock lock(model::recorder); 
+       return model::recorder.get()->isRecordingAction;
+}
 
 
-bool isActive()  { return isActive_; }
+bool isRecordingInput()
+{ 
+       model::RecorderLock lock(model::recorder); 
+       return model::recorder.get()->isRecordingInput; 
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool startActionRec(RecTriggerMode mode)
+void startActionRec(RecTriggerMode mode)
 {
-       isActive_ = true;
-       if (mode == RecTriggerMode::NORMAL)
-               return startActionRec_();
-       if (mode == RecTriggerMode::SIGNAL) {
+       if (mode == RecTriggerMode::NORMAL) {
+               if (startActionRec_())
+                       setRecordingAction_(true);
+       }
+       else {   // RecTriggerMode::SIGNAL
                clock::setStatus(ClockStatus::WAITING);
                clock::rewind();
                m::midiDispatcher::setSignalCallback(startActionRec_);
                v::dispatcher::setSignalCallback(startActionRec_);
+               setRecordingAction_(true);
        }
-       return true;
 }
 
 
@@ -118,27 +140,38 @@ bool startActionRec(RecTriggerMode mode)
 
 void stopActionRec()
 {
-       isActive_ = false;
+       setRecordingAction_(false);
+
+       /* If you stop the Action Recorder in SIGNAL mode before any actual 
+       recording: just clean up everything and return. */
 
        if (clock::getStatus() == ClockStatus::WAITING) {
                clock::setStatus(ClockStatus::STOPPED);
+               m::midiDispatcher::setSignalCallback(nullptr);
+               v::dispatcher::setSignalCallback(nullptr);
                return;
        }
 
-       clock::setStatus(ClockStatus::RUNNING);
-
-       recorder::disable();
-       std::unordered_set<int> channels = recorderHandler::consolidate();
+       std::unordered_set<ID> channels = recorderHandler::consolidate();
 
        /* Enable reading actions for Channels that have just been filled with 
        actions. Start reading right away, without checking whether 
        conf::treatRecsAsLoops is enabled or not. */
 
-       pthread_mutex_lock(mixerMutex_);
-       for (int index : channels)
-               mh::getChannelByIndex(index)->startReadingActions(
-                       /*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false); 
-       pthread_mutex_unlock(mixerMutex_);
+       for (ID id : channels)
+               m::model::onGet(m::model::channels, id, [](Channel& c)
+               {
+                       c.startReadingActions(/*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+               });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleActionRec(RecTriggerMode m)
+{
+       isRecordingAction() ? stopActionRec() : startActionRec(m);
 }
 
 
@@ -147,17 +180,21 @@ void stopActionRec()
 
 bool startInputRec(RecTriggerMode mode)
 {
-       if (mode == RecTriggerMode::NORMAL)
-               isActive_ = startInputRec_();
-       if (mode == RecTriggerMode::SIGNAL) {
+       if (mode == RecTriggerMode::NORMAL) {
+               if (!startInputRec_())
+                       return false;
+               setRecordingInput_(true);
+               return true;
+       }
+       else {   // RecTriggerMode::SIGNAL
                if (!mh::hasRecordableSampleChannels())
                        return false;
                clock::setStatus(ClockStatus::WAITING);
                clock::rewind();
                mixer::setSignalCallback(startInputRec_);
-               isActive_ = true;
+               setRecordingInput_(true);
+               return true;
        }
-       return isActive_;
 }
 
 
@@ -166,14 +203,31 @@ bool startInputRec(RecTriggerMode mode)
 
 void stopInputRec()
 {
-       isActive_ = false;
+       setRecordingInput_(false);
 
-       if (clock::getStatus() == ClockStatus::WAITING) {
+       mixer::stopInputRec();
+       
+       /* If you stop the Input Recorder in SIGNAL mode before any actual 
+       recording: just clean up everything and return. */
+
+       if (clock::getStatus() == ClockStatus::WAITING) {
                clock::setStatus(ClockStatus::STOPPED);
+               mixer::setSignalCallback(nullptr);
        }
-       else {
-               clock::setStatus(ClockStatus::RUNNING);
-               mh::stopInputRec();
+       else
+               mh::finalizeInputRec();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool toggleInputRec(RecTriggerMode m)
+{
+       if (isRecordingInput()) {
+               stopInputRec();
+               return true;
        }
+       return startInputRec(m);
 }
-}}} // giada::m::recManager
\ No newline at end of file
+}}} // giada::m::recManager
index 13c6f859b4b242cc3f4a6ac2229febc7f801ed7f..d50e4bdf37c25393af0bb0fc52a66ef666604dbe 100644 (file)
 #define G_REC_MANAGER_H
 
 
-#include <pthread.h>
-#include "types.h"
+#include "core/types.h"
 
 
 namespace giada {
 namespace m {
 namespace recManager
 {
-void init(pthread_mutex_t* mixerMutex);
+bool isRecording();
+bool isRecordingAction();
+bool isRecordingInput();
 
-/* isActive
-Returns true if its ready for recording, whether it is actually recording 
-something or is in wait mode for a signal. */
-
-bool isActive();
-
-bool startActionRec(RecTriggerMode m);
+void startActionRec(RecTriggerMode m);
 void stopActionRec();
+void toggleActionRec(RecTriggerMode m);
+
 bool startInputRec(RecTriggerMode m);
 void stopInputRec();
+bool toggleInputRec(RecTriggerMode m);
 }}} // giada::m::recManager
 
-#endif
\ No newline at end of file
+#endif
index c5fb6e72243cd225d0ba08739e3e88651f8bb0ef..d5e156f832be696906fd9a312ef9c34163d1ec9c 100644 (file)
 #include <memory>
 #include <algorithm>
 #include <cassert>
-#include "../utils/log.h"
-#include "action.h"
-#include "channel.h"
-#include "recorder.h"
-
-
-using std::map;
-using std::vector;
+#include "utils/log.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/action.h"
+#include "core/idManager.h"
+#include "core/recorder.h"
 
 
 namespace giada {
@@ -44,28 +42,20 @@ namespace recorder
 {
 namespace
 {
-/* actions
-The big map of actions {frame : actions[]}. This belongs to Recorder, but it
-is often parsed by Mixer. So every "write" action performed on it (add, 
-remove, ...) must be guarded by a lock on the mixerMutex. Until a proper
-lock-free solution will be implemented. */
-
-ActionMap actions;
-
-pthread_mutex_t* mixerMutex_ = nullptr;
-bool             active_     = false;
-int              actionId_   = 0;
+IdManager actionId_;
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void lock_(std::function<void()> f)
+Action* findAction_(ActionMap& src, ID id)
 {
-       assert(mixerMutex_ != nullptr);
-       pthread_mutex_lock(mixerMutex_);
-       f();
-       pthread_mutex_unlock(mixerMutex_);
+       for (auto& kv : src)
+               for (Action& a : kv.second)
+                       if (a.id == id)
+                               return &a;
+       assert(false);
+       return nullptr; 
 }
 
 
@@ -84,29 +74,17 @@ void optimize_(ActionMap& map)
 /* -------------------------------------------------------------------------- */
 
 
-void removeIf_(std::function<bool(const Action*)> f)
+void removeIf_(std::function<bool(const Action&)> f)
 {
-       ActionMap temp = actions;
-
-       /*
-       for (auto& kv : temp) {
-               vector<Action*>& as = kv.second;
-               as.erase(std::remove_if(as.begin(), as.end(), f), as.end());
-       }*/
-       for (auto& kv : temp) {
-               auto i = std::begin(kv.second);
-               while (i != std::end(kv.second)) {
-                       if (f(*i)) {
-                               delete *i;
-                               i = kv.second.erase(i);
-                       }
-                       else
-                               ++i;
+       model::onSwap(model::actions, [&](model::Actions& a)
+       {
+               for (auto& kv : a.map) {
+                       std::vector<Action>& as = kv.second;
+                       as.erase(std::remove_if(as.begin(), as.end(), f), as.end());
                }
-       }
-       optimize_(temp);
-
-       lock_([&](){ actions = std::move(temp); });
+               optimize_(a.map);
+               updateMapPointers(a.map);
+       });
 }
 } // {anonymous}
 
@@ -116,11 +94,9 @@ void removeIf_(std::function<bool(const Action*)> f)
 /* -------------------------------------------------------------------------- */
 
 
-void init(pthread_mutex_t* m)
+void init()
 {
-       mixerMutex_ = m;
-       active_     = false;
-       actionId_   = 0;
+       actionId_ = IdManager();
        clearAll();
 }
 
@@ -128,59 +104,48 @@ void init(pthread_mutex_t* m)
 /* -------------------------------------------------------------------------- */
 
 
-void debug()
+void clearAll()
 {
-       int total = 0;
-       puts("-------------");
-       for (auto& kv : actions) {
-               printf("frame: %d\n", kv.first);
-               for (const Action* a : kv.second) {
-                       total++;
-                       printf(" this=%p - id=%d, frame=%d, channel=%d, value=0x%X, prev=%p, next=%p\n", 
-                               (void*) a, a->id, a->frame, a->channel, a->event.getRaw(), (void*) a->prev, (void*) a->next);   
-               }
-       }
-       printf("TOTAL: %d\n", total);
-       puts("-------------");
+       model::onSwap(model::actions, [&](model::Actions& a)
+       {
+               a.map.clear();
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void clearAll()
+void clearChannel(ID channelId)
 {
-       removeIf_([=](const Action* a) { return true; }); // TODO optimize this
+       removeIf_([=](const Action& a) { return a.channelId == channelId; });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void clearChannel(int channel)
+void clearActions(ID channelId, int type)
 {
-       removeIf_([=](const Action* a) { return a->channel == channel; });
+       removeIf_([=](const Action& a)
+       { 
+               return a.channelId == channelId && a.event.getStatus() == type;
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void clearActions(int channel, int type)
+void deleteAction(ID id)
 {
-       removeIf_([=](const Action* a)
-       { 
-               return a->channel == channel && a->event.getStatus() == type;
-       });
+       removeIf_([=](const Action& a) { return a.id == id; });
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void deleteAction(const Action* target)
+void deleteAction(ID currId, ID nextId)
 {
-       removeIf_([=](const Action* a) { return a == target; });
+       removeIf_([=](const Action& a) { return a.id == currId || a.id == nextId; });
 }
 
 
@@ -189,47 +154,71 @@ void deleteAction(const Action* target)
 
 void updateKeyFrames(std::function<Frame(Frame old)> f)
 {
-       /* This stuff must be performed in a lock, because we are moving the vector
-       of actions from the real ActionMap to the temporary one. */
+       std::unique_ptr<model::Actions> ma = model::actions.clone();
        
-       ActionMap temp;
+       /* Remove all existing actions: let's start from scratch. */
 
-       lock_([&]()
-       { 
-               for (auto& kv : actions) {
+       ma->map.clear();
+
+       /* Copy all existing actions in local data by cloning them, with just a
+       difference: they have a new frame value. */
+
+       {
+               model::ActionsLock lock(model::actions);
+
+               for (const auto& kv : model::actions.get()->map) {
                        Frame frame = f(kv.first);
-                       temp[frame] = std::move(kv.second);  // Move std::vector<Action*>
-                       for (const Action* action : temp[frame])
-                               const_cast<Action*>(action)->frame = frame;
-                       gu_log("[recorder::updateKeyFrames] %d -> %d\n", kv.first, frame);
+                       for (const Action& a : kv.second) {
+                               Action copy = a;
+                               copy.frame = frame;
+                               ma->map[frame].push_back(copy);
+                       }
+                       u::log::print("[recorder::updateKeyFrames] %d -> %d\n", kv.first, frame);
                }
-               actions = std::move(temp); 
-       });
+       }
+
+       updateMapPointers(ma->map);
+
+       model::actions.swap(std::move(ma));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateEvent(const Action* a, MidiEvent e)
+void updateEvent(ID id, MidiEvent e)
 {
-       assert(a != nullptr);
-       lock_([&] { const_cast<Action*>(a)->event = e; });
+       model::onSwap(model::actions, [&](model::Actions& a)
+       {
+               findAction_(a.map, id)->event = e;
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateSiblings(const Action* a, const Action* prev, const Action* next)
+void updateSiblings(ID id, ID prevId, ID nextId)
 {
-       assert(a != nullptr);
-       lock_([&] 
-       { 
-               const_cast<Action*>(a)->prev = prev;
-               const_cast<Action*>(a)->next = next;
-               if (prev != nullptr) const_cast<Action*>(prev)->next = a;               
-               if (next != nullptr) const_cast<Action*>(next)->prev = a;       
+       model::onSwap(model::actions, [&](model::Actions& a)
+       {
+               Action* pcurr = findAction_(a.map, id);
+               Action* pprev = findAction_(a.map, prevId);
+               Action* pnext = findAction_(a.map, nextId);
+
+               pcurr->prev   = pprev;
+               pcurr->prevId = pprev->id;
+               pcurr->next   = pnext;
+               pcurr->nextId = pnext->id;
+
+               if (pprev != nullptr) {
+                       pprev->next   = pcurr;
+                       pprev->nextId = pcurr->id;
+               }
+               if (pnext != nullptr) {
+                       pnext->prev   = pcurr;
+                       pnext->prevId = pcurr->id;
+               }
        });
 }
 
@@ -237,104 +226,129 @@ void updateSiblings(const Action* a, const Action* prev, const Action* next)
 /* -------------------------------------------------------------------------- */
 
 
-void updateActionMap(ActionMap&& am)
+bool hasActions(ID channelId, int type)
 {
-       lock_([&](){ actions = am; });
+       model::ActionsLock lock(model::actions);
+       
+       for (const auto& kv : model::actions.get()->map)
+               for (const Action& a : kv.second)
+                       if (a.channelId == channelId && (type == 0 || type == a.event.getStatus()))
+                               return true;
+       return false;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateActionId(int id)
+Action makeAction(ID id, ID channelId, Frame frame, MidiEvent e)
 {
-       if (actionId_ <= id)  // Never decrease it
-               actionId_ = id;
+       Action out {actionId_.get(id), channelId, frame, e, -1, -1};
+       actionId_.set(id);
+       return out;
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-bool hasActions(int channel, int type)
+Action makeAction(const patch::Action& a)
 {
-       for (auto& kv : actions)
-               for (const Action* action : kv.second)
-                       if (action->channel == channel && (type == 0 || type == action->event.getStatus()))
-                               return true;
-       return false;
+       actionId_.set(a.id);
+       return Action {a.id, a.channelId, a.frame, a.event, -1, -1, a.prevId,
+               a.nextId};
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool isActive() { return active_; }
-void enable()   { active_ = true; }
-void disable()  { active_ = false; }
-
-
-/* -------------------------------------------------------------------------- */
+Action rec(ID channelId, Frame frame, MidiEvent event)
+{
+       Action a = makeAction(0, channelId, frame, event);
+       
+       /* If key frame doesn't exist yet, the [] operator in std::map is smart 
+       enough to insert a new item first. No plug-in data for now. */
 
+       model::onSwap(model::actions, [&](model::Actions& mas)
+       {
+               mas.map[frame].push_back(a);
+               updateMapPointers(mas.map);
+       });
 
-const Action* makeAction(int id, int channel, Frame frame, MidiEvent e)
-{
-       return new Action{ id, channel, frame, e, -1, -1, nullptr, nullptr };
+       return a;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-const Action* rec(int channel, Frame frame, MidiEvent event)
+void rec(std::vector<Action>& as)
 {
-       /* If key frame doesn't exist yet, the [] operator in std::map is smart 
-       enough to insert a new item first. No plug-in data for now. */
+       if (as.size() == 0)
+               return;
 
-       lock_([&]
-       { 
-               actions[frame].push_back(makeAction(actionId_++, channel, frame, event));
+       /* Generate new action ID and fix next and prev IDs. */
+
+       for (Action& a : as) {
+               int id = a.id;
+               a.id = actionId_.get();
+               for (Action& aa : as) {
+                       if (aa.prevId == id) aa.prevId = a.id;
+                       if (aa.nextId == id) aa.nextId = a.id;
+               }
+       }
+       
+       model::onSwap(model::actions, [&](model::Actions& mas)
+       {
+               for (const Action& a : as)
+                       mas.map[a.frame].push_back(a);
+               updateMapPointers(mas.map);
        });
-       return actions[frame].back();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void rec(const std::vector<const Action*>& as)
+void rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2)
 {
-       ActionMap temp = actions;
+       model::onSwap(model::actions, [&](model::Actions& mas)
+       {
+               mas.map[f1].push_back(makeAction(0, channelId, f1, e1));
+               mas.map[f2].push_back(makeAction(0, channelId, f2, e2));
 
-       for (const Action* a : as) {
-               const_cast<Action*>(a)->id = actionId_++;
-               temp[a->frame].push_back(a); // Memory is already allocated by recorderHandler
-       }
+               Action* a1 = findAction_(mas.map, mas.map[f1].back().id);
+               Action* a2 = findAction_(mas.map, mas.map[f2].back().id);
+               a1->nextId = a2->id;
+               a2->prevId = a1->id;
 
-       lock_([&](){ actions = std::move(temp); });
+               updateMapPointers(mas.map);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-vector<const Action*> getActionsOnFrame(Frame frame)
+const std::vector<Action>* getActionsOnFrame(Frame frame)
 {
-       return actions.count(frame) ? actions[frame] : vector<const Action*>();
+       model::ActionsLock lock(model::actions);
+       
+       if (model::actions.get()->map.count(frame) == 0)
+               return nullptr;
+       return &model::actions.get()->map.at(frame);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-const Action* getClosestAction(int channel, Frame f, int type)
+Action getClosestAction(ID channelId, Frame f, int type)
 {
-       const Action* out = nullptr;
-       forEachAction([&](const Action* a)
+       Action out = {};
+       forEachAction([&](const Action& a)
        {
-               if (a->event.getStatus() != type || a->channel != channel)
+               if (a.event.getStatus() != type || a.channelId != channelId)
                        return;
-               if (out == nullptr || (a->frame <= f && a->frame > out->frame))
+               if (!out.isValid() || (a.frame <= f && a.frame > out.frame))
                        out = a;
        });
        return out;
@@ -344,34 +358,43 @@ const Action* getClosestAction(int channel, Frame f, int type)
 /* -------------------------------------------------------------------------- */
 
 
-ActionMap getActionMap() { return actions; }
-
-int getLatestActionId() { return actionId_; }
+std::vector<Action> getActionsOnChannel(ID channelId)
+{
+       std::vector<Action> out;
+       forEachAction([&](const Action& a)
+       {
+               if (a.channelId == channelId)
+                       out.push_back(a);
+       });
+       return out;
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-vector<const Action*> getActionsOnChannel(int channel)
+void updateMapPointers(ActionMap& src)
 {
-       vector<const Action*> out;
-       forEachAction([&](const Action* a)
-       {
-               if (a->channel == channel)
-                       out.push_back(a);
-       });
-       return out;
+       for (auto& kv : src) {
+               for (Action& action : kv.second) {
+                       if (action.nextId != 0)
+                               action.next = findAction_(src, action.nextId);
+                       if (action.prevId != 0)
+                               action.prev = findAction_(src, action.prevId);
+               }
+       }
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void forEachAction(std::function<void(const Action*)> f)
+void forEachAction(std::function<void(const Action&)> f)
 {
-       for (auto& kv : actions)
-               for (const Action* action : kv.second)
+       model::ActionsLock lock(model::actions);
+       
+       for (auto& kv : model::actions.get()->map)
+               for (const Action& action : kv.second)
                        f(action);
 }
-
 }}}; // giada::m::recorder::
index 0f3f1fb513b55cccace9e31a3512d67971ec92d3..77eead0068de569ccaea3b6a5e54a594fc692f56 100644 (file)
 #define G_RECORDER_H
 
 
-#include <pthread.h>
 #include <map>
 #include <vector>
 #include <functional>
-#include "types.h"
-#include "midiEvent.h"
+#include <memory>
+#include "core/types.h"
+#include "core/action.h"
+#include "core/patch.h"
+#include "core/midiEvent.h"
 
 
 namespace giada {
 namespace m 
 {
-struct Action;
-
 namespace recorder
 {
-using ActionMap = std::map<Frame, std::vector<const Action*>>;
+using ActionMap = std::map<Frame, std::vector<Action>>;
 
-void debug();
 /* init
 Initializes the recorder: everything starts from here. */
 
-void init(pthread_mutex_t* mixerMutex);
+void init();
 
 /* clearAll
 Deletes all recorded actions. */
@@ -60,17 +59,22 @@ void clearAll();
 /* clearChannel
 Clears all actions from a channel. */
 
-void clearChannel(int channel);
+void clearChannel(ID channelId);
 
 /* clearActions
 Clears the actions by type from a channel. */
 
-void clearActions(int channel, int type);
+void clearActions(ID channelId, int type);
 
-/* deleteAction
+/* deleteAction (1)
 Deletes a specific action. */
 
-void deleteAction(const Action* a);
+void deleteAction(ID id);
+
+/* deleteAction (2)
+Deletes a specific pair of actions. Useful for composite stuff (i.e. MIDI). */
+
+void deleteAction(ID currId, ID nextId);
 
 /* updateKeyFrames
 Update all the key frames in the internal map of actions, according to a lambda 
@@ -78,80 +82,74 @@ function 'f'. */
 
 void updateKeyFrames(std::function<Frame(Frame old)> f);
 
-/* updateActionMap
-Replaces the current map of actions with a new one. Warning: 'am' will be moved
-as a replacement (no copy). */
-
-void updateActionMap(ActionMap&& am);
-
 /* updateEvent
 Changes the event in action 'a'. */
 
-void updateEvent(const Action* a, MidiEvent e);
+void updateEvent(ID id, MidiEvent e);
 
 /* updateSiblings
-Changes previous and next actions in action 'a'. Mostly used for chained actions
-such as envelopes. */
-
-void updateSiblings(const Action* a, const Action* prev, const Action* next);
+Changes previous and next actions in action with id 'id'. Mostly used for 
+chained actions such as envelopes. */
 
-void updateActionId(int id);
+void updateSiblings(ID id, ID prevId, ID nextId);
 
 /* hasActions
 Checks if the channel has at least one action recorded. */
 
-bool hasActions(int channel, int type=0);
-
-/* isActive
-Is recorder recording something? */
+bool hasActions(ID channelId, int type=0);
 
-bool isActive();
+/* makeAction
+Makes a new action given some data. */
 
-void enable();
-void disable();
-
-const Action* makeAction(int id, int channel, Frame frame, MidiEvent e);
+Action makeAction(ID id, ID channelId, Frame frame, MidiEvent e);
+Action makeAction(const patch::Action& a);
 
 /* rec (1)
-Records an action and returns it. */
+Records an action and returns it. Used by the Action Editor. */
 
-const Action* rec(int channel, Frame frame, MidiEvent e);
+Action rec(ID channelId, Frame frame, MidiEvent e);
 
 /* rec (2)
 Transfer a vector of actions into the current ActionMap. This is called by 
 recordHandler when a live session is over and consolidation is required. */
 
-void rec(const std::vector<const Action*>& actions);
+void rec(std::vector<Action>& actions);
+
+/* rec (3)
+Records two actions on channel 'channel'. Useful when recording composite 
+actions in the Action Editor. */
+
+void rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2);
 
 /* forEachAction
 Applies a read-only callback on each action recorded. NEVER do anything inside 
 the callback that might alter the ActionMap. */
 
-void forEachAction(std::function<void(const Action*)> f);
+void forEachAction(std::function<void(const Action&)> f);
 
 /* getActionsOnFrame
-Returns a vector of actions recorded on frame 'f'. */
+Returns a pointer to a vector of actions recorded on frame 'f', or nullptr if
+the frame has no actions. */
 
-std::vector<const Action*> getActionsOnFrame(Frame f);
+const std::vector<Action>* getActionsOnFrame(Frame f);
 
 /* getActionsOnChannel
 Returns a vector of actions belonging to channel 'ch'. */
 
-std::vector<const Action*> getActionsOnChannel(int ch);
+std::vector<Action> getActionsOnChannel(ID channelId);
 
 /* getClosestAction
 Given a frame 'f' returns the closest action. */
 
-const Action* getClosestAction(int channel, Frame f, int type);
-
-
-int getLatestActionId();
-
-/* getActionMap
-Returns a copy of the internal action map. Used only by recorderHandler. */
+Action getClosestAction(ID channelId, Frame f, int type);
 
-ActionMap getActionMap();
+/* updateMapPointers
+Updates all prev/next actions pointers into the action map. This is required
+after an action has been recorded, since pushing back new actions in a Action 
+vector makes it reallocating the existing ones. Also needed in model::Data copy
+constructor. */
 
+void updateMapPointers(ActionMap& src); 
 }}}; // giada::m::recorder::
 
 
index 53415db5aa6dcdef848717633a80f7d2cef72590..0bfba6d4967a4f8f7e91efa9efbffea02dace8fe 100644 (file)
 #include <algorithm>
 #include <cmath>
 #include <cassert>
-#include "../utils/log.h"
-#include "../utils/ver.h"
+#include "utils/log.h"
+#include "utils/ver.h"
+#include "model/model.h"
 #include "recorder.h"
 #include "action.h"
 #include "clock.h"
 #include "const.h"
+#include "patch.h"
 #include "recorderHandler.h"
 
 
@@ -43,18 +45,20 @@ namespace recorderHandler
 {
 namespace
 {
-std::vector<const Action*> recs_;
+constexpr int MAX_LIVE_RECS_CHUNK = 128;
+
+std::vector<Action> recs_; 
 
 
 /* -------------------------------------------------------------------------- */
 
 
-const Action* getActionById_(int id, const recorder::ActionMap& source)
+const Action* getActionPtrById_(int id, const recorder::ActionMap& source)
 {
        for (auto& kv : source)
-               for (const Action* action : kv.second)
-                       if (action->id == id)
-                               return action;
+               for (const Action& action : kv.second)
+                       if (action.id == id)
+                               return &action;
        return nullptr;
 }
 
@@ -65,12 +69,12 @@ const Action* getActionById_(int id, const recorder::ActionMap& source)
 /* areComposite_
 Composite: NOTE_ON + NOTE_OFF on the same note. */
 
-bool areComposite_(const Action* a1, const Action* a2)
+bool areComposite_(const Action& a1, const Action& a2)
 {
-       return a1->event.getStatus() == MidiEvent::NOTE_ON  &&
-              a2->event.getStatus() == MidiEvent::NOTE_OFF &&
-              a1->event.getNote() == a2->event.getNote()   &&
-              a1->channel == a2->channel;
+       return a1.event.getStatus() == MidiEvent::NOTE_ON  &&
+              a2.event.getStatus() == MidiEvent::NOTE_OFF &&
+              a1.event.getNote() == a2.event.getNote()    &&
+              a1.channelId == a2.channelId;
 }
 
 
@@ -84,20 +88,20 @@ in linear sequence, the potential partner of 'a1' always lies beyond a1 itself.
 Without this trick (i.e. if it loops from vector.begin() each time) the
 algorithm would end up matching wrong partners. */
 
-void consolidate_(const Action* a1, size_t i)
+void consolidate_(const Action& a1, size_t i)
 {
        for (auto it = recs_.begin() + i; it != recs_.end(); ++it) {
 
-               const Action* a2 = *it;
+               const Action& a2 = *it;
 
                if (!areComposite_(a1, a2))
                        continue;
 
-               const_cast<Action*>(a1)->next = a2;
-               const_cast<Action*>(a2)->prev = a1;
+               const_cast<Action&>(a1).nextId = a2.id;
+               const_cast<Action&>(a2).prevId = a1.id;
 
                break;
-       }               
+       }
 }
 
 
@@ -109,30 +113,28 @@ void consolidate_()
        for (auto it = recs_.begin(); it != recs_.end(); ++it)
                consolidate_(*it, it - recs_.begin());  // Pass current index
 }
+} // {anonymous}
 
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-void readPatch_DEPR_(const std::vector<patch::action_t>& pactions)
+void init()
 {
-       for (const patch::action_t paction : pactions)
-               recs_.push_back(recorder::makeAction(-1, paction.channel, paction.frame, MidiEvent(paction.event)));
-       
-       consolidate();
+       recs_.reserve(MAX_LIVE_RECS_CHUNK);
 }
-} // {anonymous}
 
 
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-bool isBoundaryEnvelopeAction(const Action* a)
+bool isBoundaryEnvelopeAction(const Action& a)
 {
-       assert(a->prev != nullptr);
-       assert(a->next != nullptr);
-       return a->prev->frame > a->frame || a->next->frame < a->frame;
+       assert(a.prev != nullptr);
+       assert(a.next != nullptr);
+       return a.prev->frame > a.frame || a.next->frame < a.frame;
 }
 
 
@@ -175,26 +177,22 @@ void updateSamplerate(int systemRate, int patchRate)
 /* -------------------------------------------------------------------------- */
 
 
-bool cloneActions(int chanIndex, int newChanIndex)
+bool cloneActions(ID channelId, ID newChannelId)
 {
-       recorder::ActionMap temp = recorder::getActionMap();
-
-       bool cloned   = false;
-       int  actionId = recorder::getLatestActionId(); 
+       bool cloned = false;
+       std::vector<Action> actions;
 
-       recorder::forEachAction([&](const Action* a) 
+       recorder::forEachAction([&](const Action& a) 
        {
-               if (a->channel == chanIndex) {
-                       Action* clone = new Action(*a);
-                       clone->id      = ++actionId;
-                       clone->channel = newChanIndex;
-                       temp[clone->frame].push_back(clone);
-                       cloned = true;
-               }
+               if (a.channelId != channelId)
+                       return;
+               Action clone(a);
+               clone.channelId = newChannelId;
+               actions.push_back(clone);
+               cloned = true;
        });
 
-       recorder::updateActionId(actionId);
-       recorder::updateActionMap(std::move(temp));
+       recorder::rec(actions);
 
        return cloned;
 }
@@ -203,24 +201,28 @@ bool cloneActions(int chanIndex, int newChanIndex)
 /* -------------------------------------------------------------------------- */
 
 
-void liveRec(int channel, MidiEvent e)
+void liveRec(ID channelId, MidiEvent e)
 {
        assert(e.isNoteOnOff()); // Can't record any other kind of events for now
-       recs_.push_back(recorder::makeAction(-1, channel, clock::getCurrentFrame(), e));
+
+       if (recs_.size() >= recs_.capacity())
+               recs_.reserve(recs_.size() + MAX_LIVE_RECS_CHUNK);
+       
+       recs_.push_back(recorder::makeAction(-1, channelId, clock::getCurrentFrame(), e));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-std::unordered_set<int> consolidate()
+std::unordered_set<ID> consolidate()
 {
        consolidate_();
        recorder::rec(recs_);
 
-       std::unordered_set<int> out;
-       for (const Action* action : recs_)
-               out.insert(action->channel);
+       std::unordered_set<ID> out;
+       for (const Action& action : recs_)
+               out.insert(action.channelId);
 
        recs_.clear();
        return out;
@@ -230,66 +232,46 @@ std::unordered_set<int> consolidate()
 /* -------------------------------------------------------------------------- */
 
 
-void writePatch(int chanIndex, std::vector<patch::action_t>& pactions)
+void clearAllActions()
 {
-       recorder::forEachAction([&] (const Action* a) 
-       {
-               if (a->channel != chanIndex) 
-                       return;
-               pactions.push_back(patch::action_t { 
-                       a->id, 
-                       a->channel, 
-                       a->frame, 
-                       a->event.getRaw(), 
-                       a->prev != nullptr ? a->prev->id : -1,
-                       a->next != nullptr ? a->next->id : -1
-               });
-       });
+       for (size_t i = 0; i < model::channels.size(); i++)
+               model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c) { c.hasActions = false; });
+       recorder::clearAll();
 }
 
 
-
 /* -------------------------------------------------------------------------- */
 
 
-void readPatch(const std::vector<patch::action_t>& pactions)
+recorder::ActionMap makeActionsFromPatch(const std::vector<patch::Action>& pactions)
 {
-       if (u::ver::isLess(patch::versionMajor, patch::versionMinor, patch::versionPatch, 0, 15, 3)) {
-               readPatch_DEPR_(pactions);
-               return;
-       }
+       recorder::ActionMap out;
 
-       recorder::ActionMap temp = recorder::getActionMap();
+       /* First pass: add actions with no relationship, that is with no prev/next
+       pointers filled in. */
 
-       /* First pass: add actions with no relationship (no prev/next). */
+       for (const patch::Action& paction : pactions)
+               out[paction.frame].push_back(recorder::makeAction(paction));
 
-       for (const patch::action_t paction : pactions) {
-               temp[paction.frame].push_back(recorder::makeAction(
-                       paction.id, 
-                       paction.channel, 
-                       paction.frame, 
-                       MidiEvent(paction.event)));
-               recorder::updateActionId(paction.id + 1);
-       }
-
-       /* Second pass: fill in previous and next actions, if any. */
+       /* Second pass: fill in previous and next actions, if any. Is this the
+       fastest/smartest way to do it? Maybe not. Optimizations are welcome. */
 
-       for (const patch::action_t paction : pactions) {
-               if (paction.next == -1 && paction.prev == -1
+       for (const patch::Action& paction : pactions) {
+               if (paction.nextId == 0 && paction.prevId == 0
                        continue;
-               Action* curr = const_cast<Action*>(getActionById_(paction.id, temp));
+               Action* curr = const_cast<Action*>(getActionPtrById_(paction.id, out));
                assert(curr != nullptr);
-               if (paction.next != -1) {
-                       curr->next = getActionById_(paction.next, temp);
+               if (paction.nextId != 0) {
+                       curr->next = getActionPtrById_(paction.nextId, out);
                        assert(curr->next != nullptr);
                }
-               if (paction.prev != -1) {
-                       curr->prev = getActionById_(paction.prev, temp);
+               if (paction.prevId != 0) {
+                       curr->prev = getActionPtrById_(paction.prevId, out);
                        assert(curr->prev != nullptr);
                }
        }
 
-       recorder::updateActionMap(std::move(temp));
+       return out;
 }
 }}}; // giada::m::recorderHandler::
 
index 0554b15fcfcae5cb7b4a664a0f23c5ace34663da..3da1743b9f4e0fb97141bae0e5ccecae8056781f 100644 (file)
 
 #include <unordered_set>
 #include "midiEvent.h"
-#include "patch.h"
 
 
 namespace giada {
 namespace m 
 {
+namespace patch
+{
+struct Action;
+}
 struct Action;
-
-
 namespace recorderHandler
 {
-bool isBoundaryEnvelopeAction(const Action* a);
+void init();
+
+bool isBoundaryEnvelopeAction(const Action& a);
 
 /* updateBpm
 Changes actions position by calculating the new bpm value. */
@@ -57,24 +60,28 @@ mandatory. */
 void updateSamplerate(int systemRate, int patchRate);
 
 /* cloneActions
-Clones actions in channel 'chanIndex', giving them a new channel index. Returns
+Clones actions in channel 'channelId', giving them a new channel ID. Returns
 whether any action has been cloned. */
 
-bool cloneActions(int chanIndex, int newChanIndex);
+bool cloneActions(ID channelId, ID newChannelId);
 
 /* liveRec
 Records a user-generated action. NOTE_ON or NOTE_OFF only for now. */
 
-void liveRec(int channel, MidiEvent e);
+void liveRec(ID channelId, MidiEvent e);
 
 /* consolidate
-Records all live actions. Returns a set of channels indexes that have been 
+Records all live actions. Returns a set of channels IDs that have been 
 recorded. */
 
-std::unordered_set<int> consolidate();
+std::unordered_set<ID> consolidate();
+
+/* clearAllActions
+Deletes all recorded actions. */
+
+void clearAllActions();
 
-void writePatch(int chanIndex, std::vector<patch::action_t>& pactions);
-void readPatch(const std::vector<patch::action_t>& pactions);
+recorder::ActionMap makeActionsFromPatch(const std::vector<patch::Action>& pactions);
 
 }}}; // giada::m::recorderHandler::
 
diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp
deleted file mode 100644 (file)
index 3a5d986..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 "sampleChannelProc.h"
-#include "sampleChannelRec.h"
-#include "channelManager.h"
-#include "const.h"
-#include "wave.h"
-#include "sampleChannel.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace m 
-{
-SampleChannel::SampleChannel(bool inputMonitor, int bufferSize)
-       : Channel          (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize),
-         mode             (ChannelMode::SINGLE_BASIC),
-         wave             (nullptr),
-         tracker          (0),
-         trackerPreview   (0),
-         shift            (0),
-         quantizing       (false),
-         inputMonitor     (inputMonitor),
-         boost            (G_DEFAULT_BOOST),
-         pitch            (G_DEFAULT_PITCH),
-         begin            (0),
-         end              (0),
-         midiInReadActions(0x0),
-         midiInPitch      (0x0),
-         rsmp_state       (nullptr)
-{
-       rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr);
-       if (rsmp_state == nullptr) {
-               gu_log("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
-               throw std::bad_alloc();
-       }
-       bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleChannel::~SampleChannel()
-{
-       if (rsmp_state != nullptr)
-               src_delete(rsmp_state);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
-{
-       Channel::copy(src_, pluginMutex);
-       const SampleChannel* src = static_cast<const SampleChannel*>(src_);
-       tracker         = src->tracker;
-       begin           = src->begin;
-       end             = src->end;
-       boost           = src->boost;
-       mode            = src->mode;
-       quantizing      = src->quantizing;
-       setPitch(src->pitch);
-
-       if (src->wave)
-               pushWave(std::make_unique<Wave>(*src->wave)); // invoke Wave's copy constructor
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::parseEvents(mixer::FrameEvents fe)
-{
-       sampleChannelProc::parseEvents(this, fe);
-       sampleChannelRec::parseEvents(this, fe);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::prepareBuffer(bool running)
-{
-       sampleChannelProc::prepareBuffer(this, running);
-}
-
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::rewindBySeq()
-{
-       sampleChannelProc::rewindBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::start(int localFrame, bool doQuantize, int velocity)
-{
-       sampleChannelProc::start(this, localFrame, doQuantize, velocity);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stop()
-{
-       sampleChannelProc::stop(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopBySeq(bool chansStopOnSeqHalt)
-{
-       sampleChannelProc::stopBySeq(this, chansStopOnSeqHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::kill(int localFrame)
-{
-       sampleChannelProc::kill(this, localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::recordStart(bool canQuantize) 
-{
-       return sampleChannelRec::recordStart(this, canQuantize);
-}
-
-
-bool SampleChannel::recordKill()
-{
-       return sampleChannelRec::recordKill(this);
-}
-
-
-void SampleChannel::recordStop()
-{
-       sampleChannelRec::recordStop(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt)
-{
-       sampleChannelRec::startReadingActions(this, treatRecsAsLoops, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopReadingActions(bool running, bool treatRecsAsLoops, 
-               bool recsStopOnChanHalt)
-{
-       sampleChannelRec::stopReadingActions(this, running, treatRecsAsLoops, 
-               recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopInputRec(int globalFrame)
-{
-       sampleChannelProc::stopInputRec(this, globalFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setMute(bool value)
-{
-       sampleChannelProc::setMute(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setSolo(bool value)
-{
-       sampleChannelProc::setSolo(this, value);
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::process(AudioBuffer& out, const AudioBuffer& in, 
-       bool audible, bool running)
-{
-       sampleChannelProc::process(this, out, in, audible, running);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::readPatch(const string& basePath, const patch::channel_t& pch)
-{
-       Channel::readPatch("", pch);
-       channelManager::readPatch(this, basePath, pch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::writePatch(int i, bool isProject)
-{
-       Channel::writePatch(i, isProject);
-       channelManager::writePatch(this, isProject, i);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
-{
-       sampleChannelRec::setReadActions(this, v, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::hasLogicalData() const
-{ 
-       return wave != nullptr && wave->isLogical();
-};
-
-
-bool SampleChannel::hasEditedData() const
-{ 
-       return wave != nullptr && wave->isEdited();
-};
-
-
-bool SampleChannel::hasData() const
-{
-       return wave != nullptr;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setBegin(int f)
-{
-       if (f < 0)
-               begin = 0;
-       else
-       if (f > wave->getSize())
-               begin = wave->getSize();
-       else
-       if (f >= end)
-               begin = end - 1;
-       else
-               begin = f;
-
-       tracker = begin;
-       trackerPreview = begin;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setEnd(int f)
-{
-       if (f >= wave->getSize())
-               end = wave->getSize() - 1;
-       else
-       if (f <= begin)
-               end = begin + 1;
-       else
-               end = f;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::getBegin() const { return begin; }
-int SampleChannel::getEnd() const   { return end; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setPitch(float v)
-{
-       if (v > G_MAX_PITCH)
-               pitch = G_MAX_PITCH;
-       else
-       if (v < G_MIN_PITCH)
-               pitch = G_MIN_PITCH;
-       else 
-               pitch = v;
-
-// ????        /* if status is off don't slide between frequencies */
-// ???? 
-// ????        if (status & (STATUS_OFF | STATUS_WAIT))
-// ????                src_set_ratio(rsmp_state, 1/pitch);
-}
-
-
-float SampleChannel::getPitch() const { return pitch; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::getPosition() const
-{
-       if (status != ChannelStatus::EMPTY   && 
-           status != ChannelStatus::MISSING && 
-           status != ChannelStatus::OFF)
-               return tracker - begin;
-       else
-               return -1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-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() const
-{
-       return boost;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::empty()
-{
-       status     = ChannelStatus::EMPTY;
-       begin      = 0;
-       end        = 0;
-       tracker    = 0;
-       volume     = G_DEFAULT_VOL;
-       boost      = G_DEFAULT_BOOST;
-       hasActions = false;
-       wave.reset(nullptr);
-       sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::pushWave(std::unique_ptr<Wave>&& w)
-{
-       status = ChannelStatus::OFF;
-       wave   = std::move(w);
-       begin  = 0;
-       end    = wave->getSize() - 1;
-       sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::canInputRec() const
-{
-       return wave == nullptr && armed;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBuffer(AudioBuffer& dest, int start, int offset)
-{
-       if (pitch == 1.0) return fillBufferCopy(dest, start, offset);
-       else              return fillBufferResampled(dest, start, offset);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBufferResampled(AudioBuffer& dest, int start, int offset)
-{
-       rsmp_data.data_in       = wave->getFrame(start);        // Source data
-       rsmp_data.input_frames  = end - start;                  // How many readable frames
-       rsmp_data.data_out      = dest[offset];                 // Destination (processed data)
-       rsmp_data.output_frames = dest.countFrames() - offset;  // How many frames to process
-       rsmp_data.end_of_input  = false;
-       rsmp_data.src_ratio     = 1 / pitch;
-
-       src_process(rsmp_state, &rsmp_data);
-
-       return rsmp_data.input_frames_used; // Returns used frames
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBufferCopy(AudioBuffer& dest, int start, int offset)
-{
-       int used = dest.countFrames() - offset;
-       if (used + start > wave->getSize())
-               used = wave->getSize() - start;
-
-       dest.copyData(wave->getFrame(start), used, offset);
-
-       return used;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::isAnyLoopMode() const
-{
-       return mode == ChannelMode::LOOP_BASIC || mode == ChannelMode::LOOP_ONCE || 
-              mode == ChannelMode::LOOP_REPEAT || mode == ChannelMode::LOOP_ONCE_BAR;
-}
-
-
-bool SampleChannel::isAnySingleMode() const
-{
-       return !isAnyLoopMode();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::isOnLastFrame() const
-{
-       return tracker >= end;
-}
-
-}} // giada::m::
diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h
deleted file mode 100644 (file)
index c2b2dd1..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_H
-#define G_SAMPLE_CHANNEL_H
-
-
-#include <memory>
-#include <functional>
-#include <samplerate.h>
-#include "types.h"
-#include "channel.h"
-
-
-class Wave;
-
-
-namespace giada {
-namespace m 
-{
-class SampleChannel : public Channel
-{
-public:
-
-       SampleChannel(bool inputMonitor, int bufferSize);
-       ~SampleChannel();
-
-       void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
-       void prepareBuffer(bool running) override;
-       void parseEvents(mixer::FrameEvents fe) override;
-       void process(AudioBuffer& out, const AudioBuffer& in, bool audible, bool running) override;
-       void readPatch(const std::string& basePath, const patch::channel_t& pch) override;
-       void writePatch(int i, bool isProject) override;
-
-       void start(int frame, bool doQuantize, int velocity) override;
-       void stop() override;
-       void kill(int frame) override;
-       bool recordStart(bool canQuantize) override;
-       bool recordKill() override;
-       void recordStop() override;
-       void setMute(bool value) override;
-       void setSolo(bool value) override;
-       void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override;
-       void stopReadingActions(bool running, bool treatRecsAsLoops, 
-               bool recsStopOnChanHalt) override;
-       void empty() override;
-       void stopBySeq(bool chansStopOnSeqHalt) override;
-       void rewindBySeq() override;
-       void stopInputRec(int globalFrame) override;
-       bool canInputRec() const override;
-       bool hasLogicalData() const override;
-       bool hasEditedData() const override;
-       bool hasData() const override;
-
-       float getBoost() const; 
-       int   getBegin() const;
-       int   getEnd() const;
-       float getPitch() const;
-       bool isAnyLoopMode() const;
-       bool isAnySingleMode() const;
-       bool isOnLastFrame() const;
-
-       /* getPosition
-       Returns the position of an active sample. If EMPTY o MISSING returns -1. */
-
-       int getPosition() const;
-
-       /* fillBuffer
-       Fills 'dest' buffer at point 'offset' with Wave data taken from 'start'. 
-       Returns how many frames have been used from the original Wave data. It also
-       resamples data if pitch != 1.0f. */
-
-       int fillBuffer(AudioBuffer& dest, int start, int offset);
-
-       /* pushWave
-       Adds a new wave to this channel. */
-
-       void pushWave(std::unique_ptr<Wave>&& w);
-
-       void setPitch(float v);
-       void setBegin(int f);
-       void setEnd(int f);
-       void setBoost(float v);
-
-       void setReadActions(bool v, bool recsStopOnChanHalt);
-
-       /* onPreviewEnd
-       A callback fired when audio preview ends. */
-
-       std::function<void()> onPreviewEnd;
-
-       /* bufferPreview
-       Extra buffer for audio preview. */
-
-       AudioBuffer bufferPreview;
-       
-       ChannelMode mode;
-       
-       std::unique_ptr<Wave> wave;
-       int   tracker;         // chan position
-       int   trackerPreview;  // chan position for audio preview
-       int   shift;
-       bool  quantizing;      // quantization in progress
-       bool  inputMonitor;  
-       float boost;
-       float pitch;
-
-       /* begin, end
-       Begin/end point to read wave data from/to. */
-
-       int begin;
-       int end;
-
-       /* midi stuff */
-
-       bool     midiInVeloAsVol;
-       uint32_t midiInReadActions;
-       uint32_t midiInPitch;
-       
-private:
-
-       /* rsmp_state, rsmp_data
-       Structs from libsamplerate. */
-
-       SRC_STATE* rsmp_state;
-       SRC_DATA   rsmp_data;
-
-       int fillBufferResampled(AudioBuffer& dest, int start, int offset);
-       int fillBufferCopy     (AudioBuffer& dest, int start, int offset);
-};
-
-}} // giada::m::
-
-
-#endif
diff --git a/src/core/sampleChannelProc.cpp b/src/core/sampleChannelProc.cpp
deleted file mode 100644 (file)
index 4612f5b..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 "../utils/math.h"
-#include "const.h"
-#include "pluginHost.h"
-#include "sampleChannel.h"
-#include "sampleChannelProc.h"
-#include "mixerHandler.h"
-
-
-namespace giada {
-namespace m {
-namespace sampleChannelProc
-{
-namespace
-{
-void rewind_(SampleChannel* ch, int localFrame)
-{
-       ch->tracker    = ch->begin;
-       ch->quantizing = false;  // No more quantization now
-
-       /* On rewind, if channel is playing fill again buffer to create something like 
-       this:
-                   v-------------- localFrame
-               [old data-----]*[new data--] */
-
-       if (localFrame > 0 && ch->isPlaying())
-               ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame); 
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* quantize
-Starts channel according to quantizer. */
-
-void quantize_(SampleChannel* ch, int localFrame, bool quantoPassed)
-{
-       /* Skip if LOOP_ANY, not in quantizer-wait mode or still waiting for the 
-       quantization time to end. */
-
-       if (ch->isAnyLoopMode() || !ch->quantizing || !quantoPassed)
-               return;
-
-       switch (ch->status) {
-               case ChannelStatus::OFF:
-                       ch->status   = ChannelStatus::PLAY;
-                       ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame);
-                       ch->sendMidiLstatus();
-                       // ch->quantizing = false is set by sampleChannelRec::quantize()
-                       break;
-
-               default:
-                       rewind_(ch, localFrame);
-                       break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onBar
-Things to do when the sequencer is on a bar. */
-
-void onBar_(SampleChannel* ch, int localFrame)
-{
-       switch (ch->status) {
-               case ChannelStatus::PLAY:
-                       if (ch->mode == ChannelMode::LOOP_REPEAT)
-                               rewind_(ch, localFrame);
-                       break;
-
-               case ChannelStatus::WAIT:
-                       if (ch->mode == ChannelMode::LOOP_ONCE_BAR) {
-                               ch->status   = ChannelStatus::PLAY;
-                               ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame);
-                               ch->sendMidiLstatus();
-                       }
-                       break;
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onFirstBeat
-Things to do when the sequencer is on the first beat. */
-
-void onFirstBeat_(SampleChannel* ch, int localFrame)
-{
-       if (!ch->hasData())
-               return;
-
-       switch (ch->status) {
-               case ChannelStatus::PLAY: 
-                       if (ch->isAnyLoopMode())
-                               rewind_(ch, localFrame);
-                       break;
-
-               case ChannelStatus::WAIT:
-                       ch->status   = ChannelStatus::PLAY;
-                       ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame);                     
-                       ch->sendMidiLstatus();
-                       break;
-
-               case ChannelStatus::ENDING: 
-                       if (ch->isAnyLoopMode())
-                               kill(ch, localFrame);
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onLastFrame
-Things to do when the sample has reached the end (i.e. last frame). Called by
-prepareBuffer(). */
-
-void onLastFrame_(SampleChannel* ch, int localFrame, bool running)
-{
-       switch (ch->status) {
-               case ChannelStatus::PLAY:
-                       /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for
-                        SINGLE_ENDLESS, which runs forever unless it's in ENDING mode. */
-                       if ((ch->mode == ChannelMode::SINGLE_BASIC || 
-                                  ch->mode == ChannelMode::SINGLE_PRESS ||
-                                  ch->mode == ChannelMode::SINGLE_RETRIG) || 
-                                 (ch->isAnyLoopMode() && !running))
-                               ch->status = ChannelStatus::OFF;
-                       ch->sendMidiLstatus();
-                       break;                  
-
-               case ChannelStatus::ENDING:
-                       /* LOOP_ONCE or LOOP_ONCE_BAR: if ending (i.e. the user requested their
-                       termination), stop 'em. Let them wait otherwise. */
-                       if (ch->mode == ChannelMode::LOOP_ONCE ||
-                           ch->mode == ChannelMode::LOOP_ONCE_BAR)
-                               ch->status = ChannelStatus::WAIT;
-                       else {
-                               ch->status = ChannelStatus::OFF;
-                               ch->sendMidiLstatus();
-                       }
-                       break;
-
-               default: break;
-       }
-       rewind_(ch, localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processData_(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in, 
-       bool running)
-{
-       assert(out.countSamples() == ch->buffer.countSamples());
-       if (in.isAllocd())
-               assert(in.countSamples() == ch->buffer.countSamples());
-
-       /* If armed and input buffer is not empty (i.e. input device available) and
-       input monitor is on, copy input buffer to channel buffer: this enables the 
-       input monitoring. The channel buffer will be overwritten later on by 
-       pluginHost::processStack, so that you would record "clean" audio 
-       (i.e. not plugin-processed). */
-
-       if (ch->armed && in.isAllocd() && ch->inputMonitor) {
-               for (int i=0; i<ch->buffer.countFrames(); i++)
-                       for (int j=0; j<ch->buffer.countChannels(); j++)
-                               ch->buffer[i][j] += in[i][j];   // add, don't overwrite
-       }
-
-#ifdef WITH_VST
-       pluginHost::processStack(ch->buffer, pluginHost::StackType::CHANNEL, ch);
-#endif
-
-       for (int i=0; i<out.countFrames(); i++) {
-               if (running)
-                       ch->calcVolumeEnvelope();
-               if (!ch->mute)
-                       for (int j=0; j<out.countChannels(); j++)
-                               out[i][j] += ch->buffer[i][j] * ch->volume * ch->volume_i * ch->calcPanning(j) * ch->boost;     
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processPreview_(SampleChannel* ch, m::AudioBuffer& out)
-{
-       ch->bufferPreview.clear();
-
-       /* If the tracker exceedes the end point and preview is looped, split the 
-       rendering as in SampleChannel::reset(). */
-
-       if (ch->trackerPreview + ch->bufferPreview.countFrames() >= ch->end) {
-               int offset = ch->end - ch->trackerPreview;
-               ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
-               ch->trackerPreview  = ch->begin;
-               if (ch->previewMode == PreviewMode::LOOP)
-                       ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->begin, offset);
-               else
-               if (ch->previewMode == PreviewMode::NORMAL) {
-                       ch->previewMode = PreviewMode::NONE;
-                       if (ch->onPreviewEnd)
-                               ch->onPreviewEnd();
-               }
-       }
-       else
-               ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
-
-       for (int i=0; i<out.countFrames(); i++)
-               for (int j=0; j<out.countChannels(); j++)
-                       out[i][j] += ch->bufferPreview[i][j] * ch->volume * ch->calcPanning(j) * ch->boost;     
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void kill(SampleChannel* ch, int localFrame)
-{
-       switch (ch->status) {
-               case ChannelStatus::WAIT:
-               case ChannelStatus::PLAY:
-               case ChannelStatus::ENDING:
-                       /*  Clear data in range [localFrame, (buffer.size)) if the kill event 
-                       occurs in the middle of the buffer. */
-                       if (localFrame != 0)
-                               ch->buffer.clear(localFrame);
-                       ch->status = ChannelStatus::OFF;
-                       ch->sendMidiLstatus();
-                       rewind_(ch, localFrame);
-                       break;
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stop(SampleChannel* ch)
-{
-       switch (ch->status) {
-               case ChannelStatus::PLAY:
-                       if (ch->mode == ChannelMode::SINGLE_PRESS)
-                               kill(ch, 0);
-                       break;
-
-               default:
-                       /* If quantizing, stop a SINGLE_PRESS immediately. */
-                       if (ch->mode == ChannelMode::SINGLE_PRESS && ch->quantizing)
-                               ch->quantizing = false; 
-                       break;          
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopInputRec(SampleChannel* ch, int globalFrame)
-{
-       /* 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 global frame, not at the 
-       next first beat. */
-       if (ch->isAnyLoopMode() && ch->status == ChannelStatus::OFF && ch->armed) {
-               ch->status  = ChannelStatus::PLAY;
-               ch->tracker = globalFrame;      
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt)
-{
-       switch (ch->status) {
-               case ChannelStatus::WAIT:
-                       /* Loop-mode channels in wait status get stopped right away. */
-                       if (ch->isAnyLoopMode())
-                               ch->status = ChannelStatus::OFF;
-                       break;
-
-               case ChannelStatus::PLAY:
-                       /* Kill samples if a) chansStopOnSeqHalt == true (run the sample to end 
-                       otherwise); b) when a channel is reading (and playing) actions. */
-                       if (chansStopOnSeqHalt)
-                               if (ch->isAnyLoopMode() || ch->isReadingActions())
-                                       kill(ch, 0);
-                       break;
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindBySeq(SampleChannel* ch)
-{
-       /* Rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode. Rewind by 
-       sequencer is a user-generated event, it always occurs on local frame 0. */
-
-       if (ch->hasData()) {
-               if ((ch->isAnyLoopMode()) || (ch->recStatus == ChannelStatus::PLAY && (ch->isAnySingleMode())))
-                       rewind_(ch, 0);
-       }       
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setMute(SampleChannel* ch, bool value)
-{
-       ch->mute = value;
-
-       // This is for processing playing_inaudible
-       ch->sendMidiLstatus();  
-
-       ch->sendMidiLmute();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setSolo(SampleChannel* ch, bool value)
-{
-       ch->solo = value;
-       m::mh::updateSoloCount();
-
-       // This is for processing playing_inaudible
-       for (Channel* channel : mixer::channels)                
-               channel->sendMidiLstatus();
-
-       ch->sendMidiLsolo();
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity)
-{
-       /* For one-shot modes, velocity drives the internal volume. */
-       if (velocity != 0) {
-               if (ch->isAnySingleMode() && ch->midiInVeloAsVol)
-                       ch->volume_i = u::math::map<int, float>(velocity, 0, G_MAX_VELOCITY, 0.0, 1.0);         
-       }
-
-       switch (ch->status)     {
-               case ChannelStatus::OFF:
-                       if (ch->isAnyLoopMode()) {
-                               ch->status = ChannelStatus::WAIT;
-                               ch->sendMidiLstatus();
-                       }
-                       else {
-                               if (doQuantize)
-                                       ch->quantizing = true;
-                               else {
-                                       ch->status = ChannelStatus::PLAY;
-                                       ch->sendMidiLstatus();
-                               }
-                       }
-                       break;
-
-               case ChannelStatus::PLAY:
-                       if (ch->mode == ChannelMode::SINGLE_RETRIG) {
-                               if (doQuantize)
-                                       ch->quantizing = true;
-                               else
-                                       rewind_(ch, localFrame);
-                       }
-                       else
-                       if (ch->isAnyLoopMode() || ch->mode == ChannelMode::SINGLE_ENDLESS) {
-                               ch->status = ChannelStatus::ENDING;
-                               ch->sendMidiLstatus();
-                       }
-                       else
-                       if (ch->mode == ChannelMode::SINGLE_BASIC) {
-                               rewind_(ch, localFrame);
-                               ch->status = ChannelStatus::OFF;
-                               ch->sendMidiLstatus();
-                       }
-                       break;
-
-               case ChannelStatus::WAIT:
-                       ch->status = ChannelStatus::OFF;
-                       ch->sendMidiLstatus();
-                       break;
-
-               case ChannelStatus::ENDING:
-                       ch->status = ChannelStatus::PLAY;
-                       ch->sendMidiLstatus();
-                       break;
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void prepareBuffer(SampleChannel* ch, bool running)
-{
-       namespace um = u::math;
-
-       ch->buffer.clear();
-
-       if (!ch->hasData() || !ch->isPlaying())
-               return;
-
-       Frame framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, 0);
-       ch->tracker += framesUsed;
-
-       /* The "framesUsed * (1 / ch->pitch)" operation might yield results greater
-       than the current buffer size. So clamping is mandatory. */
-
-       if (ch->isOnLastFrame()) {
-               Frame min  = 0; 
-               Frame max  = ch->buffer.countFrames() - 1;
-               framesUsed = static_cast<Frame>(framesUsed * (1 / ch->pitch));
-               onLastFrame_(ch, um::bound(framesUsed, min, max, max), running);
-       }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
-{
-       quantize_(ch, fe.frameLocal, fe.quantoPassed);
-       if (fe.onBar)
-               onBar_(ch, fe.frameLocal);
-       if (fe.onFirstBeat)
-               onFirstBeat_(ch, fe.frameLocal);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void process(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in,
-       bool audible, bool running)
-{
-       if (audible)
-               processData_(ch, out, in, running);
-
-       if (ch->isPreview())
-               processPreview_(ch, out);
-}
-}}};
diff --git a/src/core/sampleChannelProc.h b/src/core/sampleChannelProc.h
deleted file mode 100644 (file)
index 36d39e2..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_PROC_H
-#define G_SAMPLE_CHANNEL_PROC_H
-
-
-#include "mixer.h"
-#include "audioBuffer.h"
-#include "types.h"
-
-
-namespace giada {
-namespace m 
-{
-class SampleChannel;
-
-namespace sampleChannelProc
-{
-/**/
-void prepareBuffer(SampleChannel* ch, bool running);
-
-/* parseEvents
-Parses events gathered by Mixer::masterPlay(). */
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents ev);
-
-/**/
-void process(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in, 
-    bool audible, bool running);
-
-/* kill
-Stops a channel abruptly. */
-
-void kill(SampleChannel* ch, int localFrame);
-
-/* stop
-Stops a channel normally (via key or MIDI). */
-
-void stop(SampleChannel* ch);
-
-/* stopInputRec
-Prepare a channel for playing when the input recording is done. */
-
-void stopInputRec(SampleChannel* ch, int globalFrame);
-
-/* stopBySeq
-Stops a channel when the stop button on main transport is pressed. */
-
-void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt);
-
-/* rewind
-Rewinds channel when rewind button on main transport is pressed. */
-
-void rewindBySeq(SampleChannel* ch);
-
-/* start
-Starts a channel. doQuantize = false (don't quantize) when Mixer is reading 
-actions from Recorder. */
-
-void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity);
-
-void setMute(SampleChannel* ch, bool value);
-void setSolo(SampleChannel* ch, bool value);
-}}};
-
-
-#endif
diff --git a/src/core/sampleChannelRec.cpp b/src/core/sampleChannelRec.cpp
deleted file mode 100644 (file)
index a79f012..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 "../utils/math.h"
-#include "recorder.h"
-#include "recorderHandler.h"
-#include "const.h"
-#include "conf.h"
-#include "clock.h"
-#include "action.h"
-#include "kernelAudio.h"
-#include "sampleChannel.h"
-#include "sampleChannelRec.h"
-
-
-namespace giada {
-namespace m {
-namespace sampleChannelRec
-{
-namespace
-{
-/* onFirstBeat
-Things to do when the sequencer is on the first beat. */
-
-void onFirstBeat_(SampleChannel* ch, bool recsStopOnChanHalt)
-{
-       if (ch->wave == nullptr)
-               return;
-
-       switch (ch->recStatus) {
-               case ChannelStatus::ENDING:
-                       ch->recStatus = ChannelStatus::OFF;
-                       setReadActions(ch, false, recsStopOnChanHalt);  // rec stop
-                       break;
-
-               case ChannelStatus::WAIT:
-                       ch->recStatus = ChannelStatus::PLAY;
-                       setReadActions(ch, true, recsStopOnChanHalt);   // rec start                    
-                       break;
-
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recorderCanRec_(SampleChannel* ch)
-{
-       /* Can record on a channel if:
-               - recorder is on
-               - mixer is running
-               - mixer is not recording a take somewhere
-               - channel is MIDI or SAMPLE type with data in it  */
-
-       return recorder::isActive() && clock::isRunning() && !mixer::recording && 
-               (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData()));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* calcVolumeEnv
-Computes any changes in volume done via envelope tool. */
-
-void calcVolumeEnv_(SampleChannel* ch, const Action* a1)
-{
-       assert(a1 != nullptr);
-       assert(a1->next != nullptr);
-
-       const Action* a2 = a1->next;
-
-       double vf1 = u::math::map<int, double>(a1->event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
-       double vf2 = u::math::map<int, double>(a2->event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
-
-       ch->volume_i = vf1;
-       ch->volume_d = a2->frame == a1->frame ? 0 : (vf2 - vf1) / (a2->frame - a1->frame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void parseAction_(SampleChannel* ch, const Action* a, int localFrame, int globalFrame)
-{
-       if (!ch->readActions)
-               return;
-
-       switch (a->event.getStatus()) {
-               case MidiEvent::NOTE_ON:
-                       if (ch->isAnySingleMode()) {
-                               ch->start(localFrame, false, 0);
-                               /* This is not a user-generated event, so fill the first chunk of buffer. 
-                               Then, sampleChannelProc::prepareBuffer will take care of filling the
-                               subsequent buffers from the next cycle on. */
-                               if (ch->status == ChannelStatus::PLAY)
-                                       ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame);
-                       }
-                       break;
-               case MidiEvent::NOTE_OFF:
-                       if (ch->isAnySingleMode())
-                               ch->stop();
-                       break;
-               case MidiEvent::NOTE_KILL:
-                       if (ch->isAnySingleMode())
-                               ch->kill(localFrame);
-                       break;
-               case MidiEvent::ENVELOPE:
-                       calcVolumeEnv_(ch, a);
-                       break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void recordKeyPressAction_(SampleChannel* ch)
-{
-       if (!recorderCanRec_(ch))
-               return;
-
-       /* Disable reading actions while recording SINGLE_PRESS mode. Don't let 
-       existing actions interfere with the current one being recorded. */
-
-       if (ch->mode == ChannelMode::SINGLE_PRESS)
-               ch->readActions = false;
-       
-       recorderHandler::liveRec(ch->index, MidiEvent(MidiEvent::NOTE_ON, 0, 0));
-       ch->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void quantize_(SampleChannel* ch, bool quantoPassed)
-{
-       /* Skip if in loop mode or not in a quantization stage. Otherwise the 
-       quantization wait has expired: record the keypress.  */
-
-       if (!ch->isAnyLoopMode() && ch->quantizing && quantoPassed && ch->status == ChannelStatus::PLAY) {
-               ch->quantizing = false;
-               recordKeyPressAction_(ch);
-       }
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
-{
-       quantize_(ch, fe.quantoPassed);
-       if (fe.onFirstBeat)
-               onFirstBeat_(ch, conf::recsStopOnChanHalt);
-       for (const Action* action : fe.actions)
-               if (action->channel == ch->index)
-                       parseAction_(ch, action, fe.frameLocal, fe.frameGlobal);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recordStart(SampleChannel* ch, bool canQuantize)
-{
-       /* Record a 'start' event if the quantizer is off, otherwise let mixer to 
-       handle it when a quantoWait has passed (see quantize_()). Also skip if 
-       channel is in any loop mode, where KEYPRESS and KEYREL are meaningless. */
-
-       if (!canQuantize && !ch->isAnyLoopMode() && recorderCanRec_(ch))
-               recordKeyPressAction_(ch);
-       return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recordKill(SampleChannel* ch)
-{
-       /* Don't record NOTE_KILL actions for LOOP channels. */
-       if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) {
-               recorder::rec(ch->index, clock::getCurrentFrame(), MidiEvent(MidiEvent::NOTE_KILL, 0, 0));
-               ch->hasActions = true;
-       }
-       return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void recordStop(SampleChannel* ch)
-{
-       /* Record a stop event only if channel is SINGLE_PRESS. For any other mode 
-       the stop event is meaningless. */
-       if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS)
-               recorderHandler::liveRec(ch->index, MidiEvent(MidiEvent::NOTE_OFF, 0, 0));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt)
-{
-       ch->readActions = v;
-       if (!ch->readActions && recsStopOnChanHalt)
-               ch->kill(0); // FIXME - wrong frame value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, bool recsStopOnChanHalt)
-{
-       if (treatRecsAsLoops)
-               ch->recStatus = ChannelStatus::WAIT;
-       else
-               setReadActions(ch, true, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopReadingActions(SampleChannel* ch, bool isClockRunning, bool treatRecsAsLoops, 
-               bool recsStopOnChanHalt)
-{
-       /* First of all, if the clock 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 (!isClockRunning) {
-               ch->recStatus = ChannelStatus::OFF;
-               setReadActions(ch, false, false);
-       }
-       else
-       if (ch->recStatus == ChannelStatus::WAIT)
-               ch->recStatus = ChannelStatus::OFF;
-       else
-       if (ch->recStatus == ChannelStatus::ENDING)
-               ch->recStatus = ChannelStatus::PLAY;
-       else
-       if (treatRecsAsLoops)
-               ch->recStatus = ChannelStatus::ENDING;
-       else
-               setReadActions(ch, false, recsStopOnChanHalt);
-}
-}}};
\ No newline at end of file
diff --git a/src/core/sampleChannelRec.h b/src/core/sampleChannelRec.h
deleted file mode 100644 (file)
index ef64e33..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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_SAMPLE_CHANNEL_REC_H
-#define G_SAMPLE_CHANNEL_REC_H
-
-
-
-
-namespace giada {
-namespace m 
-{
-class SampleChannel;
-
-namespace sampleChannelRec
-{
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe);
-
-/* recordStart
-Records a 'start' action if capable of. Returns true if a start() call can
-be performed. */
-
-bool recordStart(SampleChannel* ch, bool doQuantize);
-
-/* recordKill
-Records a 'kill' action if capable of. Returns true if a kill() call can
-be performed. */
-
-bool recordKill(SampleChannel* ch);
-
-/* recordStop
-Ends overdub mode SINGLE_PRESS channels. */
-
-void recordStop(SampleChannel* ch);
-
-/* setReadActions
-If enabled (v == true), Recorder will read actions from channel 'ch'. If 
-recsStopOnChanHalt == true and v == false, will also kill the channel. */
-
-void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt);
-
-void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, 
-       bool recsStopOnChanHalt);
-void stopReadingActions(SampleChannel* ch, bool isClockRunning, 
-       bool treatRecsAsLoops, bool recsStopOnChanHalt);
-}}};
-
-
-#endif
\ No newline at end of file
diff --git a/src/core/storager.cpp b/src/core/storager.cpp
deleted file mode 100644 (file)
index eb36fcf..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 (!jObject) {
-               gu_log("[storager::setString] key '%s' not found, using default value\n", key);
-               output = "";
-               return true;
-       }
-       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
deleted file mode 100644 (file)
index 52500a2..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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 15efc16d0e8714a92f3e20e59482b9b7b0a4a584..0d05ae8d56aa0e184261129f94899858db5a4c85 100644 (file)
 
 namespace giada
 {
+using ID    = int;
 using Pixel = int;
 using Frame = int;
 
 enum class ClockStatus { STOPPED, WAITING, RUNNING };
 
-enum class ChannelType : int { SAMPLE = 1, MIDI };
+enum class ChannelType : int { SAMPLE = 1, MIDI, MASTER, PREVIEW };
 
 enum class ChannelStatus : int
 {
@@ -56,4 +57,4 @@ enum class EventType : int { AUTO = 0, MANUAL };
 };
 
 
-#endif
\ No newline at end of file
+#endif
index 7841338c53d0c4a968333becda42137c9f69462f..bc7f9adff884261c7b50a39d65d921cca46b7890 100644 (file)
 
 #include <cassert>
 #include <cstring>  // memcpy
-#include "../utils/fs.h"
-#include "../utils/log.h"
-#include "../utils/string.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
 #include "const.h"
 #include "wave.h"
 
 
-using std::string;
-using namespace giada;
-
-
-Wave::Wave()
-: m_rate   (0),
+namespace giada {
+namespace m 
+{
+Wave::Wave(ID id)
+: id       (id),
+  m_rate   (0),
   m_bits   (0),
   m_logical(false),
   m_edited (false) 
@@ -60,11 +60,12 @@ float* Wave::operator [](int offset) const
 
 
 Wave::Wave(const Wave& other)
-:      m_rate    (other.m_rate),
-       m_bits    (other.m_bits),       
-       m_logical (true),   // A cloned wave does not exist on disk
-       m_edited  (false),
-       m_path    (other.m_path)
+: id        (other.id), 
+  m_rate    (other.m_rate),
+  m_bits    (other.m_bits),    
+  m_logical (false),
+  m_edited  (false),
+  m_path    (other.m_path)
 {
        buffer.alloc(other.getSize(), other.getChannels());
        buffer.copyData(other.getFrame(0), other.getSize());
@@ -86,9 +87,9 @@ void Wave::alloc(int size, int channels, int rate, int bits, const std::string&
 /* -------------------------------------------------------------------------- */
 
 
-string Wave::getBasename(bool ext) const
+std::string Wave::getBasename(bool ext) const
 {
-       return ext ? gu_basename(m_path) : gu_stripExt(gu_basename(m_path));
+       return ext ? u::fs::basename(m_path) : u::fs::stripExt(u::fs::basename(m_path));
 }
 
 
@@ -118,7 +119,7 @@ int Wave::getDuration() const
 
 std::string Wave::getExtension() const
 {
-       return gu_getExt(m_path);
+       return u::fs::getExt(m_path);
 }
 
 
@@ -142,19 +143,19 @@ void Wave::setEdited(bool e)  { m_edited = e; }
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::setPath(const string& p, int id) 
+void Wave::setPath(const std::string& p, int id) 
 { 
        if (id == -1)
                m_path = p; 
        else 
-               m_path = gu_stripExt(p) + "-" + u::string::iToString(id) + "." + gu_getExt(p);
+               m_path = u::fs::stripExt(p) + "-" + std::to_string(id) + "." + u::fs::getExt(p);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::copyData(float* data, int frames, int offset)
+void Wave::copyData(const float* data, int frames, int offset)
 {
        buffer.copyData(data, frames, offset);
 }
@@ -163,7 +164,9 @@ void Wave::copyData(float* data, int frames, int offset)
 /* -------------------------------------------------------------------------- */
 
 
-void Wave::moveData(giada::m::AudioBuffer& b)
+void Wave::moveData(AudioBuffer& b)
 {
        buffer.moveData(b);
-}
\ No newline at end of file
+}
+
+}}; // giada::m::
index 50bedb8aa448c661a1802f45339990156f318f2e..de4f52e2dcab726064eb0799f5e23154611656ff 100644 (file)
 #define G_WAVE_H
 
 
-#include <sndfile.h>
 #include <string>
-#include "const.h"
-#include "audioBuffer.h"
+#include "core/audioBuffer.h"
+#include "core/types.h"
 
 
+namespace giada {
+namespace m 
+{
 class Wave
 {
 public:
 
-       Wave();
+       Wave(ID id);
        Wave(const Wave& other);
 
        float* operator [](int offset) const;
@@ -73,25 +75,29 @@ public:
        /* moveData
        Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */
 
-       void moveData(giada::m::AudioBuffer& b); 
+       void moveData(AudioBuffer& b); 
        
        /* copyData
        Copies 'frames' frames from the new 'data' into m_data, starting from frame 
        'offset'. It takes for granted that the new data contains the same number of 
        channels than m_channels. */
 
-       void copyData(float* data, int frames, int offset=0);
+       void copyData(const float* data, int frames, int offset=0);
 
        void alloc(int size, int channels, int rate, int bits, const std::string& path);
 
+       ID id;
+
 private:
 
-       giada::m::AudioBuffer buffer;
+       AudioBuffer buffer;
        int m_rate;
        int m_bits;
        bool m_logical;     // memory only (a take)
        bool m_edited;      // edited via editor
        std::string m_path; // E.g. /path/to/my/sample.wav
 };
+}}; // giada::m::
+
 
 #endif
index 18cf80c4abb8426d3d34bfde399622b8d7b55f15..a5e71b2834568266f386558cd315fdf93b789cf0 100644 (file)
@@ -28,7 +28,9 @@
 #include <cmath>
 #include <cassert>
 #include <algorithm>
-#include "../utils/log.h"
+#include "core/model/model.h"
+#include "utils/log.h"
+#include "const.h"
 #include "wave.h"
 #include "waveFx.h"
 
@@ -39,7 +41,7 @@ namespace wfx
 {
 namespace
 {
-void fadeFrame(Wave& w, int i, float val)
+void fadeFrame_(Wave& w, int i, float val)
 {
        for (int j=0; j<w.getChannels(); j++)
                w[i][j] *= val;
@@ -49,7 +51,7 @@ void fadeFrame(Wave& w, int i, float val)
 /* -------------------------------------------------------------------------- */
 
 
-float getPeak(const Wave& w, int a, int b)
+float getPeak_(const Wave& w, int a, int b)
 {
        float peak = 0.0f;
        float abs  = 0.0f;
@@ -69,34 +71,20 @@ float getPeak(const Wave& w, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-float normalizeSoft(const Wave& w)
+void normalizeHard(ID waveId, int a, int b)
 {
-       float peak = getPeak(w, 0, w.getSize());
-
-       /* peak == 0.0f: don't normalize the silence
-        * peak > 1.0f: don't reduce the amplitude, just leave it alone */
-
-       if (peak == 0.0f || peak > 1.0f)
-               return 1.0f;
-
-       return 1.0f / peak;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void normalizeHard(Wave& w, int a, int b)
-{
-       float peak = getPeak(w, a, b);
-       if (peak == 0.0f || peak > 1.0f)  // as in ::normalizeSoft
-               return;
-
-       for (int i=a; i<b; i++) {
-               for (int j=0; j<w.getChannels(); j++)
-                       w[i][j] = w[i][j] * (1.0f / peak);
-       }
-       w.setEdited(true);
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               float peak = getPeak_(w, a, b);
+               if (peak == 0.0f || peak > 1.0f)  // as in ::normalizeSoft
+                       return;
+
+               for (int i=a; i<b; i++) {
+                       for (int j=0; j<w.getChannels(); j++)
+                               w[i][j] = w[i][j] * (1.0f / peak);
+               }
+               w.setEdited(true);
+       });
 }
 
 
@@ -124,172 +112,182 @@ int monoToStereo(Wave& w)
 /* -------------------------------------------------------------------------- */
 
 
-void silence(Wave& w, int a, int b)
+void silence(ID waveId, int a, int b)
 {
-       gu_log("[wfx::silence] silencing from %d to %d\n", a, b);
-
-       for (int i=a; i<b; i++) {
-               for (int j=0; j<w.getChannels(); j++)   
-                       w[i][j] = 0.0f;
-       }
-
-       w.setEdited(true);
+       u::log::print("[wfx::silence] silencing from %d to %d\n", a, b);
+       
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               for (int i=a; i<b; i++)
+                       for (int j=0; j<w.getChannels(); j++)   
+                               w[i][j] = 0.0f;
+               w.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int cut(Wave& w, int a, int b)
+void cut(ID waveId, int a, int b)
 {
-       if (a < 0) a = 0;
-       if (b > w.getSize()) b = w.getSize();
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               if (a < 0) a = 0;
+               if (b > w.getSize()) b = w.getSize();
 
-       /* Create a new temp wave and copy there the original one, skipping the a-b 
-       range. */
+               /* Create a new temp wave and copy there the original one, skipping the a-b 
+               range. */
 
-       int newSize = w.getSize() - (b - a);
+               int newSize = w.getSize() - (b - a);
 
-       AudioBuffer newData;
-       newData.alloc(newSize, w.getChannels());
+               AudioBuffer newData;
+               newData.alloc(newSize, w.getChannels());
 
-       gu_log("[wfx::cut] cutting from %d to %d\n", a, b);
+               u::log::print("[wfx::cut] cutting from %d to %d\n", a, b);
 
-       for (int i=0, k=0; i<w.getSize(); i++) {
-               if (i < a || i >= b) {
-                       for (int j=0; j<w.getChannels(); j++)   
-                               newData[k][j] = w[i][j];
-                       k++;
+               for (int i=0, k=0; i<w.getSize(); i++) {
+                       if (i < a || i >= b) {
+                               for (int j=0; j<w.getChannels(); j++)   
+                                       newData[k][j] = w[i][j];
+                               k++;
+                       }
                }
-       }
-
-       w.moveData(newData);
-       w.setEdited(true);
 
-       return G_RES_OK;
+               w.moveData(newData);
+               w.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int trim(Wave& w, int a, int b)
+void trim(ID waveId, int a, int b)
 {
-       if (a < 0) a = 0;
-       if (b > w.getSize()) b = w.getSize();
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               if (a < 0) a = 0;
+               if (b > w.getSize()) b = w.getSize();
 
-       int newSize = b - a;
+               int newSize = b - a;
 
-       AudioBuffer newData;
-       newData.alloc(newSize, w.getChannels());
+               AudioBuffer newData;
+               newData.alloc(newSize, w.getChannels());
 
-       gu_log("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
+               u::log::print("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
 
-       for (int i=0; i<newData.countFrames(); i++)
-               for (int j=0; j<newData.countChannels(); j++)
-                       newData[i][j] = w[i+a][j];
+               for (int i=0; i<newData.countFrames(); i++)
+                       for (int j=0; j<newData.countChannels(); j++)
+                               newData[i][j] = w[i+a][j];
 
-       w.moveData(newData);
-       w.setEdited(true);
-
-       return G_RES_OK;
+               w.moveData(newData);
+               w.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int paste(const Wave& src, Wave& des, int a)
+void paste(const Wave& src, ID waveId, int a)
 {
-       assert(src.getChannels() == des.getChannels());
-
-       AudioBuffer newData;
-       newData.alloc(src.getSize() + des.getSize(), des.getChannels());
+       model::onSwap(m::model::waves, waveId, [&](Wave& des)
+       {
+               assert(src.getChannels() == des.getChannels());
 
-       /* |---original data---|///paste data///|---original data---|
-                des[0, a)      src[0, src.size)   des[a, des.size)     */
+               AudioBuffer newData;
+               newData.alloc(src.getSize() + des.getSize(), des.getChannels());
 
-       newData.copyData(des[0], a, 0);
-       newData.copyData(src[0], src.getSize(), a);
-       newData.copyData(des[a], des.getSize() - a, src.getSize() + a);
+               /* |---original data---|///paste data///|---original data---|
+                                des[0, a)      src[0, src.size)   des[a, des.size)     */
 
-       des.moveData(newData);
-       des.setEdited(true);
+               newData.copyData(des[0], a, 0);
+               newData.copyData(src[0], src.getSize(), a);
+               newData.copyData(des[a], des.getSize() - a, src.getSize() + a);
 
-       return G_RES_OK;
+               des.moveData(newData);
+               des.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void fade(Wave& w, int a, int b, int type)
+void fade(ID waveId, int a, int b, int type)
 {
-       gu_log("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
+       u::log::print("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
 
        float m = 0.0f;
        float d = 1.0f / (float) (b - a);
 
-       if (type == FADE_IN)
-               for (int i=a; i<=b; i++, m+=d)
-                       fadeFrame(w, i, m);
-       else
-               for (int i=b; i>=a; i--, m+=d)
-                       fadeFrame(w, i, m);             
-
-  w.setEdited(true);
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               if (type == FADE_IN)
+                       for (int i=a; i<=b; i++, m+=d)
+                               fadeFrame_(w, i, m);
+               else
+                       for (int i=b; i>=a; i--, m+=d)
+                               fadeFrame_(w, i, m);            
+               
+               w.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void smooth(Wave& w, int a, int b)
+void smooth(ID waveId, int a, int b)
 {
        /* Do nothing if fade edges (both of SMOOTH_SIZE samples) are > than selected 
        portion of wave. SMOOTH_SIZE*2 to count both edges. */
 
        if (SMOOTH_SIZE*2 > (b-a)) {
-               gu_log("[wfx::smooth] selection is too small, nothing to do\n");
+               u::log::print("[wfx::smooth] selection is too small, nothing to do\n");
                return;
        }
 
-       fade(w, a, a+SMOOTH_SIZE, FADE_IN);
-       fade(w, b-SMOOTH_SIZE, b, FADE_OUT);
-
-       w.setEdited(true);
+       fade(waveId, a, a+SMOOTH_SIZE, FADE_IN);
+       fade(waveId, b-SMOOTH_SIZE, b, FADE_OUT);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void shift(Wave& w, int offset)
+void shift(ID waveId, int offset)
 {
-       if (offset < 0)
-               offset = (w.getSize() + w.getChannels()) + offset;
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               if (offset < 0)
+                       offset = (w.getSize() + w.getChannels()) + offset;
 
-       float* begin = w.getFrame(0);
-       float* end   = w.getFrame(0) + (w.getSize() * w.getChannels());
+               float* begin = w.getFrame(0);
+               float* end   = w.getFrame(0) + (w.getSize() * w.getChannels());
 
-       std::rotate(begin, end - (offset * w.getChannels()), end);
-       w.setEdited(true);
+               std::rotate(begin, end - (offset * w.getChannels()), end);
+               w.setEdited(true);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void reverse(Wave& w, int a, int b)
+void reverse(ID waveId, int a, int b)
 {
        /* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */
+       model::onSwap(m::model::waves, waveId, [&](Wave& w)
+       {
+               float* begin = w.getFrame(0) + (a * w.getChannels());
+               float* end   = w.getFrame(0) + (b * w.getChannels());
 
-       float* begin = w.getFrame(0) + (a * w.getChannels());
-       float* end   = w.getFrame(0) + (b * w.getChannels());
-
-       std::reverse(begin, end);
+               std::reverse(begin, end);
 
-       w.setEdited(true);
+               w.setEdited(true);
+       });
 }
 
-}}}; // giada::m::wfx::
\ No newline at end of file
+}}}; // giada::m::wfx::
index f5e48b13b2e597bbbca1c0fa06d513179e0ec2ad..742732cd26ca81384aaa17e859ffcf4abdfb82df 100644 (file)
 #define G_WAVE_FX_H
 
 
+namespace giada {
+namespace m 
+{
 class Wave;
 
-
-namespace giada {
-namespace m {
 namespace wfx
 {
 static const int FADE_IN  = 0;
 static const int FADE_OUT = 1;
 static const int SMOOTH_SIZE = 32;
 
-/* normalizeSoft
-Normalizes the wave by returning the dB value for the boost volume. */
+/* monoToStereo
+Converts a 1-channel Wave to a 2-channels wave. It works on a free Wave object,
+not yet added to the RCUList. */
 
-float normalizeSoft(const Wave& w);
+int monoToStereo(Wave& w);
 
 /* normalizeHard
 Normalizes the wave in range a-b by altering values in memory. */
 
-void normalizeHard(Wave& w, int a, int b);
+void normalizeHard(ID waveId, int a, int b);
 
-int monoToStereo(Wave& w);
-void silence(Wave& w, int a, int b);
-int cut(Wave& w, int a, int b);
-int trim(Wave& w, int a, int b);
+void silence(ID waveId, int a, int b);
+void cut(ID waveId, int a, int b);
+void trim(ID waveId, int a, int b);
 
 /* paste
-Pastes Wave 'src' into Wave 'dest', starting from frame 'a'. */
+Pastes Wave 'src' into Wave at 'waveIndex', starting from frame 'a'. */
 
-int paste(const Wave& src, Wave& dest, int a);
+void paste(const Wave& src, ID waveId, int a);
 
 /* fade
 Fades in or fades out selection. Fade In = type 0, Fade Out = type 1 */
 
-void fade(Wave& w, int a, int b, int type);
+void fade(ID waveId, int a, int b, int type);
 
 /* smooth
 Smooth edges of selection. */
 
-void smooth(Wave& w, int a, int b);
+void smooth(ID waveId, int a, int b);
 
 /* reverse
 Flips Wave's data. */
 
-void reverse(Wave& w, int a, int b);
-
-void shift(Wave& w, int offset);
+void reverse(ID waveId, int a, int b);
 
+void shift(ID waveId, int offset);
 }}}; // giada::m::wfx::
 
+
 #endif
index 64cb1d7a333df828e3d9aec6bde8fb29f023df06..6e15f86bbf8be05a490bb35971ff2f477358a8ba 100644 (file)
 #include <cmath>
 #include <sndfile.h>
 #include <samplerate.h>
-#include "../utils/log.h"
-#include "../utils/fs.h"
+#include "utils/log.h"
+#include "utils/fs.h"
 #include "const.h"
+#include "idManager.h"
 #include "wave.h"
+#include "patch.h"
 #include "waveFx.h"
 #include "waveManager.h"
 
 
-using std::string;
-
-
 namespace giada {
 namespace m {
 namespace waveManager
 {
 namespace
 {
-int getBits(SF_INFO& header)
+IdManager waveId_;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int getBits_(const SF_INFO& header)
 {
        if      (header.format & SF_FORMAT_PCM_S8)
                return 8;
@@ -71,10 +76,19 @@ int getBits(SF_INFO& header)
 /* -------------------------------------------------------------------------- */
 
 
-Result createFromFile(const string& path)
+void init()
 {
-       if (path == "" || gu_isDir(path)) {
-               gu_log("[waveManager::create] malformed path (was '%s')\n", path.c_str());
+       waveId_ = IdManager();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Result createFromFile(const std::string& path, ID id)
+{
+       if (path == "" || u::fs::isDir(path)) {
+               u::log::print("[waveManager::create] malformed path (was '%s')\n", path.c_str());
                return { G_RES_ERR_NO_DATA };
        }
 
@@ -85,27 +99,29 @@ Result createFromFile(const string& path)
        SNDFILE* fileIn = sf_open(path.c_str(), SFM_READ, &header);
 
        if (fileIn == nullptr) {
-               gu_log("[waveManager::create] unable to read %s. %s\n", path.c_str(), sf_strerror(fileIn));
+               u::log::print("[waveManager::create] unable to read %s. %s\n", path.c_str(), sf_strerror(fileIn));
                return { G_RES_ERR_IO };
        }
 
        if (header.channels > G_MAX_IO_CHANS) {
-               gu_log("[waveManager::create] unsupported multi-channel sample\n");
+               u::log::print("[waveManager::create] unsupported multi-channel sample\n");
                return { G_RES_ERR_WRONG_DATA };
        }
 
-       std::unique_ptr<Wave> wave = std::make_unique<Wave>();
-       wave->alloc(header.frames, header.channels, header.samplerate, getBits(header), path);
+       waveId_.set(id);
+
+       std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get(id));
+       wave->alloc(header.frames, header.channels, header.samplerate, getBits_(header), path);
 
        if (sf_readf_float(fileIn, wave->getFrame(0), header.frames) != header.frames)
-               gu_log("[waveManager::create] warning: incomplete read!\n");
+               u::log::print("[waveManager::create] warning: incomplete read!\n");
 
        sf_close(fileIn);
 
        if (header.channels == 1 && !wfx::monoToStereo(*wave))
                return { G_RES_ERR_PROCESSING };
 
-       gu_log("[waveManager::create] new Wave created, %d frames\n", wave->getSize());
+       u::log::print("[waveManager::create] new Wave created, %d frames\n", wave->getSize());
 
        return { G_RES_OK, std::move(wave) };
 }
@@ -114,13 +130,13 @@ Result createFromFile(const string& path)
 
 
 std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate, 
-       const string& name)
+       const std::string& name)
 {
-       std::unique_ptr<Wave> wave = std::make_unique<Wave>();
+       std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get());
        wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name);
        wave->setLogical(true);
 
-       gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n", 
+       u::log::print("[waveManager::createEmpty] new empty Wave created, %d frames\n", 
                wave->getSize());
 
        return wave;
@@ -130,17 +146,17 @@ std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
 /* -------------------------------------------------------------------------- */
 
 
-std::unique_ptr<Wave> createFromWave(const Wave* src, int a, int b)
+std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b)
 {
-       int channels = src->getChannels();
+       int channels = src.getChannels();
        int frames   = b - a;
 
-       std::unique_ptr<Wave> wave = std::make_unique<Wave>();
-       wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath());
-       wave->copyData(src->getFrame(a), frames);
+       std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get());
+       wave->alloc(frames, channels, src.getRate(), src.getBits(), src.getPath());
+       wave->copyData(src.getFrame(a), frames);
        wave->setLogical(true);
 
-       gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
+       u::log::print("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
 
        return wave;
 }
@@ -149,31 +165,40 @@ std::unique_ptr<Wave> createFromWave(const Wave* src, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-int resample(Wave* w, int quality, int samplerate)
+std::unique_ptr<Wave> createFromPatch(const patch::Wave& w)
 {
-       float ratio = samplerate / (float) w->getRate();
-       int newSizeFrames = ceil(w->getSize() * ratio);
+       return createFromFile(w.path, w.id).wave;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int resample(Wave& w, int quality, int samplerate)
+{
+       float ratio = samplerate / (float) w.getRate();
+       int newSizeFrames = ceil(w.getSize() * ratio);
 
        AudioBuffer newData;
-       newData.alloc(newSizeFrames, w->getChannels());
+       newData.alloc(newSizeFrames, w.getChannels());
 
        SRC_DATA src_data;
-       src_data.data_in       = w->getFrame(0);
-       src_data.input_frames  = w->getSize();
+       src_data.data_in       = w.getFrame(0);
+       src_data.input_frames  = w.getSize();
        src_data.data_out      = newData[0];
        src_data.output_frames = newSizeFrames;
        src_data.src_ratio     = ratio;
 
-       gu_log("[waveManager::resample] resampling: new size=%d frames\n", newSizeFrames);
+       u::log::print("[waveManager::resample] resampling: new size=%d frames\n", newSizeFrames);
 
-       int ret = src_simple(&src_data, quality, w->getChannels());
+       int ret = src_simple(&src_data, quality, w.getChannels());
        if (ret != 0) {
-               gu_log("[waveManager::resample] resampling error: %s\n", src_strerror(ret));
+               u::log::print("[waveManager::resample] resampling error: %s\n", src_strerror(ret));
                return G_RES_ERR_PROCESSING;
        }
 
-       w->moveData(newData);
-       w->setRate(samplerate);
+       w.moveData(newData);
+       w.setRate(samplerate);
 
        return G_RES_OK;
 }
@@ -182,28 +207,25 @@ int resample(Wave* w, int quality, int samplerate)
 /* -------------------------------------------------------------------------- */
 
 
-int save(Wave* w, const string& path)
+int save(const Wave& w, const std::string& path)
 {
        SF_INFO header;
-       header.samplerate = w->getRate();
-       header.channels   = w->getChannels();
+       header.samplerate = w.getRate();
+       header.channels   = w.getChannels();
        header.format     = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
 
        SNDFILE* file = sf_open(path.c_str(), SFM_WRITE, &header);
        if (file == nullptr) {
-               gu_log("[waveManager::save] unable to open %s for exporting: %s\n",
+               u::log::print("[waveManager::save] unable to open %s for exporting: %s\n",
                        path.c_str(), sf_strerror(file));
                return G_RES_ERR_IO;
        }
 
-       if (sf_writef_float(file, w->getFrame(0), w->getSize()) != w->getSize())
-               gu_log("[waveManager::save] warning: incomplete write!\n");
+       if (sf_writef_float(file, w.getFrame(0), w.getSize()) != w.getSize())
+               u::log::print("[waveManager::save] warning: incomplete write!\n");
 
        sf_close(file);
 
-       w->setLogical(false);
-       w->setEdited(false);
-
        return G_RES_OK;
 }
 }}}; // giada::m::waveManager
index 927054a840f8827bdc68c01b19bf6a4e9ab63c4f..15e9b2561945bc1b5ad101498f80494fcb1dbf0e 100644 (file)
 
 #include <string>
 #include <memory>
-
-
-class Wave;
+#include "core/types.h"
 
 
 namespace giada {
-namespace m {
+namespace m 
+{
+class Wave;
+namespace patch
+{
+class Wave;
+}
 namespace waveManager
 {
 struct Result
@@ -45,11 +49,16 @@ struct Result
     int status;
     std::unique_ptr<Wave> wave;
 };
+/* init
+Initializes internal data. */
+       
+void init();
 
 /* create
-Creates a new Wave object with data read from file 'path'. */
+Creates a new Wave object with data read from file 'path'. Takes an optional
+'id' parameter for patch persistence. */
 
-Result createFromFile(const std::string& path);
+Result createFromFile(const std::string& path, ID id=0);
 
 /* createEmpty
 Creates a new silent Wave object. */
@@ -60,11 +69,21 @@ std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
 /* createFromWave
 Creates a new Wave from an existing one, copying the data in range a - b. */
 
-std::unique_ptr<Wave> createFromWave(const Wave* src, int a, int b);
+std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b);
+
+/* createFromPatch
+Creates a new Wave given the patch raw data. */
+
+std::unique_ptr<Wave> createFromPatch(const patch::Wave& w);
 
-int resample(Wave* w, int quality, int samplerate); 
-int save(Wave* w, const std::string& path);
+int resample(Wave& w, int quality, int samplerate); 
+
+/* save
+Writes Wave data to file 'path'. Only 'wav' format is supported for now. */
+
+int save(const Wave& w, const std::string& path);
 
 }}}; // giada::m::waveManager
 
-#endif
\ No newline at end of file
+
+#endif
index 1586aaaf1588073fd8904f83ff4a8a34a12725ea..4d732723ccbaca56f4e64f50b0e06e295fe84872 100755 (executable)
@@ -1941,7 +1941,7 @@ struct JackHandle {
 };\r
 \r
 /* --- Monocasual hack ------------------------------------------------------ */\r
-#ifdef __linux__\r
+#if defined(__linux__) || defined(__FreeBSD__)\r
 void *RtApi :: __HACK__getJackClient() {\r
        JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
        return (void*) handle->client;\r
index ddb42ccb7449fd136214479ed3da64dae30ce0bf..5e95b236b41558849376f7d1c98e6ed96a9d75c6 100755 (executable)
@@ -676,7 +676,7 @@ class RtApi
 public:
 
   /* --- Monocasual hack ---------------------------------------------------- */
-  #ifdef __linux__
+  #if defined(__linux__) || defined(__FreeBSD__)
        void *__HACK__getJackClient();
   #endif
   /* ------------------------------------------------------------------------ */
index 5d2c6d53695fd0618024a59a57102c0c39d6ca2f..28cef60c4591c1782880c04d40b68cd10f38c8fa 100644 (file)
 
 
 #include <cassert>
-#include "../core/clock.h"
-#include "../core/const.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../core/recorderHandler.h"
-#include "../core/recorder.h"
-#include "../core/action.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/clock.h"
+#include "core/const.h"
+#include "core/recorderHandler.h"
+#include "core/recorder.h"
+#include "core/action.h"
 #include "recorder.h"
 #include "actionEditor.h"
 
 
-using std::vector;
-
-
 namespace giada {
 namespace c {
 namespace actionEditor
 {
 namespace
 {
-Frame fixVerticalEnvActions_(Frame f, const m::Action* a1, const m::Action* a2)
+Frame fixVerticalEnvActions_(Frame f, const m::Action& a1, const m::Action& a2)
 {
-       if      (a1->frame == f) f += 1;
-       else if (a2->frame == f) f -= 1;
-       if (a1->frame == f || a2->frame == f)
+       if      (a1.frame == f) f += 1;
+       else if (a2.frame == f) f -= 1;
+       if (a1.frame == f || a2.frame == f)
                return -1;
        return f;
 }
@@ -61,18 +59,19 @@ Frame fixVerticalEnvActions_(Frame f, const m::Action* a1, const m::Action* a2)
 /* recordFirstEnvelopeAction_
 First action ever? Add actions at boundaries. */
 
-void recordFirstEnvelopeAction_(int channel, Frame frame, int value)
+void recordFirstEnvelopeAction_(ID channelId, Frame frame, int value)
 {
        namespace mr = m::recorder;
 
        m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, G_MAX_VELOCITY);
        m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
-       const m::Action* a1 = mr::rec(channel, 0, e1);  
-       const m::Action* a2 = mr::rec(channel, frame, e2);
-       const m::Action* a3 = mr::rec(channel, m::clock::getFramesInLoop() - 1, e1);
-       mr::updateSiblings(a1, a3, a2); // Circular loop (begin)
-       mr::updateSiblings(a2, a1, a3);
-       mr::updateSiblings(a3, a2, a1); // Circular loop (end)
+       const m::Action a1 = mr::rec(channelId, 0, e1); 
+       const m::Action a2 = mr::rec(channelId, frame, e2); 
+       const m::Action a3 = mr::rec(channelId, m::clock::getFramesInLoop() - 1, e1);
+
+       mr::updateSiblings(a1.id, /*prev=*/a3.id, /*next=*/a2.id); // Circular loop (begin)
+       mr::updateSiblings(a2.id, /*prev=*/a1.id, /*next=*/a3.id);
+       mr::updateSiblings(a3.id, /*prev=*/a2.id, /*next=*/a1.id); // Circular loop (end)
 }
 
 
@@ -83,20 +82,39 @@ void recordFirstEnvelopeAction_(int channel, Frame frame, int value)
 Find action right before frame 'frame' and inject a new action in there. 
 Vertical envelope points are forbidden. */
 
-void recordNonFirstEnvelopeAction_(int channel, Frame frame, int value)
+void recordNonFirstEnvelopeAction_(ID channelId, Frame frame, int value)
 {
        namespace mr = m::recorder;
 
-       m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
-       const m::Action* a1 = mr::getClosestAction(channel, frame, m::MidiEvent::ENVELOPE);
-       const m::Action* a3 = a1->next;
-       assert(a1 != nullptr);
-       assert(a3 != nullptr);
+       const m::Action a1 = mr::getClosestAction(channelId, frame, m::MidiEvent::ENVELOPE);
+       const m::Action a3 = a1.next != nullptr ? *a1.next : m::Action{};
+
+       assert(a1.isValid());
+       assert(a3.isValid());
+
        frame = fixVerticalEnvActions_(frame, a1, a3);
        if (frame == -1) // Vertical points, nothing to do here
                return;
-       const m::Action* a2 = mr::rec(channel, frame, e2);
-       mr::updateSiblings(a2, a1, a3);
+
+       m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
+       const m::Action a2 = mr::rec(channelId, frame, e2);
+
+       mr::updateSiblings(a2.id, a1.id, a3.id);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isSinglePressMode_(ID channelId)
+{
+       bool b;
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               b = sc.mode == ChannelMode::SINGLE_PRESS;
+       });
+       return b;
 }
 }; // {anonymous}
 
@@ -106,7 +124,7 @@ void recordNonFirstEnvelopeAction_(int channel, Frame frame, int value)
 /* -------------------------------------------------------------------------- */
 
 
-void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Frame f2)
+void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2)
 {
        namespace mr = m::recorder;
        namespace cr = c::recorder;
@@ -116,7 +134,7 @@ void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Fram
 
        /* Avoid frame overflow. */
 
-       int overflow = f2 - (m::clock::getFramesInLoop());
+       Frame overflow = f2 - (m::clock::getFramesInLoop());
        if (overflow > 0) {
                f2 -= overflow;
                f1 -= overflow;
@@ -125,97 +143,114 @@ void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Fram
        m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON,  note, velocity);
        m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity);
 
-       const m::Action* a1 = mr::rec(ch->index, f1, e1);
-       const m::Action* a2 = mr::rec(ch->index, f2, e2);
+       mr::rec(channelId, f1, f2, e1, e2);
 
-       mr::updateSiblings(a1, nullptr, a2);
-
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void deleteMidiAction(m::MidiChannel* ch, const m::Action* a)
+void deleteMidiAction(ID channelId, const m::Action& a)
 {
        namespace mr = m::recorder;
        namespace cr = c::recorder;
 
-       assert(a != nullptr);
-       assert(a->event.getStatus() == m::MidiEvent::NOTE_ON);
+       assert(a.isValid());
+       assert(a.event.getStatus() == m::MidiEvent::NOTE_ON);
 
        /* Send a note-off first in case we are deleting it in a middle of a 
-       key_on/key_off sequence. */
+       key_on/key_off sequence. Check if 'next' exist first: could be orphaned. */
        
-       if (a->next != nullptr) {
-               ch->sendMidi(a->next, 0);
-               mr::deleteAction(a->next);
+       if (a.next != nullptr) {
+               m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+               {
+                       m::MidiChannel& mc = static_cast<m::MidiChannel&>(c);
+                       if (mc.isPlaying() && !mc.mute)
+                               mc.sendMidi(a.next->event, 0);
+               });
+               mr::deleteAction(a.id, a.next->id);
        }
-       mr::deleteAction(a);
+       else
+               mr::deleteAction(a.id);
 
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateMidiAction(m::MidiChannel* ch, const m::Action* a, int note, int velocity, 
+void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity, 
        Frame f1, Frame f2)
 {
        namespace mr = m::recorder;
 
-       mr::deleteAction(a->next);
-       mr::deleteAction(a);
-       
-       recordMidiAction(ch, note, velocity, f1, f2);
+       mr::deleteAction(a.id, a.next->id);
+       recordMidiAction(channelId, note, velocity, f1, f2);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void recordSampleAction(const m::SampleChannel* ch, int type, Frame f1, Frame f2)
+void recordSampleAction(ID channelId, int type, Frame f1, Frame f2)
 {
        namespace mr = m::recorder;
-       namespace cr = c::recorder;
 
-       if (ch->mode == ChannelMode::SINGLE_PRESS) {
+       if (isSinglePressMode_(channelId)) {
+               if (f2 == 0)
+                       f2 = f1 + G_DEFAULT_ACTION_SIZE;
                m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, 0, 0);
                m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, 0, 0);
-               const m::Action* a1 = mr::rec(ch->index, f1, e1);
-               const m::Action* a2 = mr::rec(ch->index, f2 == 0 ? f1 + G_DEFAULT_ACTION_SIZE : f2, e2);
-               mr::updateSiblings(a1, nullptr, a2);
+               mr::rec(channelId, f1, f2, e1, e2);
        }
        else {
                m::MidiEvent e1 = m::MidiEvent(type, 0, 0);
-               mr::rec(ch->index, f1, e1);
+               mr::rec(channelId, f1, e1);
        }
-       
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateSampleAction(m::SampleChannel* ch, const m::Action* a, int type, Frame f1
-       Frame f2)
+void updateSampleAction(ID channelId, const m::Action& a, int type
+       Frame f1, Frame f2)
 {
        namespace mr = m::recorder;     
 
-       if (ch->mode == ChannelMode::SINGLE_PRESS)
-               mr::deleteAction(a->next);
-       mr::deleteAction(a);
+       if (isSinglePressMode_(channelId))
+               mr::deleteAction(a.id, a.next->id);
+       else
+               mr::deleteAction(a.id);
+
+       recordSampleAction(channelId, type, f1, f2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
 
-       recordSampleAction(ch, type, f1, f2);
+void deleteSampleAction(ID channelId, const m::Action& a)
+{
+       namespace mr = m::recorder;
+       namespace cr = c::recorder;
+
+       if (a.next != nullptr) // For ChannelMode::SINGLE_PRESS combo
+               mr::deleteAction(a.next->id);
+       mr::deleteAction(a.id);
+
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void recordEnvelopeAction(m::Channel* ch, int frame, int value)
+void recordEnvelopeAction(ID channelId, Frame f, int value)
 {
        namespace mr = m::recorder;     
        namespace cr = c::recorder;     
@@ -225,115 +260,103 @@ void recordEnvelopeAction(m::Channel* ch, int frame, int value)
        /* First action ever? Add actions at boundaries. Else, find action right
        before frame 'f' and inject a new action in there. Vertical envelope points 
        are forbidden for now. */
+       
 
-       if (!mr::hasActions(ch->index, m::MidiEvent::ENVELOPE))
-               recordFirstEnvelopeAction_(ch->index, frame, value);
+       if (!mr::hasActions(channelId, m::MidiEvent::ENVELOPE))
+               recordFirstEnvelopeAction_(channelId, f, value);
        else 
-               recordNonFirstEnvelopeAction_(ch->index, frame, value);
+               recordNonFirstEnvelopeAction_(channelId, f, value);
 
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void deleteEnvelopeAction(m::Channel* ch, const m::Action* a)
+void deleteEnvelopeAction(ID channelId, const m::Action& a)
 {
        namespace mr  = m::recorder;
        namespace cr  = c::recorder;
        namespace mrh = m::recorderHandler;
 
-       assert(a != nullptr);
-
-       /* Delete a boundary action wipes out everything. If is volume, remember to
-       restore _i and _d members in channel. */
-
+       /* Deleting a boundary action wipes out everything. If is volume, remember 
+       to restore _i and _d members in channel. */
+       /* TODO - move this to c::*/
        if (mrh::isBoundaryEnvelopeAction(a)) {
-               if (a->isVolumeEnvelope()) {
-                       ch->volume_i = 1.0;
-                       ch->volume_d = 0.0;
+               if (a.isVolumeEnvelope()) {
+                       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+                       {
+                               c.volume_i = 1.0;
+                               c.volume_d = 0.0;
+                       });
                }
-               mr::clearActions(ch->index, a->event.getStatus());
-               return;
+               mr::clearActions(channelId, a.event.getStatus());
+       }
+       else {
+               assert(a.prev != nullptr);
+               assert(a.next != nullptr);
+               
+               const m::Action a1     = *a.prev;
+               const m::Action a1prev = *a1.prev;
+               const m::Action a3     = *a.next; 
+               const m::Action a3next = *a3.next; 
+
+               /* Original status:   a1--->a--->a3
+                  Modified status:   a1-------->a3 
+               Order is important, here: first update siblings, then delete the action.
+               Otherwise mr::deleteAction would complain of missing prevId/nextId no
+               longer found. */
+
+               mr::updateSiblings(a1.id, a1prev.id, a3.id);
+               mr::updateSiblings(a3.id, a1.id, a3next.id);
+               mr::deleteAction(a.id);
        }
 
-       const m::Action* a1 = a->prev;
-       const m::Action* a3 = a->next; 
-
-       /* Original status:   a1--->a--->a3
-          Modified status:   a1-------->a3 */
-
-       mr::deleteAction(a);
-       mr::updateSiblings(a1, a1->prev, a3);
-       mr::updateSiblings(a3, a1, a3->next);
-
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+       recorder::updateChannel(channelId, /*updateActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateEnvelopeAction(m::Channel* ch, const m::Action* a, int frame, int value)
+void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value)
 {
        namespace mr  = m::recorder;
        namespace cr  = c::recorder;
        namespace mrh = m::recorderHandler;
 
-       assert(a != nullptr);
-
        /* Update the action directly if it is a boundary one. Else, delete the
        previous one and record a new action. */
 
        if (mrh::isBoundaryEnvelopeAction(a))
-               mr::updateEvent(a, m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value));
+               mr::updateEvent(a.id, m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value));
        else {
-               deleteEnvelopeAction(ch, a);
-               recordEnvelopeAction(ch, frame, value); 
+               deleteEnvelopeAction(channelId, a);
+               recordEnvelopeAction(channelId, f, value);
        }
-
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);       
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void deleteSampleAction(m::SampleChannel* ch, const m::Action* a)
-{
-       namespace mr = m::recorder;
-       namespace cr = c::recorder;
-
-       assert(a != nullptr);
-
-       if (a->next != nullptr) // For ChannelMode::SINGLE_PRESS combo
-               mr::deleteAction(a->next);
-       mr::deleteAction(a);
-
-       cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-vector<const m::Action*> getActions(const m::Channel* ch)
+std::vector<m::Action> getActions(ID channelId)
 {
-       return m::recorder::getActionsOnChannel(ch->index);
+       return m::recorder::getActionsOnChannel(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateVelocity(const m::MidiChannel* ch, const m::Action* a, int value)
+void updateVelocity(const m::Action& a, int value)
 {
        namespace mr = m::recorder;
        
-       m::MidiEvent event(a->event);
+       m::MidiEvent event(a.event);
        event.setVelocity(value);
 
-       mr::updateEvent(a, event);
+       mr::updateEvent(a.id, event);
 }
 }}}; // giada::c::actionEditor::
index 7c4f81423e8f4f3f6206ddee530e1ae251e472f7..2f70d6c540914fcdb168d585372a87df0aa0b1f6 100644 (file)
@@ -30,7 +30,7 @@
 
 
 #include <vector>
-#include "../core/types.h"
+#include "core/types.h"
 
 
 namespace giada {
@@ -43,27 +43,30 @@ class MidiChannel;
 namespace c {
 namespace actionEditor 
 {
-std::vector<const m::Action*> getActions(const m::Channel* ch);
+std::vector<m::Action> getActions(ID channelId);
 
 /* MIDI actions.  */
 
-void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Frame f2=0);
-void deleteMidiAction(m::MidiChannel* ch, const m::Action* a);
-void updateMidiAction(m::MidiChannel* ch, const m::Action* a, int note, int velocity, 
-    Frame f1, Frame f2);
-void updateVelocity(const m::MidiChannel* ch, const m::Action* a, int value);
+void recordMidiAction(ID channelId, int note, int velocity, Frame f1, 
+       Frame f2=0);
+void deleteMidiAction(ID channelId, const m::Action& a);
+void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity, 
+       Frame f1, Frame f2);
+void updateVelocity(const m::Action& a, int value);
 
 /* Sample Actions. */
 
-void recordSampleAction(const m::SampleChannel* ch, int type, Frame f1, Frame f2=0);
-void deleteSampleAction(m::SampleChannel* ch, const m::Action* a);
-void updateSampleAction(m::SampleChannel* ch, const m::Action* a, int type, Frame f1, Frame f2=0);
+void recordSampleAction(ID channelId, int type, Frame f1, Frame f2=0);
+void deleteSampleAction(ID channelId, const m::Action& a);
+void updateSampleAction(ID channelId, const m::Action& a, int type, 
+    Frame f1, Frame f2=0);
 
 /* Envelope actions (only volume for now). */
 
-void recordEnvelopeAction(m::Channel* ch, int frame, int value);
-void deleteEnvelopeAction(m::Channel* ch, const m::Action* a);
-void updateEnvelopeAction(m::Channel* ch, const m::Action* a, int frame, int value);
+void recordEnvelopeAction(ID channelId, Frame f, int value);
+void deleteEnvelopeAction(ID channelId, const m::Action& a);
+void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value);
 }}}; // giada::c::actionEditor::
 
+
 #endif
index 41df9413528ebf57743e847a1bcf6d78721998ab..563a955922b6f2d5dbfd51f4e2e7d6028a7b232e 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <functional>
 #include <cmath>
+#include <cassert>
 #include <FL/Fl.H>
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/sampleEditor.h"
-#include "../gui/dialogs/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 "../utils/log.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"
-#include "../core/channel.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../core/recorder.h"
-#include "../core/plugin.h"
-#include "../core/waveManager.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/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 "utils/log.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/model/model.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"
+#include "core/recorder.h"
+#include "core/plugin.h"
+#include "core/waveManager.h"
 #include "main.h"
 #include "channel.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using std::string;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
-namespace c     {
+namespace c {
 namespace channel 
 {
-int loadChannel(m::SampleChannel* ch, const string& fname)
+namespace
+{
+void printLoadError_(int res)
 {
-       using namespace giada::m;
+       if      (res == G_RES_ERR_WRONG_DATA)
+               v::gdAlert("Multichannel samples not supported.");
+       else if (res == G_RES_ERR_IO)
+               v::gdAlert("Unable to read this sample.");
+       else if (res == G_RES_ERR_PATH_TOO_LONG)
+               v::gdAlert("File path too long.");
+       else if (res == G_RES_ERR_NO_DATA)
+               v::gdAlert("No file specified.");
+}
 
-       /* Always stop a channel before loading a new sample in it. This will prevent
-       issues if tracker is outside the boundaries of the new sample -> segfault. */
 
-       if (ch->isPlaying())
-               ch->kill(0);
+/* -------------------------------------------------------------------------- */
 
-       /* Save the patch and take the last browser's dir in order to re-use it the 
-       next time. */
 
-       conf::samplePath = gu_dirname(fname);
+void onRefreshSampleEditor_(bool gui, std::function<void(v::gdSampleEditor*)> f)
+{
+       v::gdSampleEditor* gdEditor = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       if (gdEditor == nullptr) 
+               return;
+       if (!gui) Fl::lock();
+       f(gdEditor);
+       if (!gui) Fl::unlock();
+}
+} // {anonymous}
 
-       waveManager::Result res = waveManager::createFromFile(fname); 
 
-       if (res.status != G_RES_OK)
-               return res.status;
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 
-       if (res.wave->getRate() != conf::samplerate) {
-               gu_log("[loadChannel] input rate (%d) != system rate (%d), conversion needed\n",
-                       res.wave->getRate(), conf::samplerate);
-               res.status = waveManager::resample(res.wave.get(), conf::rsmpQuality, conf::samplerate); 
-               if (res.status != G_RES_OK)
-                       return res.status;
-       }
 
-       ch->pushWave(std::move(res.wave));
+int loadChannel(ID channelId, const std::string& fname)
+{
+       /* Save the patch and take the last browser's dir in order to re-use it the 
+       next time. */
 
-       G_MainWin->keyboard->updateChannel(ch->guiChannel);
+       m::conf::samplePath = u::fs::dirname(fname);
 
-       return res.status;
+       int res = m::mh::loadChannel(channelId, fname);
+       if (res != G_RES_OK)
+               printLoadError_(res);
+       
+       return res;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-m::Channel* addChannel(int column, ChannelType type, int size)
+void addChannel(ID columnId, ChannelType type, int size)
 {
-       m::Channel* ch = m::mh::addChannel(type);
-       geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size);
-       ch->guiChannel = gch;
-       return ch;
+       m::mh::addChannel(type, columnId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void deleteChannel(m::Channel* ch)
+void addAndLoadChannel(ID columnId, const std::string& fpath)
+{
+       int res = m::mh::addAndLoadChannel(columnId, fpath);
+       if (res != G_RES_OK)
+               printLoadError_(res);
+}
+
+
+void addAndLoadChannels(ID columnId, const std::vector<std::string>& fpaths)
 {
-       using namespace giada::m;
+       if (fpaths.size() == 1)
+               return addAndLoadChannel(columnId, fpaths[0]);
 
-       if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
-               return;
-       recorder::clearChannel(ch->index);
-       ch->hasActions = false;
-#ifdef WITH_VST
-       pluginHost::freeStack(pluginHost::StackType::CHANNEL, &mixer::mutex, ch);
-#endif
-       Fl::lock();
-       G_MainWin->keyboard->deleteChannel(ch->guiChannel);
-       Fl::unlock();
-       mh::deleteChannel(ch);
-       u::gui::closeAllSubwindows();
+       bool errors = false;
+       for (const std::string& f : fpaths)
+               if (m::mh::addAndLoadChannel(columnId, f) != G_RES_OK)
+                       errors = true;
+
+       if (errors)
+               v::gdAlert("Some files weren't loaded sucessfully.");
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void freeChannel(m::Channel* ch)
+void deleteChannel(ID channelId)
 {
-       if (ch->isPlaying()) {
-               if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
-                       return;
-       }
-       else
-       if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
+       if (!v::gdConfirmWin("Warning", "Delete channel: are you sure?"))
                return;
-
-       G_MainWin->keyboard->freeChannel(ch->guiChannel);
-       m::recorder::clearChannel(ch->index);
-       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);
+       u::gui::closeAllSubwindows();
+       m::recorder::clearChannel(channelId);
+       m::mh::deleteChannel(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleArm(m::Channel* ch, bool gui)
+void freeChannel(ID channelId)
 {
-       ch->armed = !ch->armed;
-       if (!gui)
-               ch->guiChannel->arm->value(ch->armed);
+       if (!v::gdConfirmWin("Warning", "Free channel: are you sure?"))
+               return;
+       u::gui::closeAllSubwindows();
+       m::recorder::clearChannel(channelId);
+       m::mh::freeChannel(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleInputMonitor(m::Channel* ch)
+void setArm(ID channelId, bool value)
+{
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c) { c.armed = value; });
+}
+
+
+void toggleArm(ID channelId)
 {
-       m::SampleChannel* sch = static_cast<m::SampleChannel*>(ch);
-       sch->inputMonitor = !sch->inputMonitor;
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c) { c.armed = !c.armed; });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int cloneChannel(m::Channel* src)
+void setInputMonitor(ID channelId, bool value)
 {
-       using namespace giada::m;
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c) 
+       { 
+               static_cast<m::SampleChannel&>(c).inputMonitor = value;
+       });
+}
 
-       Channel* ch    = mh::addChannel(src->type);
-       geChannel* gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), 
-               ch, src->guiChannel->getSize());
 
-       ch->guiChannel = gch;
-       ch->copy(src, &mixer::mutex);
+/* -------------------------------------------------------------------------- */
+
 
-       G_MainWin->keyboard->updateChannel(ch->guiChannel);
-       return true;
+void cloneChannel(ID channelId)
+{
+       m::mh::cloneChannel(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setVolume(m::Channel* ch, float v, bool gui, bool editor)
+void setVolume(ID channelId, float value, bool gui, bool editor)
 {
-       ch->volume = v;
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c) { c.volume = value; });
 
        /* Changing channel volume? Update wave editor (if it's shown). */
 
-       if (!editor) {
-               gdSampleEditor* gdEditor = static_cast<gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-               if (gdEditor) {
-                       Fl::lock();
-                       gdEditor->volumeTool->refresh();
-                       Fl::unlock();
-               }
-       }
+       if (editor) 
+               onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->volumeTool->rebuild(); });
 
        if (!gui) {
                Fl::lock();
-               ch->guiChannel->vol->value(v);
+               G_MainWin->keyboard->getChannel(channelId)->vol->value(value);
                Fl::unlock();
        }
 }
@@ -238,101 +243,128 @@ void setVolume(m::Channel* ch, float v, bool gui, bool editor)
 /* -------------------------------------------------------------------------- */
 
 
-void setPitch(m::SampleChannel* ch, float val)
-{
-       ch->setPitch(val);
-       gdSampleEditor* gdEditor = static_cast<gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-       if (gdEditor) {
-               Fl::lock();
-               gdEditor->pitchTool->refresh();
-               Fl::unlock();
-       }
+void setPitch(ID channelId, float val, bool gui)
+{      
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       { 
+               static_cast<m::SampleChannel&>(c).setPitch(val); 
+       });
+       
+       onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->pitchTool->rebuild(); });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setPanning(m::SampleChannel* ch, float val)
+void setPan(ID channelId, float val, bool gui)
 {
-       ch->setPan(val);
-       gdSampleEditor* gdEditor = static_cast<gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-       if (gdEditor) {
-               Fl::lock();
-               gdEditor->panTool->refresh();
-               Fl::unlock();
-       }
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c) { c.setPan(val); });
+
+       onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->panTool->rebuild(); });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleMute(m::Channel* ch, bool gui)
+void setMute(ID channelId, bool value)
 {
-       ch->setMute(!ch->mute);
-       if (!gui) {
-               Fl::lock();
-               ch->guiChannel->mute->value(ch->mute);
-               Fl::unlock();
-       }
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setMute(value); });
+}
+
+
+void toggleMute(ID channelId)
+{
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setMute(!ch.mute); });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleSolo(m::Channel* ch, bool gui)
+void setSampleMode(ID channelId, ChannelMode m)
 {
-       ch->setSolo(!ch->solo);
-       if (!gui) {
-               Fl::lock();
-               ch->guiChannel->solo->value(ch->solo);
-               Fl::unlock();
-       }
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               static_cast<m::SampleChannel&>(ch).mode = m;
+       });
+
+       u::gui::refreshActionEditor();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void kill(m::Channel* ch)
-{
-       ch->kill(0); // on frame 0: it's a user-generated event
+void setSolo(ID channelId, bool value)
+{      
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setSolo(value); });
+       m::mh::updateSoloCount();
 }
 
 
+void toggleSolo(ID channelId)
+{      
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setSolo(!ch.solo); });
+       m::mh::updateSoloCount();
+}
+
 /* -------------------------------------------------------------------------- */
 
 
-void setBoost(m::SampleChannel* ch, float val)
+void start(ID channelId, int velocity, bool record)
 {
-       ch->setBoost(val);
-       gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-       if (gdEditor) {
-               Fl::lock();
-               gdEditor->boostTool->refresh();
-               Fl::unlock();
-       }
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               if (record && !ch.recordStart(m::clock::canQuantize()))
+                       return;
+               ch.start(/*localFrame=*/0, m::clock::canQuantize(), velocity); // Frame 0: user-generated event
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setName(m::Channel* ch, const string& name)
+void kill(ID channelId, bool record)
 {
-       ch->name = name;
-       ch->guiChannel->update();
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               if (record && !ch.recordKill())
+                       return;
+               ch.kill(/*localFrame=*/0); // Frame 0: user-generated event
+       });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stop(ID channelId)
+{      
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               ch.recordStop();
+               ch.stop();
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleReadingActions(m::Channel* ch, bool gui)
+void setName(ID channelId, const std::string& name)
 {
+       m::mh::renameChannel(channelId, name);
+}
 
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleReadingActions(ID channelId)
+{
        /* When you call 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
@@ -341,45 +373,39 @@ void toggleReadingActions(m::Channel* ch, bool gui)
        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 == ChannelStatus::WAIT))
-               stopReadingActions(ch, gui);
-       else
-               startReadingActions(ch, gui);
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               if (ch.readActions || (!ch.readActions && ch.recStatus == ChannelStatus::WAIT))
+                       ch.stopReadingActions(m::clock::isRunning(), m::conf::treatRecsAsLoops, 
+                               m::conf::recsStopOnChanHalt);
+               else
+                       ch.startReadingActions(m::conf::treatRecsAsLoops, m::conf::recsStopOnChanHalt);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void startReadingActions(m::Channel* ch, bool gui)
+void startReadingActions(ID channelId)
 {
-       using namespace giada::m;
-
-       ch->startReadingActions(conf::treatRecsAsLoops, conf::recsStopOnChanHalt); 
-
-       if (!gui) {
-               Fl::lock();
-               static_cast<geSampleChannel*>(ch->guiChannel)->readActions->value(1);
-               Fl::unlock();
-       }
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               ch.startReadingActions(m::conf::treatRecsAsLoops, m::conf::recsStopOnChanHalt);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void stopReadingActions(m::Channel* ch, bool gui)
+void stopReadingActions(ID channelId)
 {
-       using namespace giada::m;
-
-       ch->stopReadingActions(clock::isRunning(), conf::treatRecsAsLoops, 
-               conf::recsStopOnChanHalt);
-
-       if (!gui) {
-               Fl::lock();
-               static_cast<geSampleChannel*>(ch->guiChannel)->readActions->value(0);
-               Fl::unlock();
-       }
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+       {
+               ch.stopReadingActions(m::clock::isRunning(), m::conf::treatRecsAsLoops, 
+                       m::conf::recsStopOnChanHalt);
+       });
 }
 
 }}}; // giada::c::channel::
index 0808297a15a3af45c0c0c1fd6388ef2ecf30a5e4..2c76656439c7cca57bb65c34b0b3694906a7bd81 100644 (file)
 
 
 #include <string>
-#include "../core/types.h"
-
-
-class gdSampleEditor;
+#include <vector>
+#include "core/types.h"
 
 
 namespace giada {
 namespace m
 {
 class Channel;
-class SampleChannel;
 }
 namespace c {
 namespace channel 
 {
 /* addChannel
-Adds an empty new channel to the stack. Returns the new channel. */
+Adds an empty new channel to the stack. */
 
-m::Channel* addChannel(int column, ChannelType type, int size);
+void addChannel(ID columnId, ChannelType type, int size);
 
 /* loadChannel
 Fills an existing channel with a wave. */
 
-int loadChannel(m::SampleChannel* ch, const std::string& fname);
+int loadChannel(ID channelId, const std::string& fname);
+
+/* addAndLoadChannel
+Adds a new Sample Channel and fills it with a wave right away. */
+
+void addAndLoadChannel(ID columnId, const std::string& fpath); 
+
+/* addAndLoadChannels
+As above, with multiple audio file paths in input. */
+
+void addAndLoadChannels(ID columnId, const std::vector<std::string>& fpaths);
 
 /* deleteChannel
 Removes a channel from Mixer. */
 
-void deleteChannel(m::Channel* ch);
+void deleteChannel(ID channelId);
 
 /* freeChannel
 Unloads the sample from a sample channel. */
 
-void freeChannel(m::Channel* ch);
+void freeChannel(ID channelId);
 
 /* cloneChannel
 Makes an exact copy of Channel *ch. */
 
-int cloneChannel(m::Channel* ch);
+void cloneChannel(ID channelId);
+
+/* set*
+Sets several channel properties. */
 
-/* toggle/set*
-Toggles 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 setArm(ID channelId, bool value);
+void toggleArm(ID channelId);
+void setInputMonitor(ID channelId, bool value);
+void setMute(ID channelId, bool value);
+void toggleMute(ID channelId);
+void setSolo(ID channelId, bool value);
+void toggleSolo(ID channelId);
+void setVolume(ID channelId, float v, bool gui=true, bool editor=false);
+void setName(ID channelId, const std::string& name);
+void setPitch(ID channelId, float val, bool gui=true);
+void setPan(ID channelId, float val, bool gui=true);
+void setSampleMode(ID channelId, ChannelMode m);
 
-void toggleArm(m::Channel* ch, bool gui=true);
-void toggleInputMonitor(m::Channel* ch);
-void kill(m::Channel* ch);
-void toggleMute(m::Channel* ch, bool gui=true);
-void toggleSolo(m::Channel* ch, bool gui=true);
-void setVolume(m::Channel* ch, float v, bool gui=true, bool editor=false);
-void setName(m::Channel* ch, const std::string& name);
-void setPitch(m::SampleChannel* ch, float val);
-void setPanning(m::SampleChannel* ch, float val);
-void setBoost(m::SampleChannel* ch, float val);
+void start(ID channelId, int velocity, bool record);
+void kill(ID channelId, bool record);
+void stop(ID channelId);
 
 /* toggleReadingRecs
 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 toggleReadingActions(m::Channel* ch, bool gui=true);
-void startReadingActions(m::Channel* ch, bool gui=true);
-void stopReadingActions(m::Channel* ch, bool gui=true);
+void toggleReadingActions(ID channelId);
+void startReadingActions(ID channelId);
+void stopReadingActions(ID channelId);
 
 }}}; // giada::c::channel::
 
index 0a269149e87dbeff6b8a5858612252ea7f0a6a2c..8cd806f42408d7280a0ebac842891d98f35d467a 100644 (file)
 
 
 #include <FL/Fl.H>
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/warnings.h"
-#include "../gui/elems/basics/button.h"
-#include "../gui/elems/mainWindow/mainTransport.h"
-#include "../gui/elems/mainWindow/mainTimer.h"
-#include "../gui/elems/mainWindow/keyboard/keyboard.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
-#include "../utils/gui.h"
-#include "../utils/log.h"
-#include "../utils/math.h"
-#include "../core/recorder.h"
-#include "../core/conf.h"
-#include "../core/recManager.h"
-#include "../core/kernelAudio.h"
-#include "../core/channel.h"
-#include "../core/mixer.h"
-#include "../core/mixerHandler.h"
-#include "../core/wave.h"
-#include "../core/midiDispatcher.h"
-#include "../core/channel.h"
-#include "../core/clock.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../core/recorderHandler.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/midiIO/midiInputBase.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/mainWindow/mainTransport.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/channelButton.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/channel.h"
+#include "core/channels/midiChannel.h"
+#include "core/recorder.h"
+#include "core/conf.h"
+#include "core/recManager.h"
+#include "core/kernelAudio.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/wave.h"
+#include "core/midiDispatcher.h"
+#include "core/clock.h"
+#include "core/recorderHandler.h"
 #include "main.h"
 #include "channel.h"
-#include "transport.h"
 #include "io.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
 namespace c {
 namespace io 
 {
-void keyPress(m::Channel* ch, bool ctrl, bool shift, int velocity)
+void keyPress(ID channelId, bool ctrl, bool shift, int velocity)
 {
-       /* Everything occurs on frame 0 here: they are all user-generated events. */
        if (ctrl)
-               c::channel::toggleMute(ch);
+               c::channel::toggleMute(channelId);
        else
-       if (shift) {
-               if (ch->recordKill())
-                       ch->kill(0);
-       }
-       else {
-               if (ch->recordStart(m::clock::canQuantize()))
-                       ch->start(0, m::clock::canQuantize(), velocity);
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void keyRelease(m::Channel* ch, bool ctrl, bool shift)
-{
-       if (!ctrl && !shift) {
-               ch->recordStop();
-               ch->stop();
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleActionRec(bool gui)
-{
-       m::recManager::isActive() ? stopActionRec(gui) : startActionRec(gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startActionRec(bool gui)
-{
-       RecTriggerMode triggerMode = static_cast<RecTriggerMode>(m::conf::recTriggerMode);
-
-       if (!m::recManager::startActionRec(triggerMode))
-               return;
-       if (!gui) Fl::lock();
-       G_MainWin->mainTransport->setRecTriggerModeActive(false);
-       G_MainWin->mainTransport->updatePlay(m::clock::isRunning());
-       G_MainWin->mainTransport->updateRecAction(1);
-       if (!gui) Fl::unlock();
+       if (shift)
+               c::channel::kill(channelId, /*record=*/true);
+       else
+               c::channel::start(channelId, velocity, /*record=*/true);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void stopActionRec(bool gui)
+void keyRelease(ID channelId, bool ctrl, bool shift)
 {
-       m::recManager::stopActionRec();
-
-       if (!gui) Fl::lock();
-       G_MainWin->mainTransport->updateRecAction(0);
-       G_MainWin->mainTransport->setRecTriggerModeActive(true);
-       for (m::Channel* ch : m::mixer::channels)
-               if (ch->type == ChannelType::SAMPLE)
-                       G_MainWin->keyboard->setChannelWithActions(static_cast<geSampleChannel*>(ch->guiChannel));
-       if (!gui) Fl::unlock();
-
-       u::gui::refreshActionEditor();  // in case it's open
+       if (!ctrl && !shift)
+               c::channel::stop(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toggleInputRec(bool gui)
+void setSampleChannelKey(ID channelId, int k)
 {
-       if (m::recManager::isActive())
-               stopInputRec(gui);
-       else
-       if (!startInputRec(gui))
-               gdAlert("No channels armed/available for audio recording.");
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               c.key = k;
+       });
+
+       Fl::lock();
+       G_MainWin->keyboard->getChannel(channelId)->mainButton->setKey(k);
+       Fl::unlock();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool startInputRec(bool gui)
+void midiLearn(m::MidiEvent e, std::atomic<uint32_t>& param, ID channelId)
 {
-       RecTriggerMode triggerMode = static_cast<RecTriggerMode>(m::conf::recTriggerMode);
-
-       if (!m::recManager::startInputRec(triggerMode)) {
-               if (!gui) Fl::lock();
-               G_MainWin->mainTransport->updateRecInput(0);  // set it off, anyway
-               if (!gui) Fl::unlock();
-               return false;
-       }       
-
-       if (!gui) Fl::lock();
-       G_MainWin->mainTransport->setRecTriggerModeActive(false);
-       G_MainWin->mainTransport->updatePlay(m::clock::isRunning());
-       G_MainWin->mainTransport->updateRecInput(1);
-       G_MainWin->mainTimer->setLock(true);
-       if (!gui) Fl::unlock();
-
-       return true;
-}
-
+       /* No MIDI learning if we are learning a Channel (channelId != 0) and 
+       the selected MIDI channel is filtered OR if we are learning a global 
+       parameter (channel == 0) and the selected MIDI channel is filtered. */
 
-/* -------------------------------------------------------------------------- */
+       if (channelId == 0) {
+               if (!m::conf::isMidiInAllowed(e.getChannel()))
+                       return;
+       }
+       else {
+               m::model::ChannelsLock l(m::model::channels);
+               if (!m::model::get(m::model::channels, channelId).isMidiInAllowed(e.getChannel()))
+                       return;
+       }
 
+       param.store(e.getRawNoVelocity());
+       m::midiDispatcher::stopMidiLearn();
 
-void stopInputRec(bool gui)
-{
-       m::recManager::stopInputRec();
-
-       if (!gui) Fl::lock();
-       G_MainWin->mainTransport->setRecTriggerModeActive(true);
-       G_MainWin->mainTransport->updateRecInput(0);
-       G_MainWin->mainTimer->setLock(false);
-       /* Update sample name inside sample channels' main button. This is useless 
-       for MIDI channels, but let's do it anyway. */
-       for (const m::Channel* ch : m::mixer::channels)
-               ch->guiChannel->update();
-       if (!gui) Fl::unlock();
+       Fl::lock();
+       u::gui::refreshSubWindow(WID_MIDI_INPUT);
+       u::gui::refreshSubWindow(WID_MIDI_OUTPUT);
+       Fl::unlock();
 }
-
-}}} // giada::c::io::
\ No newline at end of file
+}}} // giada::c::io::
index 2233d400b24dc6559362010890e3f5fd8acfe50d..f39f63cb50b2eba9e51d6cd5414d7d12a697ed2e 100644 (file)
@@ -2,13 +2,6 @@
  *
  * 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-2019 Giovanni A. Zuliani | Monocasual
 #define G_GLUE_IO_H
 
 
+#include <atomic>
+#include "core/types.h"
+#include "core/midiEvent.h"
+
+
 namespace giada {
 namespace m
 {
@@ -45,28 +43,17 @@ namespace c {
 namespace io 
 {
 /* keyPress / keyRelease
-Handle the key pressure, either via mouse/keyboard or MIDI. If gui is true the 
-event comes from the main window (mouse, keyboard or MIDI), otherwise the event 
-comes from the action recorder. */
-
-void keyPress  (m::Channel* ch, bool ctrl, bool shift, int velocity);
-void keyRelease(m::Channel* ch, bool ctrl, bool shift);
+Handle the key pressure, either via mouse/keyboard or MIDI. */
 
-/* start/stopActionRec
-Handles the action recording. If gui == true the signal comes from an user
-interaction, otherwise it's a MIDI/Jack/external signal. */
+void keyPress  (ID channelId, bool ctrl, bool shift, int velocity);
+void keyRelease(ID channelId, bool ctrl, bool shift);
 
-void toggleActionRec(bool gui=true);
-void startActionRec (bool gui=true);
-void stopActionRec  (bool gui=true);
+/* setSampleChannelKey
+Set key 'k' to Sample Channel 'channelId'. Used for keyboard bindings. */
 
-/* start/stopInputRec
-Handles the input recording (take). If gui == true the signal comes from an
-internal interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
+void setSampleChannelKey(ID channelId, int k);
 
-void toggleInputRec(bool gui=true);
-bool startInputRec (bool gui=true);
-void stopInputRec  (bool gui=true);
+void midiLearn(m::MidiEvent e, std::atomic<uint32_t>& param, ID channelId);
 }}} // giada::c::io::
 
 #endif
index fe0ef93bd3a071016d09c49bf33d659e71d82a28..0810a692f1a6b97032f70bc1c584af16a63f1175 100644 (file)
 
 
 #include <cmath>
+#include <cassert>
 #include <FL/Fl.H>
-#include "../gui/elems/mainWindow/mainIO.h"
-#include "../gui/elems/mainWindow/mainTimer.h"
-#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
-#include "../gui/elems/mainWindow/keyboard/keyboard.h"
-#include "../gui/dialogs/mainWindow.h"
-#include "../utils/gui.h"
-#include "../utils/string.h"
-#include "../utils/log.h"
-#include "../core/mixerHandler.h"
-#include "../core/mixer.h"
-#include "../core/midiChannel.h"
-#include "../core/clock.h"
-#include "../core/kernelMidi.h"
-#include "../core/kernelAudio.h"
-#include "../core/recorder.h"
-#include "../core/recorderHandler.h"
-#include "../core/conf.h"
-#include "../core/const.h"
-#include "../core/pluginManager.h"
-#include "../core/pluginHost.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/dialogs/mainWindow.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "utils/log.h"
+#include "core/model/model.h"
+#include "core/channels/midiChannel.h"
+#include "core/mixerHandler.h"
+#include "core/mixer.h"
+#include "core/clock.h"
+#include "core/init.h"
+#include "core/kernelMidi.h"
+#include "core/kernelAudio.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
 #include "main.h"
 
 
-extern gdMainWindow *G_MainWin;
+extern giada::v::gdMainWindow *G_MainWin;
 
 
-using std::string;
-using namespace giada::m;
-
 namespace giada {
 namespace c {
 namespace main
 {
 namespace
 {
-void setBpm_(float f, string s)
+void setBpm_(float current, std::string s)
 {
-       if (f < G_MIN_BPM) {
-               f = G_MIN_BPM;
+       if (current < G_MIN_BPM) {
+               current = G_MIN_BPM;
                s = G_MIN_BPM_STR;
        }
        else
-       if (f > G_MAX_BPM) {
-               f = G_MAX_BPM;
+       if (current > G_MAX_BPM) {
+               current = G_MAX_BPM;
                s = G_MAX_BPM_STR;              
        }
 
-       float vPre = clock::getBpm();
-       clock::setBpm(f);
-       recorderHandler::updateBpm(vPre, f, clock::getQuanto());
-       mixer::allocVirtualInput(clock::getFramesInLoop());
+       float previous = m::clock::getBpm();
+       m::clock::setBpm(current);
+       m::recorderHandler::updateBpm(previous, current, m::clock::getQuanto());
+       m::mixer::allocVirtualInput(m::clock::getFramesInLoop());
 
        /* This function might get called by Jack callback BEFORE the UI is up
        and running, that is when G_MainWin == nullptr. */
@@ -87,7 +89,7 @@ void setBpm_(float f, string s)
                G_MainWin->mainTimer->setBpm(s.c_str());
        }
 
-       gu_log("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s.c_str(), clock::getBpm());
+       u::log::print("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s.c_str(), m::clock::getBpm());
 }
 } // {anonymous}
 
@@ -99,22 +101,22 @@ void setBpm_(float f, string s)
 
 void setBpm(const char* v1, const char* v2)
 {
-       /* Never change this stuff while recording audio */
+       /* Never change this stuff while recording audio. */
 
-       if (mixer::recording)
+       if (m::recManager::isRecordingInput())
                return;
 
        /* 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 mixer and we show 
+       of the rounding error. So we pass the actual "wrong" value to mixer and we show 
        the nice looking (but fake) one to the GUI. 
-       On Linux, let Jack handle the bpm change if its on. */
+       On Linux, let Jack handle the bpm change if it's on. */
 
-       float  f = atof(v1) + (atof(v2)/10);
-       string s = string(v1) + "." + string(v2);
+       float       f = std::atof(v1) + (std::atof(v2)/10);
+       std::string s = std::string(v1) + "." + std::string(v2);
 
-#ifdef G_OS_LINUX
-       if (kernelAudio::getAPI() == G_SYS_API_JACK)
-               kernelAudio::jackSetBpm(f);
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+       if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
+               m::kernelAudio::jackSetBpm(f);
        else
 #endif
        setBpm_(f, s);
@@ -126,14 +128,14 @@ void setBpm(const char* v1, const char* v2)
 
 void setBpm(float f)
 {
-       /* Never change this stuff while recording audio */
+       /* Never change this stuff while recording audio. */
 
-       if (mixer::recording)
+       if (m::recManager::isRecordingInput())
                return;
 
        float intpart;
        float fracpart = std::round(std::modf(f, &intpart) * 10);
-       string s = std::to_string((int) intpart) + "." + std::to_string((int)fracpart);
+       std::string s = std::to_string((int) intpart) + "." + std::to_string((int)fracpart);
 
        setBpm_(f, s);
 }
@@ -144,17 +146,15 @@ void setBpm(float f)
 
 void setBeats(int beats, int bars)
 {
-       /* Never change this stuff while recording audio */
+       /* Never change this stuff while recording audio. */
 
-       if (mixer::recording)
+       if (m::recManager::isRecordingInput())
                return;
 
-       clock::setBeats(beats);
-       clock::setBars(bars);
-       clock::updateFrameBars();
-       mixer::allocVirtualInput(clock::getFramesInLoop());
+       m::clock::setBeats(beats, bars);
+       m::mixer::allocVirtualInput(m::clock::getFramesInLoop());
 
-       G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
+       G_MainWin->mainTimer->setMeter(m::clock::getBeats(), m::clock::getBars());
        u::gui::refreshActionEditor();  // in case the action editor is open
 }
 
@@ -164,7 +164,7 @@ void setBeats(int beats, int bars)
 
 void quantize(int val)
 {
-       clock::setQuantize(val);
+       m::clock::setQuantize(val);
 }
 
 
@@ -173,7 +173,8 @@ void quantize(int val)
 
 void setOutVol(float v, bool gui)
 {
-       mixer::outVol.store(v);
+       m::mh::setOutVol(v);
+       
        if (!gui) {
                Fl::lock();
                G_MainWin->mainIO->setOutVol(v);
@@ -187,7 +188,8 @@ void setOutVol(float v, bool gui)
 
 void setInVol(float v, bool gui)
 {
-       mixer::inVol.store(v);
+       m::mh::setInVol(v);
+
        if (!gui) {
                Fl::lock();
                G_MainWin->mainIO->setInVol(v);
@@ -201,13 +203,12 @@ void setInVol(float v, bool gui)
 
 void clearAllSamples()
 {
-       clock::setStatus(ClockStatus::STOPPED);
-       for (Channel* ch : mixer::channels) {
-               ch->empty();
-               ch->guiChannel->reset();
-       }
-       recorder::clearAll();
-       return;
+       if (!v::gdConfirmWin("Warning", "Clear all samples: are you sure?"))
+               return;
+       G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+       m::clock::setStatus(ClockStatus::STOPPED);
+       m::mh::freeAllChannels();
+       m::recorderHandler::clearAllActions();
 }
 
 
@@ -216,51 +217,68 @@ void clearAllSamples()
 
 void clearAllActions()
 {
-       recorder::clearAll();
-       for (Channel* ch : mixer::channels)
-               ch->hasActions = false;
-       u::gui::updateControls();
+       if (!v::gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+               return;
+       G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+       m::recorderHandler::clearAllActions();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void resetToInitState(bool resetGui, bool createColumns)
+void resetToInitState(bool createColumns)
 {
-       u::gui::closeAllSubwindows();
-       mixer::close();
-       clock::init(conf::samplerate, conf::midiTCfps);
-       mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
-       recorder::init(&mixer::mutex);
-
-#ifdef WITH_VST
-       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex);
-       pluginManager::init(conf::samplerate, kernelAudio::getRealBufSize());
-#endif
+       if (!v::gdConfirmWin("Warning", "Close project: are you sure?"))
+               return;
+       m::init::reset();       
+       m::mixer::enable();
+}
+
 
-       G_MainWin->keyboard->clear();
-       if (createColumns)
-               G_MainWin->keyboard->init();
+/* -------------------------------------------------------------------------- */
 
-       u::gui::updateMainWinLabel(G_DEFAULT_PATCH_NAME);
 
-       if (resetGui)
-               u::gui::updateControls();
+void beatsMultiply()
+{
+       setBeats(m::clock::getBeats() * 2, m::clock::getBars());
+}
+
+void beatsDivide()
+{
+       setBeats(m::clock::getBeats() / 2, m::clock::getBars());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void beatsMultiply()
+void toggleInputRec()
 {
-       setBeats(clock::getBeats() * 2, clock::getBars());
+       if (!m::recManager::toggleInputRec(static_cast<RecTriggerMode>(m::conf::recTriggerMode)))
+               v::gdAlert("No channels armed/available for audio recording.");
 }
 
-void beatsDivide()
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleActionRec()
+{
+       m::recManager::isRecordingAction() ? stopActionRec() : startActionRec();
+}
+
+
+void startActionRec()
+{
+       m::recManager::startActionRec(static_cast<RecTriggerMode>(m::conf::recTriggerMode));
+}
+
+
+void stopActionRec()
 {
-       setBeats(clock::getBeats() / 2, clock::getBars());
+       m::recManager::stopActionRec();
+       u::gui::refreshActionEditor();  // If Action Editor window is open
 }
 
-}}} // giada::c::main::
\ No newline at end of file
+}}} // giada::c::main::
index d3db874174b0d770655886be7a0e2ed526fc9d28..02339db9970d6d85adc1c79be4cb48ce4f5ca964 100644 (file)
@@ -2,13 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another  function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
@@ -61,7 +54,7 @@ void clearAllActions();
 Resets Giada to init state. If resetGui also refresh all widgets. If 
 createColumns also build initial empty columns. */
 
-void resetToInitState(bool resetGui=true, bool createColumns=true);
+void resetToInitState(bool createColumns);
 
 /* beatsDivide/Multiply
 Shrinks or enlarges the number of beats by 2. */
@@ -69,6 +62,29 @@ Shrinks or enlarges the number of beats by 2. */
 void beatsMultiply();
 void beatsDivide();
 
+
+
+
+
+
+
+
+
+void rewind();
+void play();
+
+/* toggleInputRec
+Handles the input recording.*/
+
+void toggleInputRec();
+
+void toggleActionRec();
+void startActionRec();
+void stopActionRec();
+
+
+void toggleMetronome();
+
 }}} // giada::c::main::
 
 #endif
index 906971da24be7721cfd9f9a995dce48ef0bbc391..ed20ecc86b9d611fbeb42e7bead1f77c308c8f78 100644 (file)
 #ifdef WITH_VST
 
 
+#include <cassert>
 #include <FL/Fl.H>
-#include "../core/pluginManager.h"
-#include "../core/pluginHost.h"
-#include "../core/mixer.h"
-#include "../core/plugin.h"
-#include "../core/channel.h"
-#include "../core/const.h"
-#include "../core/conf.h"
-#include "../utils/gui.h"
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/pluginWindow.h"
-#include "../gui/dialogs/pluginList.h"
-#include "../gui/dialogs/warnings.h"
-#include "../gui/dialogs/config.h"
-#include "../gui/dialogs/browser/browserDir.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
+#include "core/mixer.h"
+#include "core/plugin.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "utils/gui.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/pluginWindow.h"
+#include "gui/dialogs/pluginList.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/config.h"
+#include "gui/dialogs/browser/browserDir.h"
 #include "plugin.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using namespace giada::m;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
-namespace c     {
+namespace c {
 namespace plugin 
 {
 namespace
 {
-/* getPluginWindow
-Returns the plugInWindow (GUI-less one) with the parameter list. It might be 
-nullptr if there is no plug-in window shown on screen. */
-
-gdPluginWindow* getPluginWindow(const Plugin* p)
+void updatePluginEditor_(ID pluginId, bool gui)
 {
+       m::model::PluginsLock l(m::model::plugins);
+       const m::Plugin& p = m::model::get(m::model::plugins, pluginId);
+
+       if (p.hasEditor())
+               return;
+
        /* Get the parent window first: the plug-in list. Then, if it exists, get
        the child window - the actual pluginWindow. */
 
-       gdPluginList* parent = static_cast<gdPluginList*>(u::gui::getSubwindow(G_MainWin, WID_FX_LIST));
+       v::gdPluginList* parent = static_cast<v::gdPluginList*>(u::gui::getSubwindow(G_MainWin, WID_FX_LIST));
        if (parent == nullptr)
-               return nullptr;
-       return static_cast<gdPluginWindow*>(u::gui::getSubwindow(parent, p->getId() + 1));
+               return;
+       v::gdPluginWindow* child = static_cast<v::gdPluginWindow*>(u::gui::getSubwindow(parent, pluginId + 1));
+       if (child == nullptr) 
+               return;
+       
+       if (!gui) Fl::lock();
+       child->updateParameters(!gui);
+       if (!gui) Fl::unlock();
 }
 } // {anonymous}
 
@@ -80,78 +87,60 @@ gdPluginWindow* getPluginWindow(const Plugin* p)
 /* -------------------------------------------------------------------------- */
 
 
-void addPlugin(Channel* ch, int index, m::pluginHost::StackType t)
+void addPlugin(int pluginListIndex, ID channelId)
 {
-       if (index >= pluginManager::countAvailablePlugins())
+       if (pluginListIndex >= m::pluginManager::countAvailablePlugins())
                return;
-       std::unique_ptr<Plugin> p = pluginManager::makePlugin(index);
+       std::unique_ptr<m::Plugin> p = m::pluginManager::makePlugin(pluginListIndex);
        if (p != nullptr)
-               pluginHost::addPlugin(std::move(p), t, &mixer::mutex, ch);
+               m::pluginHost::addPlugin(std::move(p), channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void swapPlugins(Channel* ch, int index1, int index2, m::pluginHost::StackType t)
+void swapPlugins(ID pluginId1, ID pluginId2, ID channelId)
 {
-  pluginHost::swapPlugin(index1, index2, t, &mixer::mutex,
-    ch);
+       m::pluginHost::swapPlugin(pluginId1, pluginId2, channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void freePlugin(Channel* ch, int index, m::pluginHost::StackType t)
+void freePlugin(ID pluginId, ID channelId)
 {
-  pluginHost::freePlugin(index, t, &mixer::mutex, ch);
+       m::pluginHost::freePlugin(pluginId, channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setProgram(Plugin* p, int index)
+void setProgram(ID pluginId, int programIndex)
 {
-       p->setCurrentProgram(index);
-
-       /* No need to update plug-in editor if it has one: the plug-in's editor takes
-       care of it on its own. Conversely, update the specific parameter for UI-less 
-       plug-ins. */
-
-       if (p->hasEditor())
-               return;
-
-       gdPluginWindow* child = getPluginWindow(p);
-       if (child == nullptr) 
-               return;
-       
-       child->updateParameters(true);
+       m::pluginHost::setPluginProgram(pluginId, programIndex); 
+       updatePluginEditor_(pluginId, /*gui=*/true); 
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setParameter(Plugin* p, int index, float value, bool gui)
+void setParameter(ID pluginId, int paramIndex, float value, bool gui)
 {
-       p->setParameter(index, value);
+       m::pluginHost::setPluginParameter(pluginId, paramIndex, value); 
+       updatePluginEditor_(pluginId, gui); 
+}
 
-       /* No need to update plug-in editor if it has one: the plug-in's editor takes
-       care of it on its own. Conversely, update the specific parameter for UI-less 
-       plug-ins. */
 
-       if (p->hasEditor())
-               return;
+/* -------------------------------------------------------------------------- */
 
-       gdPluginWindow* child = getPluginWindow(p);
-       if (child == nullptr) 
-               return;
 
-       Fl::lock();
-       child->updateParameter(index, !gui);
-       Fl::unlock();
+void toggleBypass(ID pluginId)
+{
+       m::pluginHost::toggleBypass(pluginId);
 }
 
 
@@ -160,20 +149,20 @@ void setParameter(Plugin* p, int index, float value, bool gui)
 
 void setPluginPathCb(void* data)
 {
-       gdBrowserDir* browser = (gdBrowserDir*) data;
+       v::gdBrowserDir* browser = (v::gdBrowserDir*) data;
 
        if (browser->getCurrentPath() == "") {
-               gdAlert("Invalid path.");
+               v::gdAlert("Invalid path.");
                return;
        }
 
-       if (!conf::pluginPath.empty() && conf::pluginPath.back() != ';')
-               conf::pluginPath += ";";
-       conf::pluginPath += browser->getCurrentPath();
+       if (!m::conf::pluginPath.empty() && m::conf::pluginPath.back() != ';')
+               m::conf::pluginPath += ";";
+       m::conf::pluginPath += browser->getCurrentPath();
 
        browser->do_callback();
 
-       gdConfig* configWin = static_cast<gdConfig*>(u::gui::getSubwindow(G_MainWin, WID_CONFIG));
+       v::gdConfig* configWin = static_cast<v::gdConfig*>(u::gui::getSubwindow(G_MainWin, WID_CONFIG));
        configWin->refreshVstPath();
 }
 
index 7ce3ee16bd836b33852e2c5cd0847ee1c409b91c..aab4107e98600ae1a1c6501488e3bced35bd5bf3 100644 (file)
@@ -2,9 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
@@ -35,7 +32,8 @@
 #ifdef WITH_VST
 
 
-#include "../core/pluginHost.h"
+#include "core/pluginHost.h"
+#include "core/types.h"
 
 
 namespace giada {
@@ -47,11 +45,17 @@ class Channel;
 namespace c {
 namespace plugin 
 {
-void addPlugin(m::Channel* ch, int index, m::pluginHost::StackType t);
-void swapPlugins(m::Channel* ch, int indexP1, int indexP2, m::pluginHost::StackType t);
-void freePlugin(m::Channel* ch, int index, m::pluginHost::StackType t);
-void setParameter(m::Plugin* p, int index, float value, bool gui=true); 
-void setProgram(m::Plugin* p, int index);
+void addPlugin(int pluginListIndex, ID channelId);
+
+void swapPlugins(ID pluginId1, ID pluginId2, ID channelId);
+
+void freePlugin(ID pluginId, ID channelId);
+
+void setParameter(ID pluginId, int paramIndex, float value, bool gui=true); 
+
+void setProgram(ID pluginId, int programIndex);
+
+void toggleBypass(ID pluginId);
 
 /* setPluginPathCb
 Callback attached to the DirBrowser for adding new Plug-in search paths in the
index ec99dc2cecd8e9bffa1b75d05ac07711f6afe82d..c72d582282440dfd6db36458e02e29293d6787fc 100644 (file)
 
 
 #include <cassert>
-#include "../gui/dialogs/warnings.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
-#include "../core/const.h"
-#include "../core/clock.h"
-#include "../core/kernelMidi.h"
-#include "../core/channel.h"
-#include "../core/recorderHandler.h"
-#include "../core/recorder.h"
-#include "../core/action.h"
-#include "../core/mixer.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../utils/gui.h"
-#include "../utils/log.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "core/model/model.h"
+#include "core/kernelMidi.h"
+#include "core/recorderHandler.h"
+#include "core/recorder.h"
+#include "core/action.h"
+#include "core/mixer.h"
+#include "utils/gui.h"
+#include "utils/log.h"
 #include "recorder.h"
 
 
@@ -48,58 +49,57 @@ namespace giada {
 namespace c {
 namespace recorder 
 {
-void clearAllActions(geChannel* gch)
+void clearAllActions(ID channelId)
 {
-       if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+       if (!v::gdConfirmWin("Warning", "Clear all actions: are you sure?"))
                return;
-       gch->ch->kill(0);
-       m::recorder::clearChannel(gch->ch->index);
-       updateChannel(gch);
+       m::recorder::clearChannel(channelId);
+       updateChannel(channelId, /*updateActionEditor=*/true);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void clearVolumeActions(geChannel* gch)
+void clearVolumeActions(ID channelId)
 {
-       if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+       if (!v::gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
                return;
-       m::recorder::clearActions(gch->ch->index, m::MidiEvent::ENVELOPE);
-       updateChannel(gch);
+       m::recorder::clearActions(channelId, m::MidiEvent::ENVELOPE);
+       updateChannel(channelId, /*updateActionEditor=*/true);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void clearStartStopActions(geChannel* gch)
+void clearStartStopActions(ID channelId)
 {
-       if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+       if (!v::gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
                return;
-       gch->ch->kill(0);
-       m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_ON);
-       m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_OFF);
-       m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_KILL);
-       updateChannel(gch);
+       m::recorder::clearActions(channelId, m::MidiEvent::NOTE_ON);
+       m::recorder::clearActions(channelId, m::MidiEvent::NOTE_OFF);
+       m::recorder::clearActions(channelId, m::MidiEvent::NOTE_KILL);
+       updateChannel(channelId, /*updateActionEditor=*/true);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void updateChannel(geChannel* gch, bool refreshActionEditor)
+void updateChannel(ID channelId, bool updateActionEditor)
 {
-       gch->ch->hasActions = m::recorder::hasActions(gch->ch->index);
-       if (!gch->ch->hasActions)
-               gch->ch->readActions = false;
-
-       if (gch->ch->type == ChannelType::SAMPLE) {
-               geSampleChannel* gsch = static_cast<geSampleChannel*>(gch);
-               gsch->ch->hasActions ? gsch->showActionButton() : gsch->hideActionButton();
-       }
-       if (refreshActionEditor)
-               u::gui::refreshActionEditor(); // refresh a.editor window, it could be open
+       /* TODO - optimization needed. This functions swaps a channel only to set a 
+       boolean flag. Query the channel first and swap it only if the flag has
+       actually changed. */
+
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               c.hasActions = m::recorder::hasActions(channelId);
+       });
+                               
+       if (updateActionEditor)
+               u::gui::refreshActionEditor();
 }
 
-}}} // giada::c::recorder::
\ No newline at end of file
+}}} // giada::c::recorder::
index 54e2361ba8c5fbe11e4878a18a89be6c534618ac..0a7da33960d022f0bb8aadfc6f095b10e2125362 100644 (file)
 #define G_GLUE_RECORDER_H
 
 
-class geChannel;
-
-
 namespace giada {
 namespace c {
 namespace recorder 
 {
-void clearAllActions(geChannel* gch);
-void clearVolumeActions(geChannel* gch);
-void clearStartStopActions(geChannel* gch);
-void updateChannel(geChannel* gch, bool refreshActionEditor=true);
+void clearAllActions(ID channelId);
+void clearVolumeActions(ID channelId);
+void clearStartStopActions(ID channelId);
+void updateChannel(ID channelId, bool updateActionEditor);
 }}} // giada::c::recorder::
 
 #endif
index 1252b10168399cb1efb712287379459990b9204a..8028160facb1271327d64fa718c4a717c0413b3d 100644 (file)
 
 #include <cassert>
 #include <FL/Fl.H>
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/sampleEditor.h"
-#include "../gui/dialogs/warnings.h"
-#include "../gui/elems/basics/button.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/shiftTool.h"
-#include "../gui/elems/sampleEditor/waveform.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "../core/sampleChannel.h"
-#include "../core/waveFx.h"
-#include "../core/wave.h"
-#include "../core/waveManager.h"
-#include "../core/const.h"
-#include "../utils/gui.h"
-#include "../utils/log.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/button.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/shiftTool.h"
+#include "gui/elems/sampleEditor/waveform.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/waveFx.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include "core/mixerHandler.h"
+#include "core/const.h"
+#include "utils/gui.h"
+#include "utils/log.h"
 #include "channel.h"
 #include "sampleEditor.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -60,19 +63,41 @@ namespace sampleEditor
 {
 namespace
 {
-       /* waveBuffer
-       A Wave used during cut/copy/paste operations. */
+/* waveBuffer
+A Wave used during cut/copy/paste operations. */
 
-       std::unique_ptr<Wave> waveBuffer_;
+std::unique_ptr<m::Wave> waveBuffer_;
+
+
+/* -------------------------------------------------------------------------- */
+
+/* resetBeginEnd_
+Resets begin/end points when model has changed and a new Channel pointer is
+needed for the operation. */
+
+void resetBeginEnd_(ID channelId)
+{
+       Frame begin;
+       Frame end;
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               begin = static_cast<m::SampleChannel&>(c).begin;
+               end   = static_cast<m::SampleChannel&>(c).end;
+       });
+
+       setBeginEnd(channelId, begin, end);
+}
 }; // {anonymous}
 
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-gdSampleEditor* getSampleEditorWindow()
+v::gdSampleEditor* getSampleEditorWindow()
 {
-       gdSampleEditor* se = static_cast<gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+       v::gdSampleEditor* se = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
        assert(se != nullptr);
        return se;
 }
@@ -81,192 +106,180 @@ gdSampleEditor* getSampleEditorWindow()
 /* -------------------------------------------------------------------------- */
 
 
-void setBeginEnd(m::SampleChannel* ch, int b, int e)
+void setBeginEnd(ID channelId, int b, int e)
 {
-       ch->setBegin(b);
-       ch->setEnd(e);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       Fl::lock();
-       gdEditor->rangeTool->refresh();
-       Fl::unlock();
-
-       gdEditor->waveTools->waveform->recalcPoints();
-       gdEditor->waveTools->waveform->clearSel();
-       gdEditor->waveTools->waveform->redraw();
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               static_cast<m::SampleChannel&>(c).setBegin(b);
+               static_cast<m::SampleChannel&>(c).setEnd(e);
+       });
+
+       getSampleEditorWindow()->rebuild();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void cut(m::SampleChannel* ch, int a, int b)
+void cut(ID channelId, ID waveId, int a, int b)
 {
-       copy(ch, a, b);
-       if (!m::wfx::cut(*ch->wave, a, b)) {
-               gdAlert("Unable to cut the sample!");
-               return;
-       }
-       setBeginEnd(ch, ch->getBegin(), ch->getEnd());
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->clearSel();
-       gdEditor->waveTools->waveform->refresh();
-       gdEditor->updateInfo();
+       copy(waveId, a, b);
+       m::wfx::cut(waveId, a, b);
+       resetBeginEnd_(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void copy(m::SampleChannel* ch, int a, int b)
+void copy(ID waveId, int a, int b)
 {
-       waveBuffer_ = m::waveManager::createFromWave(ch->wave.get(), a, b);
+       m::model::WavesLock lock(m::model::waves);
+
+       waveBuffer_ = m::waveManager::createFromWave(m::model::get(m::model::waves, waveId), a, b);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void paste(m::SampleChannel* ch, int a)
+void paste(ID channelId, ID waveId, int a)
 {
        if (!isWaveBufferFull()) {
-               gu_log("[sampleEditor::paste] Buffer is empty, nothing to paste\n");
+               u::log::print("[sampleEditor::paste] Buffer is empty, nothing to paste\n");
                return;
        }
-       
-       m::wfx::paste(*waveBuffer_.get(), *ch->wave.get(), a);
+
+       m::wfx::paste(*waveBuffer_, waveId, a);
 
        /* Shift begin/end points to keep the previous position. */
 
-       int delta = waveBuffer_->getSize();
-       if (a < ch->getBegin() && a < ch->getEnd())
-               setBeginEnd(ch, ch->getBegin() + delta, ch->getEnd() + delta);
+       int   delta = waveBuffer_->getSize();
+       Frame begin;
+       Frame end;
+
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               begin = static_cast<m::SampleChannel&>(c).begin;
+               end   = static_cast<m::SampleChannel&>(c).end;
+       });
+
+       if (a < begin && a < end)
+               setBeginEnd(channelId, begin + delta, end + delta);
        else
-       if (a < ch->getEnd())
-               setBeginEnd(ch, ch->getBegin(), ch->getEnd() + delta);
+       if (a < end)
+               setBeginEnd(channelId, begin, end + delta);
 
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->clearSel();
-       gdEditor->waveTools->waveform->refresh();
-       gdEditor->updateInfo();
+       getSampleEditorWindow()->rebuild();
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 
-void silence(m::SampleChannel* ch, int a, int b)
+void silence(ID waveId, int a, int b)
 {
-       m::wfx::silence(*ch->wave, a, b);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->refresh();
+       m::wfx::silence(waveId, a, b);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void fade(m::SampleChannel* ch, int a, int b, int type)
+void fade(ID waveId, int a, int b, int type)
 {
-       m::wfx::fade(*ch->wave, a, b, type);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->refresh();
+       m::wfx::fade(waveId, a, b, type);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void smoothEdges(m::SampleChannel* ch, int a, int b)
+void smoothEdges(ID waveId, int a, int b)
 {
-       m::wfx::smooth(*ch->wave, a, b);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->refresh();
+       m::wfx::smooth(waveId, a, b);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void reverse(m::SampleChannel* ch, int a, int b)
+void reverse(ID waveId, int a, int b)
 {
-       m::wfx::reverse(*ch->wave, a, b);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->refresh();
+       m::wfx::reverse(waveId, a, b);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void normalizeHard(m::SampleChannel* ch, int a, int b)
+void normalizeHard(ID waveId, int a, int b)
 {
-       m::wfx::normalizeHard(*ch->wave, a, b);
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->refresh();
+       m::wfx::normalizeHard(waveId, a, b);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void trim(m::SampleChannel* ch, int a, int b)
+void trim(ID channelId, ID waveId, int a, int b)
 {
-       if (!m::wfx::trim(*ch->wave, a, b)) {
-               gdAlert("Unable to trim the sample!");
-               return;
-       }
-       setBeginEnd(ch, ch->getBegin(), ch->getEnd());
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->clearSel();
-       gdEditor->waveTools->waveform->refresh();
-       gdEditor->updateInfo();
+       m::wfx::trim(waveId, a, b);
+       resetBeginEnd_(channelId);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setPlayHead(m::SampleChannel* ch, int f)
+void setPlayHead(ID channelId, Frame f)
 {
-       ch->trackerPreview = f;
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->waveTools->waveform->redraw();
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               static_cast<m::SampleChannel&>(c).trackerPreview.store(f);
+       });
+       getSampleEditorWindow()->refresh();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void setPreview(m::SampleChannel* ch, PreviewMode mode)
+void setPreview(ID channelId, PreviewMode mode)
 {
-       ch->previewMode = mode;
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->play->value(!gdEditor->play->value());
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               static_cast<m::SampleChannel&>(c).previewMode = mode;
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void rewindPreview(m::SampleChannel* ch)
+void rewindPreview(ID channelId)
 {
-       geWaveform* waveform = getSampleEditorWindow()->waveTools->waveform;
-       if (waveform->isSelected() && ch->trackerPreview != waveform->getSelectionA())
-               setPlayHead(ch, waveform->getSelectionA());
-       else
-               setPlayHead(ch, 0);
+       setPlayHead(channelId, 0);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void toNewChannel(m::SampleChannel* ch, int a, int b)
+void toNewChannel(ID channelId, int a, int b)
 {
-       m::SampleChannel* newCh = static_cast<m::SampleChannel*>(c::channel::addChannel(
-               ch->guiChannel->getColumnIndex(), ChannelType::SAMPLE, G_GUI_CHANNEL_H_1));
-
-       newCh->pushWave(m::waveManager::createFromWave(ch->wave.get(), a, b));
-       newCh->guiChannel->update();
+       ID columnId = G_MainWin->keyboard->getChannel(channelId)->getColumnId();
+       ID waveId;
+       
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               waveId = static_cast<m::SampleChannel&>(c).waveId;
+       });
+
+       m::model::onGet(m::model::waves, waveId, [&](m::Wave& w)
+       {
+               m::mh::addAndLoadChannel(columnId, m::waveManager::createFromWave(w, a, b));
+       });
 }
 
 
@@ -282,14 +295,51 @@ bool isWaveBufferFull()
 /* -------------------------------------------------------------------------- */
 
 
-void shift(m::SampleChannel* ch, int offset)
+void reload(ID channelId, ID waveId)
 {
-       m::wfx::shift(*ch->wave, offset - ch->shift);
-       ch->shift = offset;
-       gdSampleEditor* gdEditor = getSampleEditorWindow();
-       gdEditor->shiftTool->refresh();
-       gdEditor->waveTools->waveform->refresh();       
+       if (!v::gdConfirmWin("Warning", "Reload sample: are you sure?"))
+               return;
+
+       std::string wavePath;
+       Frame       waveSize;
+       m::model::onGet(m::model::waves, waveId, [&](m::Wave& w)
+       {
+               wavePath = w.getPath();
+               waveSize = w.getSize();
+       });
+
+       if (channel::loadChannel(channelId, wavePath) != G_RES_OK)
+               return;
+
+       ID newWaveId;
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               newWaveId = sc.waveId;
+       });
+
+       getSampleEditorWindow()->setWaveId(newWaveId);
+       getSampleEditorWindow()->rebuild();
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+
+void shift(ID channelId, ID waveId, int offset)
+{
+       Frame shift;
+
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               shift = static_cast<m::SampleChannel&>(c).shift;
+       });
+       
+       m::wfx::shift(waveId, offset - shift);
+
+       m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               static_cast<m::SampleChannel&>(c).shift = offset;
+       });
+}
 }}}; // giada::c::sampleEditor::
index a7339f815db3780c5b074effe5a731639c3aebeb..62798eaed263de314d770d36b3eb5e114b716da7 100644 (file)
 #define G_GLUE_SAMPLE_EDITOR_H
 
 
-#include "../core/types.h"
-
-
-class geWaveform;
+#include "core/types.h"
 
 
 namespace giada {
-namespace m
-{
-class SampleChannel;
-}
 namespace c {
 namespace sampleEditor 
 {
 /* setBeginEnd
 Sets start/end points in the sample editor. */
 
-void setBeginEnd(m::SampleChannel* ch, int b, int e);
-
-void cut(m::SampleChannel* ch, int a, int b);
-void copy(m::SampleChannel* ch, int a, int b);
-
-/* paste
-Pastes what's defined in m_copyBuffer into channel 'ch' at point 'a'. If 
-m_copyBuffer is empty, does nothing. */
+void setBeginEnd(ID channelId, int b, int e);
 
-void paste(m::SampleChannel* ch, int a);
+void cut(ID channelId, ID waveId, int a, int b);
+void copy(ID waveId, int a, int b);
+void paste(ID channelId, ID waveId, int a);
 
-void trim(m::SampleChannel* ch, int a, int b);
-void reverse(m::SampleChannel* ch, int a, int b);
-void normalizeHard(m::SampleChannel* ch, int a, int b);
-void silence(m::SampleChannel* ch, int a, int b);
-void fade(m::SampleChannel* ch, int a, int b, int type);
-void smoothEdges(m::SampleChannel* ch, int a, int b);
-void shift(m::SampleChannel* ch, int offset);
+void trim(ID channelId, ID waveId, int a, int b);
+void reverse(ID waveId, int a, int b);
+void normalizeHard(ID waveId, int a, int b);
+void silence(ID waveId, int a, int b);
+void fade(ID waveId, int a, int b, int type);
+void smoothEdges(ID waveId, int a, int b);
+void shift(ID channelId, ID waveId, int offset);
+void reload(ID channelId, ID waveId);
 
 bool isWaveBufferFull();
 
 /* setPlayHead
 Changes playhead's position. Used in preview. */
 
-void setPlayHead(m::SampleChannel* ch, int f);
+void setPlayHead(ID channelId, Frame f);
 
-void setPreview(m::SampleChannel* ch, PreviewMode mode);
-void rewindPreview(m::SampleChannel* ch);
+void setPreview(ID channelId, PreviewMode mode);
+void rewindPreview(ID channelId);
 
 /* toNewChannel
 Copies the selected range into a new sample channel. */
 
-void toNewChannel(m::SampleChannel* ch, int a, int b);
+void toNewChannel(ID channelId, int a, int b);
 }}}; // giada::c::sampleEditor::
 
 #endif
index d04c49475c9aa7ed7a22d663a337475cce7eaa35..8bc888c00439da8b881f99f40dd5d9f033099c50 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../core/mixer.h"
-#include "../core/mixerHandler.h"
-#include "../core/channel.h"
-#include "../core/recorderHandler.h"
-#include "../core/pluginManager.h"
-#include "../core/pluginHost.h"
-#include "../core/plugin.h"
-#include "../core/conf.h"
-#include "../core/patch.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../core/waveManager.h"
-#include "../core/clock.h"
-#include "../core/wave.h"
-#include "../utils/gui.h"
-#include "../utils/log.h"
-#include "../utils/string.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/mainWindow.h"
-#include "../gui/dialogs/warnings.h"
-#include "../gui/dialogs/browser/browserSave.h"
-#include "../gui/dialogs/browser/browserLoad.h"
+#include <cassert>
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/mixer.h"
+#include "core/wave.h"
+#include "core/mixerHandler.h"
+#include "core/recorderHandler.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
+#include "core/plugin.h"
+#include "core/conf.h"
+#include "core/patch.h"
+#include "core/init.h"
+#include "core/waveManager.h"
+#include "core/clock.h"
+#include "core/wave.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/string.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/mainWindow.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/browser/browserLoad.h"
 #include "main.h"
 #include "channel.h"
 #include "storage.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using std::string;
-using std::vector;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -68,138 +68,64 @@ namespace storage
 {
 namespace
 {
-#ifdef WITH_VST
-
-void fillPatchGlobalsPlugins_(vector<m::Plugin*> stack, vector<m::patch::plugin_t>& patch)
-{
-       using namespace giada::m;
-
-       for (const Plugin* plugin : stack) {
-               patch::plugin_t ppl;
-               ppl.path   = plugin->getUniqueId();
-               ppl.bypass = plugin->isBypassed();
-               for (int k=0; k<plugin->getNumParameters(); k++)
-                       ppl.params.push_back(plugin->getParameter(k));
-               patch.push_back(ppl);
-       }
-}
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void fillPatchColumns_()
+std::string makeWavePath_(const std::string& base, const m::Wave& w, int k)
 {
-       using namespace giada::m;
-
-       for (unsigned i=0; i<G_MainWin->keyboard->getTotalColumns(); i++) {
-               geColumn* gCol = G_MainWin->keyboard->getColumn(i);
-               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 (const Channel* mixerChannel : mixer::channels) {
-                               if (colChannel == mixerChannel) {
-                                       pCol.channels.push_back(mixerChannel->index);
-                                       break;
-                               }
-                       }
-               }
-               patch::columns.push_back(pCol);
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
+       return base + G_SLASH + w.getBasename(/*ext=*/false) + "-" + std::to_string(k) + "." +  w.getExtension();
+} 
 
 
-void fillPatchChannels_(bool isProject)
+bool isWavePathUnique_(const m::Wave& skip, const std::string& path)
 {
-       using namespace giada::m;
+       m::model::WavesLock l(m::model::waves);
 
-       for (unsigned i=0; i<mixer::channels.size(); i++)
-               mixer::channels.at(i)->writePatch(i, isProject);
+       for (const m::Wave* w : m::model::waves)
+               if (w->id != skip.id && w->getPath() == path)
+                       return false;
+       return true;
 }
 
-
-/* -------------------------------------------------------------------------- */
-
-
-void fillPatchGlobals_(const string& name)
+std::string makeUniqueWavePath_(const std::string& base, const m::Wave& w)
 {
-       using namespace giada::m;
-
-       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.load();
-       patch::masterVolOut = mixer::outVol.load();
-       patch::metronome    = mixer::isMetronomeOn();
-
-#ifdef WITH_VST
-
-       fillPatchGlobalsPlugins_(pluginHost::getStack(pluginHost::StackType::MASTER_IN),
-                       patch::masterInPlugins);
-       fillPatchGlobalsPlugins_(pluginHost::getStack(pluginHost::StackType::MASTER_OUT),
-                       patch::masterOutPlugins);
+       std::string path = base + G_SLASH + w.getBasename(/*ext=*/true);
+       if (isWavePathUnique_(w, path))
+               return path;
 
-#endif
+       int k = 0;
+       path = makeWavePath_(base, w, k);
+       while (!isWavePathUnique_(w, path))
+               path = makeWavePath_(base, w, k++);
+       
+       return path;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool savePatch_(const string& fullPath, const string& name, bool isProject)
+bool savePatch_(const std::string& path, const std::string& name, bool isProject)
 {
-       using namespace giada::m;
-
-       patch::init();
-
-       fillPatchGlobals_(name);
-       fillPatchChannels_(isProject);
-       fillPatchColumns_();
-
-       if (patch::write(fullPath)) {
-               u::gui::updateMainWinLabel(name);
-               gu_log("[savePatch] patch saved as %s\n", fullPath.c_str());
-               return true;
-       }
-       return false;
+       if (!m::patch::write(name, path, isProject))
+               return false;
+       u::gui::updateMainWinLabel(name);
+       m::conf::patchPath = isProject ? u::fs::getUpDir(u::fs::getUpDir(path)) : u::fs::dirname(path);
+       m::patch::name     = name;
+       u::log::print("[savePatch] patch saved as %s\n", path.c_str());
+       return true;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string makeSamplePath_(const string& base, const Wave& w, int k)
-{
-       return base + G_SLASH + w.getBasename(false) + "-" + u::string::iToString(k) + "." +  w.getExtension();
-} 
-
-
-string makeUniqueSamplePath_(const string& base, const m::SampleChannel* ch)
+void saveWavesToProject_(const std::string& base)
 {
-       using namespace giada::m;
-
-       string path = base + G_SLASH + ch->wave->getBasename(true);
-       if (mh::uniqueSamplePath(ch, path))
-               return path;
-
-       int k = 0;
-       path = makeSamplePath_(base, *ch->wave.get(), k);
-       while (!mh::uniqueSamplePath(ch, path))
-               path = makeSamplePath_(base, *ch->wave.get(), k++);
-       return path;
+       for (size_t i = 0; i < m::model::waves.size(); i++) {
+               m::model::onSwap(m::model::waves, m::model::getId(m::model::waves, i), [&](m::Wave& w)
+               {
+                       w.setPath(makeUniqueWavePath_(base, w));
+                       m::waveManager::save(w, w.getPath()); // TODO - error checking  
+               });
+       }
 }
 } // {anonymous}
 
@@ -211,25 +137,23 @@ string makeUniqueSamplePath_(const string& base, const m::SampleChannel* ch)
 
 void savePatch(void* data)
 {
-       gdBrowserSave* browser = (gdBrowserSave*) data;
-       string name            = gu_stripExt(browser->getName());
-       string fullPath        = browser->getCurrentPath() + G_SLASH + name + ".gptc";
+       v::gdBrowserSave* browser = (v::gdBrowserSave*) data;
+       std::string name          = u::fs::stripExt(browser->getName());
+       std::string fullPath      = browser->getCurrentPath() + G_SLASH + name + ".gptc";
 
        if (name == "") {
-               gdAlert("Please choose a file name.");
+               v::gdAlert("Please choose a file name.");
                return;
        }
 
-       if (gu_fileExists(fullPath))
-               if (!gdConfirmWin("Warning", "File exists: overwrite?"))
+       if (u::fs::fileExists(fullPath))
+               if (!v::gdConfirmWin("Warning", "File exists: overwrite?"))
                        return;
 
-       if (savePatch_(fullPath, name, false)) {  // false == not a project
-               m::conf::patchPath = gu_dirname(fullPath);
+       if (savePatch_(fullPath, name, /*isProject=*/false))
                browser->do_callback();
-       }
        else
-               gdAlert("Unable to save the patch!");
+               v::gdAlert("Unable to save the patch!");
 }
 
 
@@ -238,87 +162,72 @@ void savePatch(void* data)
 
 void loadPatch(void* data)
 {
-       using namespace giada::m;
-
-       gdBrowserLoad* browser = (gdBrowserLoad*) data;
-       string fullPath        = browser->getSelectedItem();
-       bool isProject         = gu_isProject(browser->getSelectedItem());
+       v::gdBrowserLoad* browser = (v::gdBrowserLoad*) data;
+       std::string fullPath      = browser->getSelectedItem();
+       bool isProject            = u::fs::isProject(browser->getSelectedItem());
 
        browser->showStatusBar();
 
-       gu_log("[glue] loading %s...\n", fullPath.c_str());
+       u::log::print("[glue] loading %s...\n", fullPath.c_str());
 
-       string fileToLoad = fullPath;  // patch file to read from
-       string basePath   = "";        // base path, in case of reading from a project
+       std::string fileToLoad = fullPath;  // patch file to read from
+       std::string basePath   = "";        // base path, in case of reading from a project
        if (isProject) {
-               fileToLoad = fullPath + G_SLASH + gu_stripExt(gu_basename(fullPath)) + ".gptc";
+               fileToLoad = fullPath + G_SLASH + u::fs::stripExt(u::fs::basename(fullPath)) + ".gptc";
                basePath   = fullPath + G_SLASH;
        }
 
-       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.");
+       /* Verify that the patch file is valid first. */
+
+       int ver = m::patch::verify(fileToLoad); 
+       if (ver != G_PATCH_OK) {
+               if (ver == G_PATCH_UNREADABLE)
+                       v::gdAlert("This patch is unreadable.");
                else
-               if (res == PATCH_INVALID)
-                       isProject ? gdAlert("This project is not valid.") : gdAlert("This patch is not valid.");
+               if (ver == G_PATCH_INVALID)
+                       v::gdAlert("This patch is not valid.");
+               else
+               if (ver == G_PATCH_UNSUPPORTED)
+                       v::gdAlert("This patch format is no longer supported.");
                browser->hideStatusBar();
                return;
        }
 
-       /* Close all other windows. This prevents problems if plugin windows are 
-       open. */
-
-       u::gui::closeAllSubwindows();
-
-       /* Reset the system. False(1): don't update the gui right now. False(2): do 
-       not create empty columns. */
-
-       c::main::resetToInitState(false, false);
-
-       browser->setStatusBar(0.1f);
+       /* Then reset the system and read the patch. */
 
-       /* Add common stuff, columns and channels. Also increment the progress bar by 
-       0.8 / total_channels steps. */
+       m::init::reset();
 
-       float steps = 0.8 / patch::channels.size();
-       
-       for (const patch::column_t& col : patch::columns) {
-               G_MainWin->keyboard->addColumn(col.width);
-               for (const patch::channel_t& pch : patch::channels) {
-                       if (pch.column == col.index) {
-                               Channel* ch = c::channel::addChannel(pch.column, static_cast<ChannelType>(pch.type), pch.size);
-                               ch->readPatch(basePath, pch);
-                       }
-                       browser->setStatusBar(steps);
-               }
+       if (m::patch::read(fileToLoad, basePath) != G_PATCH_OK) {
+               v::gdAlert("This patch is unreadable.");
+               m::mixer::enable();
+               return;
        }
 
-       /* Prepare Mixer and Recorder. The latter has to recompute the actions' 
-       positions if the current samplerate != patch samplerate.*/
+       /* Prepare Mixer and Recorder. The latter has to recompute the actions 
+       positions if the current samplerate != patch samplerate. */
+
+       m::mh::updateSoloCount();
+       m::recorderHandler::updateSamplerate(m::conf::samplerate, m::patch::samplerate);
 
-       mh::updateSoloCount();
-       mh::readPatch();
-       recorderHandler::updateSamplerate(conf::samplerate, patch::samplerate);
+       /* Save patchPath by taking the last dir of the broswer, in order to reuse
+       it the next time. */
 
-       /* Save patchPath by taking the last dir of the broswer, in order to reuse it 
-       the next time. */
+       m::conf::patchPath = u::fs::dirname(fullPath);
 
-       conf::patchPath = gu_dirname(fullPath);
+       /* Mixer is ready to go back online. */
 
-       /* Refresh GUI. */
+       m::mixer::enable();
 
-       u::gui::updateControls();
-       u::gui::updateMainWinLabel(patch::name);
+       /* Update Main Window's title. */
 
-       browser->setStatusBar(0.1f);
+       u::gui::updateMainWinLabel(m::patch::name);
 
-       gu_log("[glue] patch loaded successfully\n");
+       u::log::print("[glue] patch loaded successfully\n");
 
 #ifdef WITH_VST
 
-       if (pluginManager::hasMissingPlugins())
-               gdAlert("Some plugins were not loaded successfully.\nCheck the plugin browser to know more.");
+       if (m::pluginManager::hasMissingPlugins())
+               v::gdAlert("Some plugins were not loaded successfully.\nCheck the plugin browser to know more.");
 
 #endif
 
@@ -331,55 +240,34 @@ void loadPatch(void* data)
 
 void saveProject(void* data)
 {
-       using namespace giada::m;
-
-       gdBrowserSave* browser = (gdBrowserSave*) data;
-       string name            = gu_stripExt(browser->getName());
-       string folderPath      = browser->getCurrentPath();
-       string fullPath        = folderPath + G_SLASH + name + ".gprj";
+       v::gdBrowserSave* browser = (v::gdBrowserSave*) data;
+       std::string name            = u::fs::stripExt(browser->getName());
+       std::string folderPath      = browser->getCurrentPath();
+       std::string fullPath        = folderPath + G_SLASH + name + ".gprj";
+       std::string gptcPath        = fullPath + G_SLASH + name + ".gptc";
 
        if (name == "") {
-               gdAlert("Please choose a project name.");
+               v::gdAlert("Please choose a project name.");
                return;
        }
 
-       if (gu_isProject(fullPath) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
+       if (u::fs::isProject(fullPath) && !v::gdConfirmWin("Warning", "Project exists: overwrite?"))
                return;
 
-       if (!gu_dirExists(fullPath) && !gu_mkdir(fullPath)) {
-               gu_log("[saveProject] Unable to make project directory!\n");
+       if (!u::fs::dirExists(fullPath) && !u::fs::mkdir(fullPath)) {
+               u::log::print("[saveProject] Unable to make project directory!\n");
                return;
        }
 
-       gu_log("[saveProject] Project dir created: %s\n", fullPath.c_str());
-
-       /* Copy all samples inside the folder. Takes and logical ones are saved via 
-       saveSample(). Update the new sample path: everything now comes from the 
-       project folder (folderPath). Also make sure the file path is unique inside the 
-       project folder.*/
-
-       for (const Channel* ch : mixer::channels) {
-
-               if (ch->type == ChannelType::MIDI)
-                       continue;
-
-               const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
-
-               if (sch->wave == nullptr)
-                       continue;
-
-               sch->wave->setPath(makeUniqueSamplePath_(fullPath, sch));
+       u::log::print("[saveProject] Project dir created: %s\n", fullPath.c_str());
 
-               gu_log("[saveProject] Save file to %s\n", sch->wave->getPath().c_str());
+       saveWavesToProject_(fullPath);
 
-               waveManager::save(sch->wave.get(), sch->wave->getPath()); // TODO - error checking      
-       }
-
-       string gptcPath = fullPath + G_SLASH + name + ".gptc";
-       if (savePatch_(gptcPath, name, true)) // true == it's a project
+       if (savePatch_(gptcPath, name, /*isProject=*/true))
                browser->do_callback();
        else
-               gdAlert("Unable to save the project!");
+               v::gdAlert("Unable to save the project!");
+
 }
 
 
@@ -388,22 +276,19 @@ void saveProject(void* data)
 
 void loadSample(void* data)
 {
-       gdBrowserLoad* browser = (gdBrowserLoad*) data;
-       string fullPath        = browser->getSelectedItem();
+       v::gdBrowserLoad* browser  = (v::gdBrowserLoad*) data;
+       std::string       fullPath = browser->getSelectedItem();
 
        if (fullPath.empty())
                return;
 
-       int res = c::channel::loadChannel(static_cast<m::SampleChannel*>(browser->getChannel()), 
-               fullPath);
+       int res = c::channel::loadChannel(browser->getChannelId(), fullPath);
 
        if (res == G_RES_OK) {
-               m::conf::samplePath = gu_dirname(fullPath);
+               m::conf::samplePath = u::fs::dirname(fullPath);
                browser->do_callback();
                G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
        }
-       else
-               G_MainWin->keyboard->printChannelMessage(res);
 }
 
 
@@ -412,34 +297,50 @@ void loadSample(void* data)
 
 void saveSample(void* data)
 {
-       using namespace giada::m;
-
-       gdBrowserSave* browser = (gdBrowserSave*) data;
-       string name            = browser->getName();
-       string folderPath      = browser->getCurrentPath();
+       v::gdBrowserSave* browser = (v::gdBrowserSave*) data;
+       std::string name          = browser->getName();
+       std::string folderPath    = browser->getCurrentPath();
 
        if (name == "") {
-               gdAlert("Please choose a file name.");
+               v::gdAlert("Please choose a file name.");
                return;
        }
 
-       /* bruteforce check extension. */
+       std::string filePath = folderPath + G_SLASH + u::fs::stripExt(name) + ".wav";
 
-       string filePath = folderPath + G_SLASH + gu_stripExt(name) + ".wav";
+       if (u::fs::fileExists(filePath) && !v::gdConfirmWin("Warning", "File exists: overwrite?"))
+               return;
 
-       if (gu_fileExists(filePath))
-               if (!gdConfirmWin("Warning", "File exists: overwrite?"))
-                       return;
+       ID waveId;
+       m::model::onGet(m::model::channels, browser->getChannelId(), [&](m::Channel& c)
+       {
+               waveId = static_cast<m::SampleChannel&>(c).waveId;
+       });
 
-       SampleChannel* ch = static_cast<SampleChannel*>(browser->getChannel());
+       size_t waveIndex = m::model::getIndex(m::model::waves, waveId);
 
-       if (waveManager::save(ch->wave.get(), filePath)) {
-               gu_log("[saveSample] sample saved to %s\n", filePath.c_str());
-               conf::samplePath = gu_dirname(filePath);
-               browser->do_callback();
+       std::unique_ptr<m::Wave> wave = m::model::waves.clone(waveIndex);
+
+       if (!m::waveManager::save(*wave.get(), filePath)) {
+               v::gdAlert("Unable to save this sample!");
+               return;
        }
-       else
-               gdAlert("Unable to save this sample!");
-}
+       
+       u::log::print("[saveSample] sample saved to %s\n", filePath.c_str());
+       
+       /* Update last used path in conf, so that it can be reused next time. */
+
+       m::conf::samplePath = u::fs::dirname(filePath);
+
+       /* Update logical and edited states in Wave. */
 
-}}} // giada::c::storage::
\ No newline at end of file
+       wave->setLogical(false);
+       wave->setEdited(false);
+
+       m::model::waves.swap(std::move(wave), waveIndex);
+
+       /* Finally close the browser. */
+
+       browser->do_callback();
+}
+}}} // giada::c::storage::
index bb7ba34c8791d913d2849a0e2394a9733829467d..afe7723f706af069d50ccaf4e395aeb994bdd666 100644 (file)
@@ -76,7 +76,7 @@ void startSeq(bool gui)
                        break;
        }
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
        kernelAudio::jackStart();
 #endif
 
@@ -95,7 +95,7 @@ void stopSeq(bool gui)
 {
        mh::stopSequencer();
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
        kernelAudio::jackStop();
 #endif
 
diff --git a/src/glue/transport.h b/src/glue/transport.h
deleted file mode 100644 (file)
index 69713bc..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2019 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
-
-
-namespace giada {
-namespace c {
-namespace transport 
-{
-void startStopSeq(bool gui=true);
-void startSeq(bool gui=true);
-void stopSeq(bool gui=true);
-void rewindSeq(bool gui=true, bool notifyJack=true);
-void toggleMetronome(bool gui=true);
-}}} // giada::c::transport::
-
-
-#endif
index 841d1f1e1b8da7a8caa81194364001ef2d8d6651..f44ba9c67989d8578221572dffa511b814fd08ec 100644 (file)
 #include <FL/Fl_Pixmap.H>
 #include <FL/fl_draw.H>
 #include <jansson.h>
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/graphics.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/graphics.h"
 #ifdef WITH_VST
-  #include "../../deps/juce-config.h"
+#include "deps/juce-config.h"
 #endif
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../../utils/ver.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/box.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "utils/ver.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/box.h"
 #include "about.h"
 
 
-using namespace giada;
-
-
+namespace giada {
+namespace v 
+{
 gdAbout::gdAbout()
 #ifdef WITH_VST
 : gdWindow(340, 435, "About Giada")
@@ -140,3 +140,5 @@ void gdAbout::cb_close()
 {
        do_callback();
 }
+
+}} // giada::v::
\ No newline at end of file
index 6be2063bbafe28b4a274d11fdbfed5f73ff50d28..e796e8cd5b268081586a25560c3d205fb2e4dd00 100644 (file)
@@ -36,8 +36,19 @@ class geBox;
 class geButton;
 
 
+namespace giada {
+namespace v 
+{
 class gdAbout : public gdWindow
 {
+public:
+
+    gdAbout();
+    ~gdAbout();
+
+    static void cb_close(Fl_Widget* w, void* p);
+    inline void cb_close();
+
 private:
 
        geBox*    logo;
@@ -48,14 +59,8 @@ private:
        geBox* vstText;
        geBox* vstLogo;
 #endif
-
-public:
-
-       gdAbout();
-       ~gdAbout();
-
-       static void cb_close(Fl_Widget* w, void* p);
-       inline void cb_close();
 };
+}} // giada::v::
+
 
 #endif
index 84a72637bb2b94bc10ca31ba270b14927ad4ad65..65e5122dac7efabd3c4dd5b5b0e6d319b69df88e 100644 (file)
 #include <string>
 #include <FL/Fl.H>
 #include <FL/fl_draw.H>
-#include "../../../utils/gui.h"
-#include "../../../utils/string.h"
-#include "../../../core/conf.h"
-#include "../../../core/const.h"
-#include "../../../core/clock.h"
-#include "../../../core/channel.h"
-#include "../../elems/actionEditor/gridTool.h"
-#include "../../elems/basics/scroll.h"
-#include "../../elems/basics/choice.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "gui/elems/actionEditor/gridTool.h"
+#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/choice.h"
 #include "baseActionEditor.h"
 
 
 namespace giada {
 namespace v
 {
-gdBaseActionEditor::gdBaseActionEditor(m::Channel* ch)
+gdBaseActionEditor::gdBaseActionEditor(ID channelId)
 :      gdWindow (640, 284),
-       ch       (ch),
+       channelId(channelId),
        ratio    (G_DEFAULT_ZOOM_RATIO)
 {
        using namespace giada::m;
@@ -83,7 +84,7 @@ void gdBaseActionEditor::cb_zoomOut(Fl_Widget* w, void* p) { ((gdBaseActionEdito
 /* -------------------------------------------------------------------------- */
 
 
-const std::vector<const m::Action*>& gdBaseActionEditor::getActions()
+std::vector<m::Action> gdBaseActionEditor::getActions() const
 {
        return m_actions;
 }
@@ -196,9 +197,12 @@ void gdBaseActionEditor::prepareWindow()
 {
        u::gui::setFavicon(this);
 
-       std::string l = "Action Editor";
-       if (ch->name != "") l += " - " + ch->name;
-       copy_label(l.c_str());
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               std::string l = "Action Editor";
+               if (c.name != "") l += " - " + c.name;
+               copy_label(l.c_str());
+       });
 
        set_non_modal();
        size_range(640, 284);
@@ -221,4 +225,4 @@ int gdBaseActionEditor::handle(int e)
                        return Fl_Group::handle(e);
        }
 }
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 0f6dbeaf4d97475ae66c7eb33eb44f64bb1f5d70..5a5e60ca839a089234aaea968544036c790feaff 100644 (file)
@@ -29,8 +29,8 @@
 #define GD_BASE_ACTION_EDITOR_H
 
 
-#include "../../../core/types.h"
-#include "../window.h"
+#include "core/types.h"
+#include "gui/dialogs/window.h"
 
 
 class geChoice;
@@ -51,6 +51,29 @@ class geGridTool;
 
 class gdBaseActionEditor : public gdWindow
 {
+public:
+
+       virtual ~gdBaseActionEditor();
+
+       int handle(int e) override;
+
+       Pixel frameToPixel(Frame f) const;
+       Frame pixelToFrame(Pixel p, bool snap=true) const;
+       int getActionType() const;
+       std::vector<m::Action> getActions() const;
+
+       geChoice*   actionType;
+       geGridTool* gridTool;
+       geButton*   zoomInBtn;
+       geButton*   zoomOutBtn;
+       geScroll*   viewport;       // widget container
+
+       ID channelId;
+
+       float ratio;
+       Pixel fullWidth;     // Full widgets width, i.e. scaled-down full sequencer
+       Pixel loopWidth;         // Loop width, i.e. scaled-down sequencer range
+
 protected:
 
        static constexpr Pixel RESIZER_BAR_H = 20;
@@ -58,9 +81,9 @@ protected:
        static constexpr float MIN_RATIO     = 25.0f;
        static constexpr float MAX_RATIO     = 40000.0f;
 
-       std::vector<const m::Action*> m_actions;
+       std::vector<m::Action> m_actions;
 
-       gdBaseActionEditor(m::Channel* ch);
+       gdBaseActionEditor(ID channelId);
 
        void zoomIn();
        void zoomOut();
@@ -68,7 +91,7 @@ protected:
        static void cb_zoomOut(Fl_Widget* w, void* p);
        
        /* computeWidth
-  Computes total width, in pixel. */
+       Computes total width, in pixel. */
 
        void computeWidth();
 
@@ -76,36 +99,6 @@ protected:
        void centerViewportOut();
 
        void prepareWindow();
-
-public:
-
-       virtual ~gdBaseActionEditor();
-
-       /* rebuild
-       Forces all internal widgets to rebuild themselves. Used when refreshing the
-       whole Action Editor window. */
-       
-       virtual void rebuild() = 0;
-
-       int handle(int e) override;
-
-       Pixel frameToPixel(Frame f) const;
-       Frame pixelToFrame(Pixel p, bool snap=true) const;
-       int getActionType() const;
-
-       const std::vector<const m::Action*>& getActions();
-
-       geChoice*   actionType;
-       geGridTool* gridTool;
-       geButton*   zoomInBtn;
-       geButton*   zoomOutBtn;
-       geScroll*   viewport;       // widget container
-
-       m::Channel* ch;
-
-       float ratio;
-       Pixel fullWidth;     // Full widgets width, i.e. scaled-down full sequencer
-       Pixel loopWidth;         // Loop width, i.e. scaled-down sequencer range
 };
 }} // giada::v::
 
index d1547aa5688ad7c8ab746fa512e8602b4d7d3a08..ae76785b7d0213b2deaf57cf756066eb56beebc5 100644 (file)
 
 
 #include <string>
-#include "../../../core/graphics.h"
-#include "../../../core/midiChannel.h"
-#include "../../../glue/actionEditor.h"
-#include "../../elems/basics/scroll.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/resizerBar.h"
-#include "../../elems/basics/box.h"
-#include "../../elems/actionEditor/noteEditor.h"
-#include "../../elems/actionEditor/velocityEditor.h"
-#include "../../elems/actionEditor/pianoRoll.h"
-#include "../../elems/actionEditor/gridTool.h"
+#include "core/channels/midiChannel.h"
+#include "core/model/model.h"
+#include "core/graphics.h"
+#include "glue/actionEditor.h"
+#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/resizerBar.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/actionEditor/noteEditor.h"
+#include "gui/elems/actionEditor/velocityEditor.h"
+#include "gui/elems/actionEditor/pianoRoll.h"
+#include "gui/elems/actionEditor/gridTool.h"
 #include "midiActionEditor.h"
 
 
-using std::string;
-
-
 namespace giada {
 namespace v
 {
-gdMidiActionEditor::gdMidiActionEditor(m::MidiChannel* ch)
-: gdBaseActionEditor(ch)
+gdMidiActionEditor::gdMidiActionEditor(ID channelId)
+: gdBaseActionEditor(channelId)
 {
        computeWidth();
 
-       Fl_Group* upperArea = new Fl_Group(8, 8, w()-16, 20);
+       Fl_Group* upperArea = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT);
 
        upperArea->begin();
 
-               gridTool = new geGridTool(8, 8);
+               gridTool = new geGridTool(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
 
-               geBox *b1  = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20);    // padding actionType - zoomButtons
-               zoomInBtn  = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
-               zoomOutBtn = new geButton(w()-8-20,   8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+               geBox *b1  = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, G_GUI_UNIT);    // padding actionType - zoomButtons
+               zoomInBtn  = new geButton(w()-8-40-4, G_GUI_OUTER_MARGIN, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+               zoomOutBtn = new geButton(w()-8-20,   G_GUI_OUTER_MARGIN, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
        
        upperArea->end();
        upperArea->resizable(b1);
@@ -69,14 +67,14 @@ gdMidiActionEditor::gdMidiActionEditor(m::MidiChannel* ch)
 
        /* Main viewport: contains all widgets. */
 
-       viewport = new geScroll(8, 36, w()-16, h()-44);
+       viewport = new geScroll(G_GUI_OUTER_MARGIN, upperArea->y()+upperArea->h()+G_GUI_OUTER_MARGIN, w()-16, h()-44);
 
        m_ne  = new geNoteEditor(viewport->x(), viewport->y(), this);
        m_ner = new geResizerBar(m_ne->x(), m_ne->y()+m_ne->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H);
        viewport->add(m_ne);
        viewport->add(m_ner);
        
-       m_ve  = new geVelocityEditor(viewport->x(), m_ne->y()+m_ne->h()+RESIZER_BAR_H, ch);
+       m_ve  = new geVelocityEditor(viewport->x(), m_ne->y()+m_ne->h()+RESIZER_BAR_H);
        m_ver = new geResizerBar(m_ve->x(), m_ve->y()+m_ve->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H);
        viewport->add(m_ve);
        viewport->add(m_ver);
@@ -92,7 +90,8 @@ gdMidiActionEditor::gdMidiActionEditor(m::MidiChannel* ch)
 
 void gdMidiActionEditor::rebuild()
 {
-       m_actions = c::actionEditor::getActions(ch);
+       m_actions = c::actionEditor::getActions(channelId);
+
        computeWidth();
        m_ne->rebuild();
        m_ner->size(m_ne->w(), m_ner->h());
index 2cf5d63eb5414c4ea2001b0c84325229adfee749..1c23b2d7480442546cd35eeb5fb00597e735e7f7 100644 (file)
@@ -41,9 +41,14 @@ namespace v
 class geNoteEditor;
 class geVelocityEditor;
 
-
 class gdMidiActionEditor : public gdBaseActionEditor
 {
+public:
+
+    gdMidiActionEditor(ID channelId);
+
+    void rebuild() override;
+
 private:
 
     geNoteEditor*     m_ne;
@@ -51,12 +56,6 @@ private:
 
        geVelocityEditor* m_ve;
     geResizerBar*     m_ver;
-
-public:
-
-       gdMidiActionEditor(m::MidiChannel* ch);
-
-       void rebuild() override;
 };
 }} // giada::v::
 
index 17715ee4f6e0ee317b814bcf2792515c29b5203f..ba3ef58f76e90cca486b3185aef0c26afb26788e 100644 (file)
 
 
 #include <string>
-#include "../../../core/const.h"
-#include "../../../core/midiEvent.h"
-#include "../../../core/graphics.h"
-#include "../../../core/sampleChannel.h"
-#include "../../../glue/actionEditor.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/sampleActionEditor.h"
-#include "../../elems/actionEditor/envelopeEditor.h"
-#include "../../elems/actionEditor/gridTool.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/midiEvent.h"
+#include "core/graphics.h"
+#include "glue/actionEditor.h"
+#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/resizerBar.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/actionEditor/sampleActionEditor.h"
+#include "gui/elems/actionEditor/envelopeEditor.h"
+#include "gui/elems/actionEditor/gridTool.h"
 #include "sampleActionEditor.h"
 
 
-using std::string;
-
-
 namespace giada {
 namespace v
 {
-gdSampleActionEditor::gdSampleActionEditor(m::SampleChannel* ch)
-: gdBaseActionEditor(ch)
+gdSampleActionEditor::gdSampleActionEditor(ID channelId)
+: gdBaseActionEditor(channelId)
 {
        computeWidth();
 
@@ -61,7 +59,7 @@ gdSampleActionEditor::gdSampleActionEditor(m::SampleChannel* ch)
        upperArea->begin();
 
          actionType = new geChoice(8, 8, 80, 20);
-         gridTool   = new geGridTool(actionType->x()+actionType->w()+4, 8);
+         gridTool   = new geGridTool(actionType->x()+actionType->w()+8, 8);
                actionType->add("Key press");
                actionType->add("Key release");
                actionType->add("Kill chan");
@@ -84,12 +82,12 @@ gdSampleActionEditor::gdSampleActionEditor(m::SampleChannel* ch)
 
        viewport = new geScroll(8, 36, w()-16, h()-44);
 
-       m_ae  = new geSampleActionEditor(viewport->x(), viewport->y(), ch);
+       m_ae  = new geSampleActionEditor(viewport->x(), viewport->y());
        m_aer = new geResizerBar(m_ae->x(), m_ae->y()+m_ae->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H);
        viewport->add(m_ae);
        viewport->add(m_aer);
        
-       m_ee  = new geEnvelopeEditor(viewport->x(), m_ae->y()+m_ae->h()+RESIZER_BAR_H, "volume", ch);
+       m_ee  = new geEnvelopeEditor(viewport->x(), m_ae->y()+m_ae->h()+RESIZER_BAR_H, "volume");
        m_eer = new geResizerBar(m_ee->x(), m_ee->y()+m_ee->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H);
        viewport->add(m_ee);
        viewport->add(m_eer);
@@ -105,8 +103,13 @@ gdSampleActionEditor::gdSampleActionEditor(m::SampleChannel* ch)
 
 bool gdSampleActionEditor::canChangeActionType()
 {
-       m::SampleChannel* sch = static_cast<m::SampleChannel*>(ch); 
-       return sch->mode != ChannelMode::SINGLE_PRESS && !sch->isAnyLoopMode();
+       bool res;
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<const m::SampleChannel&>(c); 
+               res = sc.mode != ChannelMode::SINGLE_PRESS && !sc.isAnyLoopMode();
+       });
+       return res;
 }
 
 
@@ -115,9 +118,11 @@ bool gdSampleActionEditor::canChangeActionType()
 
 void gdSampleActionEditor::rebuild()
 {
-       m_actions = c::actionEditor::getActions(ch);
+       m_actions = c::actionEditor::getActions(channelId);
+
        canChangeActionType() ? actionType->activate() : actionType->deactivate(); 
        computeWidth();
+       
        m_ae->rebuild();
        m_aer->size(m_ae->w(), m_aer->h());
        m_ee->rebuild();        
index 2e170d85a1a65d1949894a3d7692a32bd6075b5a..19fa4d9611bd13f89c02de9da22386a377ee5fa4 100644 (file)
@@ -42,7 +42,13 @@ class geSampleActionEditor;
 class geEnvelopeEditor;
 
 class gdSampleActionEditor : public gdBaseActionEditor
-{
+{  
+public:
+
+    gdSampleActionEditor(ID channelId);
+
+    void rebuild() override;
+
 private:
 
        geSampleActionEditor* m_ae;
@@ -52,12 +58,6 @@ private:
     geResizerBar*         m_eer;
 
        bool canChangeActionType();
-       
-public:
-
-       gdSampleActionEditor(m::SampleChannel* ch);
-
-       void rebuild() override;
 };
 }} // giada::v::
 
index b7857a2bbc48ba83b340625344bd75cd9a5676bc..98a58d387c9d13b43e3b2bf8dbefd62e43770feb 100644 (file)
 
 
 #include <cstring>
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../../core/mixer.h"
-#include "../../core/clock.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../glue/main.h"
-#include "../elems/basics/input.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/check.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "core/mixer.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "glue/main.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/check.h"
 #include "beatsInput.h"
 #include "mainWindow.h"
 
 
-extern gdMainWindow* mainWin;
-
-
-using namespace giada;
+extern giada::v::gdMainWindow* mainWin;
 
 
+namespace giada {
+namespace v 
+{
 gdBeatsInput::gdBeatsInput()
-       : gdWindow(180, 36, "Beats")
+: gdWindow(180, 36, "Beats")
 {
        if (m::conf::beatsX)
                resize(m::conf::beatsX, m::conf::beatsY, w(), h());
@@ -60,11 +60,11 @@ gdBeatsInput::gdBeatsInput()
        end();
 
        beats->maximum_size(2);
-       beats->value(u::string::iToString(m::clock::getBeats()).c_str());
+       beats->value(std::to_string(m::clock::getBeats()).c_str());
        beats->type(FL_INT_INPUT);
        
        bars->maximum_size(2);
-       bars->value(u::string::iToString(m::clock::getBars()).c_str());
+       bars->value(std::to_string(m::clock::getBars()).c_str());
        bars->type(FL_INT_INPUT);
        
        ok->shortcut(FL_Enter);
@@ -102,3 +102,5 @@ void gdBeatsInput::cb_update()
        c::main::setBeats(atoi(beats->value()), atoi(bars->value()));
        do_callback();
 }
+
+}} // giada::v::
\ No newline at end of file
index 5a245ebdb1fa15ec3e83baf59e775c09f15e03f8..ffbc77600d1eea3b1035d4d5dc3456bee4a99bb3 100644 (file)
@@ -37,8 +37,16 @@ class geButton;
 class geCheck;
 
 
+namespace giada {
+namespace v 
+{
 class gdBeatsInput : public gdWindow
 {
+public:
+
+    gdBeatsInput();
+    ~gdBeatsInput();
+
 private:
 
        static void cb_update(Fl_Widget* w, void* p);
@@ -47,12 +55,8 @@ private:
        geInput* beats;
        geInput* bars;
        geButton* ok;
-
-public:
-
-       gdBeatsInput();
-       ~gdBeatsInput();
 };
+}} // giada::v::
 
 
 #endif
index 69e2296a7c71bdb4d08d91f247705771ebcda5f6..4650c8037280aa50e1c380922661bf6223b10d98 100644 (file)
 
 
 #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 "../../utils/string.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/input.h"
+#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 "utils/string.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
 #include "bpmInput.h"
 #include "mainWindow.h"
 
 
-extern gdMainWindow* mainWin;
-
-
-using std::vector;
-using std::string;
-using namespace giada;
+extern giada::v::gdMainWindow* mainWin;
 
 
+namespace giada {
+namespace v 
+{
 gdBpmInput::gdBpmInput(const char* label)
 : gdWindow(144, 36, "Bpm")
 {
@@ -66,7 +64,7 @@ gdBpmInput::gdBpmInput(const char* label)
 
        /* Use the decimal value from the string label. */
 
-       vector<string> tokens = u::string::split(label, ".");
+       std::vector<std::string> tokens = u::string::split(label, ".");
        
        input_b->maximum_size(1);
        input_b->type(FL_INT_INPUT);
@@ -107,3 +105,6 @@ void gdBpmInput::cb_update()
        c::main::setBpm(input_a->value(), input_b->value());
        do_callback();
 }
+
+
+}} // giada::v::
\ No newline at end of file
index 954323d62e28bf0b77e1e987e03d53918f4ebcf5..f941497029e6dbfb98ec9e7d8068bda388c6e01d 100644 (file)
@@ -36,21 +36,26 @@ class geInput;
 class geButton;
 
 
+namespace giada {
+namespace v 
+{
 class gdBpmInput : public gdWindow
 {
-private:
-
-       static void cb_update(Fl_Widget* w, void* p);
-       void cb_update();
-
-       geInput* input_a;
-       geInput* input_b;
-       geButton* ok;
-
 public:
 
        gdBpmInput(const char* label); // pointer to mainWin->timing->bpm->label()
        ~gdBpmInput();
+
+private:
+
+    static void cb_update(Fl_Widget* w, void* p);
+    void cb_update();
+
+    geInput* input_a;
+    geInput* input_b;
+    geButton* ok;
 };
+}} // giada::v::
+
 
 #endif
index d97bd047f4c5d39822883c19029774a5b98d13ef..c2971dc812a5f42651279791aa97469ee17ec637 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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 "core/graphics.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "utils/gui.h"
+#include "utils/fs.h"
+#include "gui/elems/browser.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/progress.h"
+#include "gui/elems/basics/check.h"
 #include "browserBase.h"
 
 
-using std::string;
-using namespace giada;
-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)
+namespace giada {
+namespace v
+{
+gdBrowserBase::gdBrowserBase(const std::string& title, const std::string& path, 
+       std::function<void(void*)> callback, ID channelId)
+: gdWindow   (m::conf::browserX, m::conf::browserY, m::conf::browserW, 
+                 m::conf::browserH, title.c_str()),
+  m_callback (callback),
+  m_channelId(channelId)
 {
        set_non_modal();
 
-       groupTop = new Fl_Group(8, 8, w-16, 40);
+       groupTop = new Fl_Group(8, 8, w()-16, 48);
     hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
-               where = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 20, 20);
-               updir   = new geButton(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
+               where   = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h()+8, 20, 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);
+       hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
 
        where->readonly(true);
        where->cursor_color(G_COLOR_BLACK);
@@ -64,14 +65,14 @@ gdBrowserBase::gdBrowserBase(int x, int y, int w, int h, const string& title,
 
        updir->callback(cb_up, (void*) this);
 
-       browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w-16, h-93);
+       browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w()-16, h()-101);
        browser->loadDir(path);
-       if (path == conf::browserLastPath)
-               browser->preselect(conf::browserPosition, conf::browserLastValue);
+       if (path == m::conf::browserLastPath)
+               browser->preselect(m::conf::browserPosition, m::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");
+       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);
@@ -96,20 +97,20 @@ gdBrowserBase::gdBrowserBase(int x, int y, int w, int h, const string& title,
 
 gdBrowserBase::~gdBrowserBase()
 {
-       conf::browserX = x();
-       conf::browserY = y();
-       conf::browserW = w();
-       conf::browserH = h();
-       conf::browserPosition = browser->position();
-       conf::browserLastPath = browser->getCurrentDir();
-       conf::browserLastValue = browser->value();
+       m::conf::browserX = x();
+       m::conf::browserY = y();
+       m::conf::browserW = w();
+       m::conf::browserH = h();
+       m::conf::browserPosition  = browser->position();
+       m::conf::browserLastPath  = browser->getCurrentDir();
+       m::conf::browserLastValue = browser->value();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdBrowserBase::cb_up   (Fl_Widget* v, void* p) { ((gdBrowserBase*)p)->cb_up(); }
+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(); }
 
@@ -119,7 +120,7 @@ void gdBrowserBase::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBrowserBas
 
 void gdBrowserBase::cb_up()
 {
-       browser->loadDir(gu_getUpDir(browser->getCurrentDir()));
+       browser->loadDir(u::fs::getUpDir(browser->getCurrentDir()));
        where->value(browser->getCurrentDir().c_str());
 }
 
@@ -157,7 +158,7 @@ void gdBrowserBase::setStatusBar(float v)
 
 void gdBrowserBase::showStatusBar()
 {
-  status->show();
+       status->show();
 }
 
 
@@ -166,26 +167,26 @@ void gdBrowserBase::showStatusBar()
 
 void gdBrowserBase::hideStatusBar()
 {
-  status->hide();
+       status->hide();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string gdBrowserBase::getCurrentPath() const 
+std::string gdBrowserBase::getCurrentPath() const 
 { 
        return where->value(); 
 }
 
 
-Channel* gdBrowserBase::getChannel() const 
+ID gdBrowserBase::getChannelId() const 
 { 
-       return channel
+       return m_channelId
 }
 
 
-string gdBrowserBase::getSelectedItem() const 
+std::string gdBrowserBase::getSelectedItem() const 
 {
        return browser->getSelectedItem();
 }
@@ -196,5 +197,7 @@ string gdBrowserBase::getSelectedItem() const
 
 void gdBrowserBase::fireCallback() const
 { 
-       callback((void*) this); 
+       m_callback((void*)this);
 }
+
+}} // giada::v::
index 458706c2c1df58ef64c802c7cfb1e5c89ea58237..cb8f71121944582c559cfc9e2bce3863f9b3f18a 100644 (file)
@@ -29,9 +29,9 @@
 #define GD_BROWSER_BASE_H
 
 
-#include "../window.h"
-#include "../../../core/plugin.h"
-#include "../../../core/channel.h"
+#include<functional>
+#include "gui/dialogs/window.h"
+#include "core/types.h"
 
 
 class Fl_Group;
@@ -42,58 +42,65 @@ class geInput;
 class geProgress;
 
 
+namespace giada {
+namespace m 
+{ 
+class Channel; 
+}
+namespace v
+{
 class gdBrowserBase : public gdWindow
 {
-protected:
-
-       giada::m::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);
-       void cb_up();
-       void cb_close();
-       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. */
+       Returns the full path of the selected file. */
 
        std::string getSelectedItem() const;
 
        std::string getCurrentPath() const;
-       giada::m::Channel* getChannel() const;
+       ID getChannelId() const;
        void fireCallback() const;
        
        /* setStatusBar
-        * Increment status bar for progress tracking. */
+       Increments status bar for progress tracking. */
 
        void setStatusBar(float v);
 
        void showStatusBar();
        void hideStatusBar();
 
+protected:
+
+       gdBrowserBase(const std::string& title, const std::string& path, 
+               std::function<void(void*)> f, ID channelId);
+
+       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);
+       void cb_up();
+       void cb_close();
+       void cb_toggleHiddenFiles();
+
+       /* m_callback
+       Fired when the save/load button is pressed. */
+
+       std::function<void(void*)> m_callback;
+
+       ID m_channelId;
+
+       Fl_Group*   groupTop;
+       geCheck*    hiddenFiles;
+       geBrowser*  browser;
+       geButton*   ok;
+       geButton*   cancel;
+       geInput*    where;
+       geButton*   updir;
+       geProgress* status;
 };
+}} // giada::v::
 
 
 #endif
index ebf8d077db919760c7d8dc00c485eaebf2f7d165..7c977899724e3211b065e078fa6a57017b80d368 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../utils/fs.h"
-#include "../../elems/browser.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/input.h"
+#include "utils/fs.h"
+#include "gui/elems/browser.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
 #include "browserDir.h"
 
 
-using std::string;
-
-
-gdBrowserDir::gdBrowserDir(int x, int y, int w, int h, const string& title,
-               const string& path, void (*callback)(void*))
-       :       gdBrowserBase(x, y, w, h, title, path, callback)
+namespace giada {
+namespace v
+{
+gdBrowserDir::gdBrowserDir(const std::string& title, const std::string& path, 
+       std::function<void(void*)> cb)
+: gdBrowserBase(title, path, cb, 0)
 {
        where->size(groupTop->w()-updir->w()-8, 20);
 
@@ -66,7 +66,7 @@ void gdBrowserDir::cb_down(Fl_Widget* v, void* p) { ((gdBrowserDir*)p)->cb_down(
 
 void gdBrowserDir::cb_load()
 {
-       callback((void*) this);
+       fireCallback();
 }
 
 
@@ -75,11 +75,13 @@ void gdBrowserDir::cb_load()
 
 void gdBrowserDir::cb_down()
 {
-       string path = browser->getSelectedItem();
+       std::string path = browser->getSelectedItem();
 
-       if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
+       if (path.empty() || !u::fs::isDir(path)) // when click on an empty area or not a dir
                return;
 
        browser->loadDir(path);
        where->value(browser->getCurrentDir().c_str());
 }
+
+}} // giada::v::
index c65ff24c6ddcb12c5f714ad9e6d05bda9bd421f0..e6b253d6fcbe8495722ba2126b69aa7936041ed3 100644 (file)
 #include "browserBase.h"
 
 
+namespace giada {
+namespace v
+{
 class gdBrowserDir : public gdBrowserBase
 {
-private:
+public:
 
-       static void cb_load(Fl_Widget* w, void* p);
-       static void cb_down(Fl_Widget* w, void* p);
-       void cb_load();
-       void cb_down();
+       gdBrowserDir(const std::string& title, const std::string& path, 
+        std::function<void(void*)> cb);
 
-public:
+private:
 
-       gdBrowserDir(int x, int y, int w, int h, const std::string& title,
-               const std::string& path, void (*callback)(void*));
+    static void cb_load(Fl_Widget* w, void* p);
+    static void cb_down(Fl_Widget* w, void* p);
+    void cb_load();
+    void cb_down();
 };
+}} // giada::v::
 
 
 #endif
index 2664dbd84b3ed282215c553e9223d722792b633d..c0e7e7f564782c8d2951f397ddc29502c3a32034 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../utils/fs.h"
-#include "../../elems/browser.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/input.h"
+#include "utils/fs.h"
+#include "gui/elems/browser.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
 #include "browserLoad.h"
 
 
-using std::string;
-using namespace giada;
-
-
-gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string& title,
-               const string& path, void (*cb)(void*), m::Channel* ch)
-       :       gdBrowserBase(x, y, w, h, title, path, cb)
+namespace giada {
+namespace v
+{
+gdBrowserLoad::gdBrowserLoad(const std::string& title, const std::string& path, 
+       std::function<void(void*)> cb, ID channelId)
+: gdBrowserBase(title, path, cb, channelId)
 {
-       channel = ch;
-
        where->size(groupTop->w()-updir->w()-8, 20);
 
        browser->callback(cb_down, (void*) this);
@@ -69,7 +66,7 @@ void gdBrowserLoad::cb_down(Fl_Widget* v, void* p) { ((gdBrowserLoad*)p)->cb_dow
 
 void gdBrowserLoad::cb_load()
 {
-       callback((void*) this);
+       fireCallback();
 }
 
 
@@ -78,11 +75,13 @@ void gdBrowserLoad::cb_load()
 
 void gdBrowserLoad::cb_down()
 {
-       string path = browser->getSelectedItem();
+       std::string path = browser->getSelectedItem();
 
-       if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
+       if (path.empty() || !u::fs::isDir(path)) // when click on an empty area or not a dir
                return;
 
        browser->loadDir(path);
        where->value(browser->getCurrentDir().c_str());
 }
+
+}} // giada::v::
index 10f085c9497a1f164384e2ce1a383dc31d020cb0..7212568ed75720db0e3f388ad097f3c4214f64c4 100644 (file)
 #include "browserBase.h"
 
 
+namespace giada {
+namespace m 
+{ 
+class Channel; 
+}
+namespace v
+{
 class gdBrowserLoad : public gdBrowserBase
 {
+public:
+
+    gdBrowserLoad(const std::string& title, const std::string& path, 
+        std::function<void(void*)> cb, ID channelId);
+
 private:
 
        static void cb_load(Fl_Widget* w, void* p);
        static void cb_down(Fl_Widget* v, void* p);
        void cb_load();
        void cb_down();
-
-public:
-
-       gdBrowserLoad(int x, int y, int w, int h, const std::string& title,
-                       const std::string& path, void (*callback)(void*), giada::m::Channel* ch);
 };
-
+}} // giada::v::
 
 #endif
index 1c39c4bf7b9f94b205893dc1bea3b8022280c540..439ba28b19d532c182adad7118808ea80b63018c 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../utils/fs.h"
-#include "../../elems/browser.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/input.h"
+#include "utils/fs.h"
+#include "gui/elems/browser.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
 #include "browserSave.h"
 
 
-using std::string;
-using namespace giada::m;
-
-
-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)
+namespace giada {
+namespace v
+{
+gdBrowserSave::gdBrowserSave(const std::string& title, const std::string& path, 
+       const std::string& name_, std::function<void(void*)> cb, ID channelId)
+: gdBrowserBase(title, path, cb, channelId)
 {
-       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());
+       name->value(name_.c_str());
        groupTop->add(name);
 
        browser->callback(cb_down, (void*) this);
@@ -74,7 +71,7 @@ void gdBrowserSave::cb_down(Fl_Widget* v, void* p) { ((gdBrowserSave*)p)->cb_dow
 
 void gdBrowserSave::cb_down()
 {
-       string path = browser->getSelectedItem();
+       std::string path = browser->getSelectedItem();
 
        if (path.empty())  // when click on an empty area
                return;
@@ -82,7 +79,7 @@ void gdBrowserSave::cb_down()
        /* 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)) {
+       if (u::fs::isDir(path)) {
                browser->loadDir(path);
                where->value(browser->getCurrentDir().c_str());
        }
@@ -94,7 +91,7 @@ void gdBrowserSave::cb_down()
 /* -------------------------------------------------------------------------- */
 
 
-string gdBrowserSave::getName() const
+std::string gdBrowserSave::getName() const
 {
   return name->value();
 }
@@ -105,5 +102,7 @@ string gdBrowserSave::getName() const
 
 void gdBrowserSave::cb_save()
 {
-       callback((void*) this);
+       fireCallback();
 }
+
+}} // giada::v::
index e88f5bf94ac970299d37a859bcf3b9ba76907bbd..4c0b3737711a1f3a5b15d5b8612daf6643c2655b 100644 (file)
 class geInput;
 
 
+namespace giada {
+namespace m 
+{ 
+class Channel; 
+}
+namespace v
+{
 class gdBrowserSave : public gdBrowserBase
 {
-private:
+public:
 
-       geInput* name;
+       gdBrowserSave(const std::string& title, const std::string& path, 
+        const std::string& name, std::function<void(void*)> cb, 
+        ID channelId);
 
-       static void cb_down(Fl_Widget* v, void* p);
-       static void cb_save(Fl_Widget* w, void* p);
-       void cb_down();
-       void cb_save();
+       std::string getName() const;
 
-public:
+private:
 
-       gdBrowserSave(int x, int y, int w, int h, const std::string& title,
-                       const std::string& path, const std::string& name, void (*callback)(void*),
-                       giada::m::Channel* ch);
+    geInput* name;
 
-       std::string getName() const;
+    static void cb_down(Fl_Widget* v, void* p);
+    static void cb_save(Fl_Widget* w, void* p);
+    void cb_down();
+    void cb_save();
 };
+}} // giada::v::
 
 
 #endif
index aef4d8c0184f3bd6e1e32f437bc55598d249e8b2..fd2efd411bcf12212bb0ba254ad97922949bcd72 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../glue/channel.h"
-#include "../../utils/gui.h"
-#include "../../core/const.h"
-#include "../../core/conf.h"
-#include "../../core/channel.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/input.h"
+#include "glue/channel.h"
+#include "utils/gui.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
 #include "channelNameInput.h"
 
 
-using namespace giada;
 
-
-gdChannelNameInput::gdChannelNameInput(m::Channel* ch)
-: gdWindow(400, 64, "New channel name"),
-  m_ch    (ch)
+namespace giada {
+namespace v 
 {
-       using namespace giada::m;
-
-       if (conf::nameX)
-               resize(conf::nameX, conf::nameY, w(), h());
+gdChannelNameInput::gdChannelNameInput(ID channelId)
+: gdWindow   (400, 64, "New channel name"),
+  m_channelId(channelId)
+{
+       if (m::conf::nameX)
+               resize(m::conf::nameX, m::conf::nameY, w(), h());
 
        set_modal();
 
-       m_name = new geInput(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), G_GUI_UNIT);
-       m_ok = new geButton(w() - 70 - G_GUI_OUTER_MARGIN, m_name->y()+m_name->h() + G_GUI_OUTER_MARGIN, 70, G_GUI_UNIT, "Ok");
+       m_name   = new geInput(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), G_GUI_UNIT);
+       m_ok     = new geButton(w() - 70 - G_GUI_OUTER_MARGIN, m_name->y()+m_name->h() + G_GUI_OUTER_MARGIN, 70, G_GUI_UNIT, "Ok");
        m_cancel = new geButton(m_ok->x() - 70 - G_GUI_OUTER_MARGIN, m_ok->y(), 70, G_GUI_UNIT, "Cancel");
        end();
 
-       m_name->value(m_ch->name.c_str());
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               m_name->value(c.name.c_str());
+       });
 
        m_ok->shortcut(FL_Enter);
        m_ok->callback(cb_update, (void*)this);
@@ -98,6 +101,8 @@ void gdChannelNameInput::cb_cancel()
 
 void gdChannelNameInput::cb_update()
 {
-       c::channel::setName(m_ch, m_name->value());
+       c::channel::setName(m_channelId, m_name->value());
        do_callback();
 }
+
+}} // giada::v::
index 00bdf0ebfdd9505319780640f7269604e7e545f3..d2d5fd633e1d48470055fc6a6c5bb6040f142427 100644 (file)
@@ -36,8 +36,16 @@ class geInput;
 class geButton;
 
 
+namespace giada {
+namespace v 
+{
 class gdChannelNameInput : public gdWindow
 {
+public:
+
+       gdChannelNameInput(ID channelId);
+       ~gdChannelNameInput();
+
 private:
 
        static void cb_update(Fl_Widget* w, void* p);
@@ -45,16 +53,13 @@ private:
        void cb_update();
        void cb_cancel();
 
-       giada::m::Channel* m_ch;
+       ID m_channelId;
 
-       geInput* m_name;
+       geInput*  m_name;
        geButton* m_ok;
        geButton* m_cancel;
-
-public:
-
-       gdChannelNameInput(giada::m::Channel* ch);
-       ~gdChannelNameInput();
 };
+}} // giada::v::
+
 
 #endif
index 90afcb24910d24e0a9f768ce029ddf51f74a8df8..0162fd81344b425887b7f73b2fdf56eb6e5bda0a 100644 (file)
 
 
 #include <FL/Fl_Tabs.H>
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../utils/gui.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 "core/conf.h"
+#include "core/const.h"
+#include "utils/gui.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/config/tabMisc.h"
+#include "gui/elems/config/tabMidi.h"
+#include "gui/elems/config/tabAudio.h"
+#include "gui/elems/config/tabBehaviors.h"
+#include "gui/elems/config/tabPlugins.h"
 #include "config.h"
 
 
-using namespace giada;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v 
+{
 gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
 {
-       if (conf::configX)
-               resize(conf::configX, conf::configY, this->w(), this->h());
+       if (m::conf::configX)
+               resize(m::conf::configX, m::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(G_COLOR_LIGHT_2);
-  tabs->begin();
+       tabs->box(G_CUSTOM_BORDER_BOX);
+       tabs->labelcolor(G_COLOR_LIGHT_2);
+       tabs->begin();
 
                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);
@@ -63,7 +62,7 @@ gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
 
        tabs->end();
 
-       save     = new geButton (w-88, h-28, 80, 20, "Save");
+       save   = new geButton (w-88, h-28, 80, 20, "Save");
        cancel = new geButton (w-176, h-28, 80, 20, "Cancel");
 
        end();
@@ -82,8 +81,8 @@ gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
 
 gdConfig::~gdConfig()
 {
-       conf::configX = x();
-       conf::configY = y();
+       m::conf::configX = x();
+       m::conf::configY = y();
 }
 
 
@@ -128,4 +127,6 @@ void gdConfig::refreshVstPath()
        tabPlugins->refreshVstPath();
 }
 
-#endif
\ No newline at end of file
+#endif
+
+}} // giada::v::
\ No newline at end of file
index cae3300c79765adda20fc59d122c670aeea3d913..894b1efe9ba2d68d1b5f81ff6a0bbe6a88f9f0a1 100644 (file)
@@ -47,34 +47,37 @@ class geRadio;
 class geBox;
 
 
+namespace giada {
+namespace v 
+{
 class gdConfig : public gdWindow
 {
-private:
-
-       static void cb_save_config(Fl_Widget* w, void* p);
-       static void cb_cancel(Fl_Widget* w, void* p);
-       void cb_save_config();
-       void cb_cancel();
-
 public:
 
-       geTabAudio* tabAudio;
-       geTabBehaviors* tabBehaviors;
-       geTabMidi* tabMidi;
-       geTabMisc* tabMisc;
-#ifdef WITH_VST
-       geTabPlugins* tabPlugins;
-#endif
-       geButton* save;
-       geButton* cancel;
-
        gdConfig(int w, int h);
        ~gdConfig();
 
 #ifdef WITH_VST
        void refreshVstPath();
 #endif
-};
 
+       geTabAudio*     tabAudio;
+       geTabBehaviors* tabBehaviors;
+       geTabMidi*      tabMidi;
+       geTabMisc*      tabMisc;
+#ifdef WITH_VST
+       geTabPlugins*   tabPlugins;
+#endif
+       geButton*       save;
+       geButton*       cancel;
+
+private:
+
+       static void cb_save_config(Fl_Widget* w, void* p);
+       static void cb_cancel(Fl_Widget* w, void* p);
+       void cb_save_config();
+       void cb_cancel();
+};
+}} // giada::v::
 
 #endif
index 8a34abf2bb4e6480020661f27767b9604c1bfca4..0776f0db46ab356bc92e09decc42af5f8872ccb8 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../core/kernelAudio.h"
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/box.h"
+#include "core/kernelAudio.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/box.h"
 #include "window.h"
 #include "devInfo.h"
 
 
-using std::string;
-using namespace giada;
-
-
+namespace giada {
+namespace v 
+{
 gdDevInfo::gdDevInfo(unsigned dev)
-       : Fl_Window(340, 300, "Device information")
+: gdWindow(340, 300, "Device information")
 {
        set_modal();
 
@@ -48,39 +47,37 @@ gdDevInfo::gdDevInfo(unsigned dev)
        close = new geButton(252, h()-28, 80, 20, "Close");
        end();
 
-       string body  = "";
-       int    lines = 7;
+       std::string body  = "";
+       int         lines = 7;
 
        body  = "Device name: " + m::kernelAudio::getDeviceName(dev) + "\n";
-       body += "Total output(s): " + u::string::iToString(m::kernelAudio::getMaxOutChans(dev)) + "\n";
-       body += "Total intput(s): " + u::string::iToString(m::kernelAudio::getMaxInChans(dev)) + "\n";
-       body += "Duplex channel(s): " + u::string::iToString(m::kernelAudio::getDuplexChans(dev)) + "\n";
-       body += "Default output: " + string(m::kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
-       body += "Default input: " + string(m::kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
+       body += "Total output(s): " + std::to_string(m::kernelAudio::getMaxOutChans(dev)) + "\n";
+       body += "Total intput(s): " + std::to_string(m::kernelAudio::getMaxInChans(dev)) + "\n";
+       body += "Duplex channel(s): " + std::to_string(m::kernelAudio::getDuplexChans(dev)) + "\n";
+       body += "Default output: " + std::string(m::kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
+       body += "Default input: " + std::string(m::kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
 
        int totalFreq = m::kernelAudio::getTotalFreqs(dev);
-       body += "Supported frequencies: " + u::string::iToString(totalFreq);
+       body += "Supported frequencies: " + std::to_string(totalFreq);
 
        for (int i=0; i<totalFreq; i++) {
                if (i % 6 == 0) {
                        body += "\n    ";  // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
                        lines++;
                }
-               body += u::string::iToString(m::kernelAudio::getFreq(dev, i)) + "  ";
+               body += std::to_string(m::kernelAudio::getFreq(dev, i)) + "  ";
        }
 
        text->copy_label(body.c_str());
 
-       /* resize the window to fit the content. fl_height() returns the height
-        * of a line. fl_height() * total lines + margins + button size */
+       /* Resize the window to fit the content. fl_height() returns the height of a 
+       line. fl_height() * total lines + margins + button size */
 
        resize(x(), y(), w(), (lines * fl_height()) + 8 + 8 + 8 + 20);
        close->position(close->x(), (lines * fl_height()) + 8 + 8);
 
-       close->callback(__cb_window_closer, (void*)this);
+       close->callback(cb_window_closer, (void*)this);
        u::gui::setFavicon(this);
        show();
 }
-
-
-gdDevInfo::~gdDevInfo() {}
+}} // giada::v::
\ No newline at end of file
index 3861967feb32b7964388386df890088491a76fa3..1f549f3e5a6bfaf2acafe1dee5eba3cfe90183e1 100644 (file)
 #define GD_DEV_INFO_H
 
 
-#include <FL/Fl_Window.H>
+#include "window.h"
 
 
 class geBox;
 class geButton;
 
 
-class gdDevInfo : public Fl_Window
+namespace giada {
+namespace v 
 {
-private:
+class gdDevInfo : public gdWindow
+{
+public:
 
-       geBox            *text;
-       geButton *close;
+    gdDevInfo(unsigned dev);
 
-public:
+private:
 
-       gdDevInfo(unsigned dev);
-       ~gdDevInfo();
+       geBox*    text;
+       geButton* close;
 };
+}} // giada::v::
 
 #endif
index 2f1acd32874efb4e872844aa20035dc27ef40040..7bd5e18db676cb24a915a86ecf1ff268166e36e9 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../../core/conf.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/midiChannel.h"
-#include "../../utils/log.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 <cassert>
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "glue/io.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "core/conf.h"
+#include "utils/log.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/channelButton.h"
 #include "keyGrabber.h"
 #include "config.h"
 #include "mainWindow.h"
 
 
-extern gdMainWindow *mainWin;
+extern giada::v::gdMainWindow* mainWin;
 
 
-using std::string;
-using namespace giada;
-
-
-gdKeyGrabber::gdKeyGrabber(m::Channel* ch)
-       : gdWindow(300, 126, "Key configuration"), ch(ch)
+namespace giada {
+namespace v 
 {
-       set_modal();
-       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");
+gdKeyGrabber::gdKeyGrabber(ID channelId)
+: gdWindow   (300, 126, "Key configuration"), 
+  m_channelId(channelId)
+{
+       begin();
+       m_text   = new geBox(8, 8, 284, 80, "");
+       m_clear  = new geButton(w()-88, m_text->y()+m_text->h()+8, 80, 20, "Clear");
+       m_cancel = new geButton(m_clear->x()-88, m_clear->y(), 80, 20, "Close");
        end();
 
-       clear->callback(cb_clear, (void*)this);
-       cancel->callback(cb_cancel, (void*)this);
+       m_clear->callback(cb_clear, (void*)this);
+       m_cancel->callback(cb_cancel, (void*)this);
 
-       updateText(ch->key);
+       rebuild();
 
        u::gui::setFavicon(this);
+       set_modal();
        show();
 }
 
@@ -70,6 +74,18 @@ gdKeyGrabber::gdKeyGrabber(m::Channel* ch)
 /* -------------------------------------------------------------------------- */
 
 
+void gdKeyGrabber::rebuild()
+{
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               updateText(c.key);
+       });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void gdKeyGrabber::cb_clear (Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
 void gdKeyGrabber::cb_cancel(Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); }
 
@@ -98,8 +114,7 @@ void gdKeyGrabber::cb_clear()
 
 void gdKeyGrabber::setButtonLabel(int key)
 {
-       ch->guiChannel->mainButton->setKey(key);
-       ch->key = key;
+       c::io::setSampleChannelKey(m_channelId, key);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -107,12 +122,12 @@ void gdKeyGrabber::setButtonLabel(int key)
 
 void gdKeyGrabber::updateText(int key)
 {
-       string tmp = "Press a key.\n\nCurrent binding: ";
+       std::string tmp = "Press a key.\n\nCurrent binding: ";
        if (key != 0)
                tmp += static_cast<char>(key);
        else
                tmp += "[none]";
-       text->copy_label(tmp.c_str());
+       m_text->copy_label(tmp.c_str());
 }
 
 
@@ -133,14 +148,16 @@ int gdKeyGrabber::handle(int e)
                            && x != FL_End
                            && x != ' ')
                        {
-                               gu_log("set key '%c' (%d) for channel %d\n", x, x, ch->index);
+                               u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_channelId);
                                setButtonLabel(x);
                                updateText(x);
                                break;
                        }
                        else
-                               gu_log("invalid key\n");
+                               u::log::print("invalid key\n");
                }
        }
        return(ret);
 }
+
+}} // giada::v::
index 24c27b70d16829400c8651346fa29d6cab234774..ff42a1afa0a6dc8ce6085e4d374bab4c9515af48 100644 (file)
@@ -37,15 +37,19 @@ class geBox;
 class geButton;
 
 
+namespace giada {
+namespace v 
+{
 class gdKeyGrabber : public gdWindow
 {
-private:
+public:
+
+       gdKeyGrabber(ID channelId);
 
-       giada::m::Channel* ch;
+       int handle(int e) override;
+       void rebuild() override;
 
-       geBox*    text;
-       geButton* clear;
-       geButton* cancel;
+private:
 
        static void cb_clear (Fl_Widget* w, void* p);
        static void cb_cancel(Fl_Widget* w, void* p);
@@ -54,11 +58,14 @@ private:
 
        void setButtonLabel(int key);
        void updateText(int key);
+       
+       ID m_channelId;
 
-public:
-
-       gdKeyGrabber(giada::m::Channel* ch);
-       int handle(int e);
+       geBox*    m_text;
+       geButton* m_clear;
+       geButton* m_cancel;
 };
+}} // giada::v::
+
 
 #endif
index 45a2ca1b492721ee3bc741497177fa3b23c6e43c..71cb6b6299646d025ac8a16f675bd043a314299a 100644 (file)
 
 
 #include <FL/Fl.H>
-#include "../../core/const.h"
-#include "../../core/conf.h"
-#include "../../core/init.h"
-#include "../../utils/gui.h"
-#include "../elems/basics/boxtypes.h"
-#include "../elems/mainWindow/mainIO.h"
-#include "../elems/mainWindow/mainMenu.h"
-#include "../elems/mainWindow/mainTimer.h"
-#include "../elems/mainWindow/mainTransport.h"
-#include "../elems/mainWindow/beatMeter.h"
-#include "../elems/mainWindow/keyboard/keyboard.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/init.h"
+#include "utils/gui.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainMenu.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/mainWindow/mainTransport.h"
+#include "gui/elems/mainWindow/beatMeter.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
 #include "warnings.h"
 #include "mainWindow.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using namespace giada;
-
-
+namespace giada {
+namespace v 
+{
 gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** argv)
        : gdWindow(W, H, title)
 {
@@ -64,8 +61,8 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg
 
        size_range(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT);
 
-       mainMenu      = new v::geMainMenu(8, -1);
-       mainIO        = new v::geMainIO(412, 8);
+       mainMenu      = new v::geMainMenu(8, 0);
+       mainIO        = new v::geMainIO(408, 8);
        mainTransport = new v::geMainTransport(8, 39);
        mainTimer     = new v::geMainTimer(598, 44);
        beatMeter     = new v::geBeatMeter(100, 83, 609, 20);
@@ -73,7 +70,7 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg
 
        /* zone 1 - menus, and I/O tools */
 
-       Fl_Group* zone1 = new Fl_Group(8, 8, W-16, 20);
+       Fl_Group* zone1 = new Fl_Group(8, 0, W-16, 28);
        zone1->add(mainMenu);
        zone1->resizable(new Fl_Box(300, 8, 80, 20));
        zone1->add(mainIO);
@@ -117,4 +114,38 @@ gdMainWindow::~gdMainWindow()
        m::conf::mainWindowY = y();
        m::conf::mainWindowW = w();
        m::conf::mainWindowH = h();
-}
\ No newline at end of file
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMainWindow::refresh()
+{
+       mainIO->refresh();
+       mainTimer->refresh();
+       mainTransport->refresh();
+       beatMeter->refresh();
+       keyboard->refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMainWindow::rebuild()
+{
+       keyboard->rebuild();
+       mainIO->rebuild();
+       mainTimer->rebuild();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMainWindow::clearKeyboard()
+{
+       keyboard->init();
+}
+}} // giada::v::
index 30479ff0bf3320ad89d106598f9abd1a3f33eeac..0237bf0971c2fb6d5f066d3a17fe4557e7a4409c 100644 (file)
@@ -44,23 +44,26 @@ class geMainMenu;
 class geBeatMeter;
 class geMainTransport;
 class geMainTimer;
-}}
-
-
 class gdMainWindow : public gdWindow
 {
 public:
 
-       giada::v::geKeyboard* keyboard;
-       giada::v::geBeatMeter* beatMeter;
-       giada::v::geMainMenu* mainMenu;
-       giada::v::geMainIO* mainIO;
-       giada::v::geMainTimer* mainTimer;
-       giada::v::geMainTransport* mainTransport;
-
     gdMainWindow(int w, int h, const char* title, int argc, char** argv);
        ~gdMainWindow();
+
+       void refresh() override;
+    void rebuild() override;
+
+       void clearKeyboard();
+
+       geKeyboard*      keyboard;
+       geBeatMeter*     beatMeter;
+       geMainMenu*      mainMenu;
+       geMainIO*        mainIO;
+       geMainTimer*     mainTimer;
+       geMainTransport* mainTransport;
 };
+}} // giada::v::
 
 
 #endif
index 495d65c1a4b86e583932f3645fee1d1b96002d16..00c5d456e06919254ba50d75abb27b7c1adb7673 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/midiDispatcher.h"
-#include "../../../core/channel.h"
-#include "../../../core/conf.h"
-#include "../../../utils/log.h"
-#include "../../elems/midiLearner.h"
+#include "core/channels/channel.h"
+#include "core/midiDispatcher.h"
+#include "core/conf.h"
+#include "utils/log.h"
+#include "gui/elems/midiLearner.h"
 #include "midiInputBase.h"
 
 
-using std::string;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v 
+{
 gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char* title)
-       : gdWindow(x, y, w, h, title)
+: gdWindow(x, y, w, h, title)
 {
 }
 
@@ -48,60 +47,29 @@ gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char* title)
 
 gdMidiInputBase::~gdMidiInputBase()
 {
-       midiDispatcher::stopMidiLearn();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdMidiInputBase::stopMidiLearn(geMidiLearner* learner)
-{
-       midiDispatcher::stopMidiLearn();
-       learner->updateValue();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdMidiInputBase::cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l)
-{
-       *param = msg;
-       stopMidiLearn(l);
-       gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
+       m::midiDispatcher::stopMidiLearn();
+       
+       m::conf::midiInputX = x();
+       m::conf::midiInputY = y();
+       m::conf::midiInputW = w();
+       m::conf::midiInputH = h();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiInputBase::cb_learn(uint32_t msg, void* d)
+void gdMidiInputBase::refresh()
 {
-
-       geMidiLearner::cbData_t* data = (geMidiLearner::cbData_t*) d;
-       geMidiLearner* learner = data->learner;
-       Channel* channel = data->channel;
-       uint32_t* param = learner->param;
-       int midiChannel = (*param & 0x0F000000) >> 24; // Brutally extract channel
-
-       /* No MIDI learning if we are learning a Channel (channel != nullptr) and 
-       the selected MIDI channel is filtered OR if we are learning a global parameter
-       (channel == nullptr) and the selected MIDI channel is filtered. */
-
-       if ((channel != nullptr && !channel->isMidiInAllowed(midiChannel)) ||
-           (channel == nullptr && !conf::isMidiInAllowed(midiChannel)))
-                       return;
-
-       gdMidiInputBase* window  = static_cast<gdMidiInputBase*>(data->window);
-       window->cb_learn(param, msg, learner);
+       for (geMidiLearner* l : m_learners)
+               l->refresh();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiInputBase::cb_close(Fl_Widget* w, void* p)  { ((gdMidiInputBase*)p)->cb_close(); }
+void gdMidiInputBase::cb_close(Fl_Widget* w, void* p) { ((gdMidiInputBase*)p)->cb_close(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -111,3 +79,5 @@ void gdMidiInputBase::cb_close()
 {
        do_callback();
 }
+
+}} // giada::v::
index f750c8d707907316d3fad1494fa98f3f41dbe0d7..6139db5e2cee3b9f26189c52b1fe004000e048ee 100644 (file)
 #define GD_MIDI_INPUT_BASE_H
 
 
-#include "../window.h"
+#include "gui/dialogs/window.h"
+#include "gui/elems/midiLearner.h"
 
 
 class geButton;
-class geMidiLearner;
+class geCheck;
+class geChoice;
 
 
+namespace giada {
+namespace v 
+{
 class gdMidiInputBase : public gdWindow
 {
-protected:
+public:
 
-       static const int LEARNER_WIDTH = 284;
+       gdMidiInputBase(int x, int y, int w, int h, const char* title);
+       ~gdMidiInputBase();
 
-       geButton* ok;
+       void refresh() override;
 
-       void stopMidiLearn(geMidiLearner* l);
+protected:
 
-       /* cb_learn
-        * callback attached to kernelMidi to learn various actions. */
+       static const int LEARNER_WIDTH = 284;
 
-       static void cb_learn(uint32_t msg, void* data);
        static void cb_close(Fl_Widget* w, void* p);
-       void cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l);
        void cb_close();
 
-public:
+       std::vector<geMidiLearner*> m_learners;
 
-       gdMidiInputBase(int x, int y, int w, int h, const char* title);
-       ~gdMidiInputBase();
+       geButton* m_ok;
+       geCheck*  m_enable;
+       geChoice* m_channel;
 };
+}} // giada::v::
 
 
 #endif
index 2d82e1897ea3c2bb6bd264424e39e8d6b57b1bc8..7366f5d16dff731548ff62824711d7914d566843 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl_Pack.H>
-#include "../../../utils/gui.h"
-#include "../../../utils/log.h"
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/sampleChannel.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/conf.h"
 #ifdef WITH_VST
-       #include "../../../core/pluginHost.h"
-       #include "../../../core/plugin.h"
+#include "core/pluginHost.h"
+#include "core/plugin.h"
 #endif
-#include "../../../utils/string.h"
-#include "../../elems/midiLearner.h"
-#include "../../elems/basics/scroll.h"
-#include "../../elems/basics/box.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/choice.h"
-#include "../../elems/basics/check.h"
+#include "utils/string.h"
+#include "gui/elems/midiLearner.h"
+#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/check.h"
 #include "midiInputChannel.h"
 
 
-using std::string;
-using std::vector;
-using namespace giada;
-using namespace giada::m;
-
-
-gdMidiInputChannel::gdMidiInputChannel(Channel* ch)
-       :       gdMidiInputBase(conf::midiInputX, conf::midiInputY, conf::midiInputW,
-                       conf::midiInputH, "MIDI Input Setup"),
-               ch(ch)
+namespace giada {
+namespace v 
 {
-       string title = "MIDI Input Setup (channel " + u::string::iToString(ch->index+1) + ")";
-       label(title.c_str());
-       size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
+gdMidiInputChannel::gdMidiInputChannel(ID channelId)
+: gdMidiInputBase(m::conf::midiInputX, m::conf::midiInputY, m::conf::midiInputW, 
+       m::conf::midiInputH, "MIDI Input Setup"),
+  m_channelId    (channelId)
+{
+       m::model::ChannelsLock l(m::model::channels);
+       const m::Channel& c = m::model::get(m::model::channels, m_channelId);
 
-       int extra = ch->type == ChannelType::SAMPLE ? 28 : 0;
+       copy_label(std::string("MIDI Input Setup (channel " + std::to_string(c.id) + ")").c_str());
+       
+       int extra = c.type == ChannelType::SAMPLE ? 28 : 0;
 
        Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20 + extra);
        groupHeader->begin();
 
-               enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT, 
+               m_enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT, 
                        "Enable MIDI input");
-               channel = new geChoice(enable->x()+enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
-               veloAsVol = new geCheck(G_GUI_OUTER_MARGIN, enable->y()+enable->h()+G_GUI_OUTER_MARGIN  , 120, G_GUI_UNIT, 
+               m_channel = new geChoice(m_enable->x()+m_enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
+               m_veloAsVol = new geCheck(G_GUI_OUTER_MARGIN, m_enable->y()+m_enable->h()+G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT, 
                        "Velocity drives volume (one-shot only)");
 
        groupHeader->resizable(nullptr);
        groupHeader->end();
 
-       container = new geScroll(G_GUI_OUTER_MARGIN, groupHeader->y()+groupHeader->h()+G_GUI_OUTER_MARGIN, 
+       m_container = new geScroll(G_GUI_OUTER_MARGIN, groupHeader->y()+groupHeader->h()+G_GUI_OUTER_MARGIN, 
                w()-16, h()-72-extra);
-       container->begin();
+       m_container->begin();
 
                addChannelLearners();
 #ifdef WITH_VST
                addPluginLearners();
 #endif
 
-       container->end();
+       m_container->end();
 
-       Fl_Group* groupButtons = new Fl_Group(8, container->y()+container->h()+8, container->w(), 20);
+       Fl_Group* groupButtons = new Fl_Group(8, m_container->y()+m_container->h()+8, m_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");
+               m_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->callback(cb_enable, (void*)this);
+       m_ok->callback(cb_close, (void*)this);
+
+       m_enable->value(c.midiIn);
+       m_enable->callback(cb_enable, (void*)this);
 
-       if (ch->type == ChannelType::SAMPLE) {
-               veloAsVol->value(static_cast<SampleChannel*>(ch)->midiInVeloAsVol);
-               veloAsVol->callback(cb_veloAsVol, (void*)this); 
+       if (c.type == ChannelType::SAMPLE) {
+               m_veloAsVol->value(static_cast<const m::SampleChannel&>(c).midiInVeloAsVol);
+               m_veloAsVol->callback(cb_veloAsVol, (void*)this);       
        }
        else
-               veloAsVol->hide();
-
-       channel->add("Channel (any)");
-       channel->add("Channel 1");
-       channel->add("Channel 2");
-       channel->add("Channel 3");
-       channel->add("Channel 4");
-       channel->add("Channel 5");
-       channel->add("Channel 6");
-       channel->add("Channel 7");
-       channel->add("Channel 8");
-       channel->add("Channel 9");
-       channel->add("Channel 10");
-       channel->add("Channel 11");
-       channel->add("Channel 12");
-       channel->add("Channel 13");
-       channel->add("Channel 14");
-       channel->add("Channel 15");
-       channel->add("Channel 16");
-       channel->value(ch->midiInFilter == -1 ? 0 : ch->midiInFilter + 1);
-       channel->callback(cb_setChannel, (void*)this);
-
-       resizable(container);
+               m_veloAsVol->hide();
+
+       m_channel->add("Channel (any)");
+       m_channel->add("Channel 1");
+       m_channel->add("Channel 2");
+       m_channel->add("Channel 3");
+       m_channel->add("Channel 4");
+       m_channel->add("Channel 5");
+       m_channel->add("Channel 6");
+       m_channel->add("Channel 7");
+       m_channel->add("Channel 8");
+       m_channel->add("Channel 9");
+       m_channel->add("Channel 10");
+       m_channel->add("Channel 11");
+       m_channel->add("Channel 12");
+       m_channel->add("Channel 13");
+       m_channel->add("Channel 14");
+       m_channel->add("Channel 15");
+       m_channel->add("Channel 16");
+       m_channel->value(c.midiInFilter == -1 ? 0 : c.midiInFilter + 1);
+       m_channel->callback(cb_setChannel, (void*)this);
+
+       resizable(m_container);
 
        end();
 
+       size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
+
        u::gui::setFavicon(this);
        set_modal();
        show();
@@ -139,40 +143,28 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch)
 /* -------------------------------------------------------------------------- */
 
 
-gdMidiInputChannel::~gdMidiInputChannel()
-{
-       conf::midiInputX = x();
-       conf::midiInputY = y();
-       conf::midiInputW = w();
-       conf::midiInputH = h();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void gdMidiInputChannel::addChannelLearners()
 {
-       Fl_Pack* pack = new Fl_Pack(container->x(), container->y(), LEARNER_WIDTH, 200);
-       pack->spacing(4);
-       pack->begin();
+       m::model::ChannelsLock l(m::model::channels);
+       m::Channel& c = m::model::get(m::model::channels, m_channelId);
 
-               geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, "channel");
+       Fl_Pack* pack = new Fl_Pack(m_container->x(), m_container->y(), LEARNER_WIDTH, 200);
+       pack->spacing(G_GUI_INNER_MARGIN);
+       pack->begin();
+               geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, "Channel");
                header->box(FL_BORDER_BOX);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "key press",   cb_learn, &ch->midiInKeyPress, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", cb_learn, &ch->midiInKeyRel, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "key kill",    cb_learn, &ch->midiInKill, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "arm",         cb_learn, &ch->midiInArm, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "mute",        cb_learn, &ch->midiInMute, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "solo",        cb_learn, &ch->midiInSolo, ch);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "volume",      cb_learn, &ch->midiInVolume, ch);
-               if (ch->type == ChannelType::SAMPLE) {
-                       new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch", cb_learn, 
-                               &(static_cast<SampleChannel*>(ch))->midiInPitch, ch);
-                       new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn, 
-                               &(static_cast<SampleChannel*>(ch))->midiInReadActions, ch);
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "key press",   c.midiInKeyPress, m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", c.midiInKeyRel,   m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "key kill",    c.midiInKill,     m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "arm",         c.midiInArm,      m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "mute",        c.midiInMute,     m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "solo",        c.midiInSolo,     m_channelId));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "volume",      c.midiInVolume,   m_channelId));
+               if (c.type == ChannelType::SAMPLE) {
+                       m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+                       m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch",        sc.midiInPitch,       m_channelId));
+                       m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", sc.midiInReadActions, m_channelId));
                }
-
        pack->end();
 }
 
@@ -184,26 +176,29 @@ void gdMidiInputChannel::addChannelLearners()
 
 void gdMidiInputChannel::addPluginLearners()
 {
-       vector<Plugin*> plugins = pluginHost::getStack(pluginHost::StackType::CHANNEL, ch);
+       m::model::ChannelsLock cl(m::model::channels);
+       m::model::PluginsLock  ml(m::model::plugins);
+
+       m::Channel& c = m::model::get(m::model::channels, m_channelId);
+       
+       int i = 1;
+       for (ID id : c.pluginIds) {
 
-       int i = 0;
-       for (Plugin* plugin : plugins) {
+               m::Plugin& p = m::model::get(m::model::plugins, id);
 
-               Fl_Pack* pack = new Fl_Pack(container->x() + ((i + 1) * (LEARNER_WIDTH + 8)),
-                       container->y(), LEARNER_WIDTH, 200);
-               pack->spacing(4);
+               Fl_Pack* pack = new Fl_Pack(m_container->x() + (i++ * (LEARNER_WIDTH + G_GUI_OUTER_MARGIN)),
+                       m_container->y(), LEARNER_WIDTH, 200);
+               pack->spacing(G_GUI_INNER_MARGIN);
                pack->begin();
 
-                       geBox* header = new geBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
+                       geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, p.getName().c_str());
                        header->box(FL_BORDER_BOX);
 
-                       for (int k=0; k<plugin->getNumParameters(); k++)
-                               new geMidiLearner(0, 0, LEARNER_WIDTH, plugin->getParameterName(k).c_str(),
-                                       cb_learn, &plugin->midiInParams.at(k), ch);
+                       for (int k=0; k<p.getNumParameters(); k++)
+                               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, 
+                                       p.getParameterName(k).c_str(), p.midiInParams.at(k), m_channelId));
 
                pack->end();
-
-               i++;
        }
 }
 
@@ -223,8 +218,11 @@ void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* w, void* p) { ((gdMidiInputChan
 
 void gdMidiInputChannel::cb_enable()
 {
-       ch->midiIn = enable->value();
-       enable->value() ? channel->activate() : channel->deactivate();
+       m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               c.midiIn = m_enable->value();
+       });
+       m_enable->value() ? m_channel->activate() : m_channel->deactivate();
 }
 
 
@@ -233,7 +231,10 @@ void gdMidiInputChannel::cb_enable()
 
 void gdMidiInputChannel::cb_veloAsVol()
 {
-       static_cast<SampleChannel*>(ch)->midiInVeloAsVol = veloAsVol->value();
+       m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               static_cast<m::SampleChannel&>(c).midiInVeloAsVol = m_veloAsVol->value();
+       });
 }
 
 
@@ -242,7 +243,10 @@ void gdMidiInputChannel::cb_veloAsVol()
 
 void gdMidiInputChannel::cb_setChannel()
 {
-       ch->midiInFilter = channel->value() == 0 ? -1 : channel->value() - 1;
-       gu_log("[gdMidiInputChannel] Set MIDI channel to %d\n", ch->midiInFilter);
+       m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               c.midiInFilter = m_channel->value() == 0 ? -1 : m_channel->value() - 1;
+               u::log::print("[gdMidiInputChannel] Set MIDI channel to %d\n", c.midiInFilter.load());
+       });
 }
-
+}} // giada::v::
index b9e95ea8c9d2db78b275e327f09cf793a0869088..add0d322af9e83b638ac298c9c9975d16047dce3 100644 (file)
@@ -39,16 +39,17 @@ class geCheck;
 class geChoice;
 
 
+namespace giada {
+namespace v 
+{
+class geMidiLearner;
 class gdMidiInputChannel : public gdMidiInputBase
 {
-private:
+public:
 
-       giada::m::Channel* ch;
+       gdMidiInputChannel(ID channelId);
 
-       geScroll* container;
-       geCheck*  enable;
-       geCheck*  veloAsVol;
-       geChoice* channel;
+private:
 
        static void cb_enable(Fl_Widget* w, void* p);
        static void cb_setChannel(Fl_Widget* w, void* p);
@@ -65,11 +66,12 @@ private:
 
 #endif
 
-public:
+       ID m_channelId;
 
-       gdMidiInputChannel(giada::m::Channel* ch);
-       ~gdMidiInputChannel();
+       geScroll* m_container;
+       geCheck*  m_veloAsVol;
 };
+}} // giada::v::
 
 
 #endif
index 089d9665dd7965262acf78aa287f6e479a960cfe..5b846dcbd1323288f3ba523b4ea8b7f8003123ea 100644 (file)
 
 
 #include <FL/Fl_Pack.H>
-#include "../../../utils/gui.h"
-#include "../../../core/conf.h"
-#include "../../../core/const.h"
-#include "../../elems/midiLearner.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/check.h"
-#include "../../elems/basics/choice.h"
+#include "utils/gui.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "gui/elems/midiLearner.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/choice.h"
 #include "midiInputMaster.h"
 
 
-using namespace giada;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v 
+{
 gdMidiInputMaster::gdMidiInputMaster()
-       : gdMidiInputBase(0, 0, 300, 284, "MIDI Input Setup (global)")
+: gdMidiInputBase(0, 0, 300, 284, "MIDI Input Setup (global)")
 {
        set_modal();
 
        Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20);
        groupHeader->begin();
 
-               enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT, 
-                       "enable MIDI input");
-               channel = new geChoice(enable->x()+enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
+               m_enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT, 
+                       "m_enable MIDI input");
+               m_channel = new geChoice(m_enable->x()+m_enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
 
        groupHeader->resizable(nullptr);
        groupHeader->end();
@@ -59,47 +58,45 @@ gdMidiInputMaster::gdMidiInputMaster()
                LEARNER_WIDTH, 212);
        pack->spacing(G_GUI_INNER_MARGIN);
        pack->begin();
-
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "rewind",           &cb_learn, &conf::midiInRewind, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "play/stop",        &cb_learn, &conf::midiInStartStop, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "action recording", &cb_learn, &conf::midiInActionRec, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "input recording",  &cb_learn, &conf::midiInInputRec, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "metronome",        &cb_learn, &conf::midiInMetronome, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "input volume",     &cb_learn, &conf::midiInVolumeIn, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "output volume",    &cb_learn, &conf::midiInVolumeOut, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ×2",     &cb_learn, &conf::midiInBeatDouble, nullptr);
-               new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ÷2",     &cb_learn, &conf::midiInBeatHalf, nullptr);
-
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "rewind",           m::conf::midiInRewind,     0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "play/stop",        m::conf::midiInStartStop,  0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "action recording", m::conf::midiInActionRec,  0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "input recording",  m::conf::midiInInputRec,   0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "metronome",        m::conf::midiInMetronome,  0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "input volume",     m::conf::midiInVolumeIn,   0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "output volume",    m::conf::midiInVolumeOut,  0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ×2",     m::conf::midiInBeatDouble, 0));
+               m_learners.push_back(new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ÷2",     m::conf::midiInBeatHalf,   0));
        pack->end();
 
-       ok = new geButton(w()-88, pack->y()+pack->h()+G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
+       m_ok = new geButton(w()-88, pack->y()+pack->h()+G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
 
        end();
 
-       ok->callback(cb_close, (void*)this);
-
-       enable->value(conf::midiIn);
-       enable->callback(cb_enable, (void*)this);
-
-       channel->add("Channel (any)");
-       channel->add("Channel 1");
-       channel->add("Channel 2");
-       channel->add("Channel 3");
-       channel->add("Channel 4");
-       channel->add("Channel 5");
-       channel->add("Channel 6");
-       channel->add("Channel 7");
-       channel->add("Channel 8");
-       channel->add("Channel 9");
-       channel->add("Channel 10");
-       channel->add("Channel 11");
-       channel->add("Channel 12");
-       channel->add("Channel 13");
-       channel->add("Channel 14");
-       channel->add("Channel 15");
-       channel->add("Channel 16");
-       channel->value(conf::midiInFilter -1 ? 0 : conf::midiInFilter + 1);
-       channel->callback(cb_setChannel, (void*)this);
+       m_ok->callback(cb_close, (void*)this);
+
+       m_enable->value(m::conf::midiIn);
+       m_enable->callback(cb_enable, (void*)this);
+
+       m_channel->add("Channel (any)");
+       m_channel->add("Channel 1");
+       m_channel->add("Channel 2");
+       m_channel->add("Channel 3");
+       m_channel->add("Channel 4");
+       m_channel->add("Channel 5");
+       m_channel->add("Channel 6");
+       m_channel->add("Channel 7");
+       m_channel->add("Channel 8");
+       m_channel->add("Channel 9");
+       m_channel->add("Channel 10");
+       m_channel->add("Channel 11");
+       m_channel->add("Channel 12");
+       m_channel->add("Channel 13");
+       m_channel->add("Channel 14");
+       m_channel->add("Channel 15");
+       m_channel->add("Channel 16");
+       m_channel->value(m::conf::midiInFilter -1 ? 0 : m::conf::midiInFilter + 1);
+       m_channel->callback(cb_setChannel, (void*)this);
 
        u::gui::setFavicon(this);
        show();
@@ -118,8 +115,8 @@ void gdMidiInputMaster::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputMast
 
 void gdMidiInputMaster::cb_enable()
 {
-       conf::midiIn = enable->value();
-       enable->value() ? channel->activate() : channel->deactivate();
+       m::conf::midiIn = m_enable->value();
+       m_enable->value() ? m_channel->activate() : m_channel->deactivate();
 }
 
 
@@ -128,6 +125,7 @@ void gdMidiInputMaster::cb_enable()
 
 void gdMidiInputMaster::cb_setChannel()
 {
-       conf::midiInFilter = channel->value() == 0 ? -1 : channel->value() - 1;
+       m::conf::midiInFilter = m_channel->value() == 0 ? -1 : m_channel->value() - 1;
 }
 
+}} // giada::v::
index 002925c65d867d1ae5cabce1ce49fa3d15016a03..5078bdc4425f2c5c80349db6682eb50fdb689b77 100644 (file)
@@ -2,8 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * midiInputMaster
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
@@ -38,22 +36,23 @@ class geCheck;
 class geChoice;
 
 
+namespace giada {
+namespace v 
+{
 class gdMidiInputMaster : public gdMidiInputBase
 {
-private:
+public:
 
-       geCheck*  enable;
-       geChoice* channel;
+    gdMidiInputMaster();
+
+private:
 
        static void cb_enable(Fl_Widget* w, void* p);
        static void cb_setChannel(Fl_Widget* w, void* p);
        void cb_enable();
        void cb_setChannel();
-
-public:
-
-       gdMidiInputMaster();
 };
+}} // giada::v::
 
 
 #endif
index 9ec2fcd99e8e388e96aeb261c02b82ec10ba1498..fc07b0202fa12160c4817eeb2503d305925c8a92 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../utils/log.h"
-#include "../../../utils/string.h"
-#include "../../elems/midiLearner.h"
+#include "core/midiDispatcher.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include "gui/elems/midiLearner.h"
 #include "midiOutputBase.h"
 
 
-using std::string;
-using namespace giada;
-
-
+namespace giada {
+namespace v 
+{
 gdMidiOutputBase::gdMidiOutputBase(int w, int h)
        : gdWindow(w, h, "Midi Output Setup")
 {
@@ -44,34 +44,19 @@ gdMidiOutputBase::gdMidiOutputBase(int w, int h)
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiOutputBase::stopMidiLearn(geMidiLearner *learner)
+gdMidiOutputBase::~gdMidiOutputBase()
 {
        m::midiDispatcher::stopMidiLearn();
-       learner->updateValue();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiOutputBase::cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l)
+void gdMidiOutputBase::refresh()
 {
-       *param = msg;
-       stopMidiLearn(l);
-       gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdMidiOutputBase::cb_learn(uint32_t msg, void *d)
-{
-       geMidiLearner::cbData_t* data   = (geMidiLearner::cbData_t*) d;
-       gdMidiOutputBase*        window = (gdMidiOutputBase*) data->window;
-       geMidiLearner* learner = data->learner;
-       uint32_t*      param   = learner->param;
-       window->cb_learn(param, msg, learner);
+       for (geMidiLearner* l : m_learners)
+               l->refresh();   
 }
 
 
@@ -110,6 +95,8 @@ void gdMidiOutputBase::cb_enableLightning() {}
 
 void gdMidiOutputBase::setTitle(int chanNum)
 {
-       string tmp = "MIDI Output Setup (channel " + u::string::iToString(chanNum) + ")"; 
+       std::string tmp = "MIDI Output Setup (channel " + std::to_string(chanNum) + ")"; 
        copy_label(tmp.c_str());
 }
+
+}} // giada::v::
index abdd89d856ffb1e3d248eb346fa8498c55f28da6..292767e0566dc59939d92af10f62d64799ac1498 100644 (file)
 
 
 #include <FL/Fl.H>
-#include "../window.h"
+#include "gui/dialogs/window.h"
+#include "gui/elems/midiLearner.h"
 
 
 class geButton;
 class geCheck;
-class geMidiLearner;
 
 
 /* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI
@@ -48,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 */
 
+namespace giada {
+namespace v 
+{
+class geMidiLearner;
 class gdMidiOutputBase : public gdWindow
 {
-protected:
-
-       geButton* close;
-       geCheck*  enableLightning;
+public:
 
-       void stopMidiLearn(geMidiLearner* l);
+       gdMidiOutputBase(int w, int h);
+       ~gdMidiOutputBase();
 
-       /* cb_learn
-        * callback attached to kernelMidi to learn various actions. */
+       void refresh() override;
 
-       static void cb_learn(uint32_t msg, void* data);
-       void cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l);
+protected:
 
        /* cb_close
        close current window. */
@@ -72,7 +72,7 @@ protected:
        /* cb_enableLightning
        enable MIDI lightning output. */
 
-       static void cb_enableLightning  (Fl_Widget* w, void* p);
+       static void cb_enableLightning(Fl_Widget* w, void* p);
        void cb_enableLightning();
 
        /* setTitle
@@ -80,10 +80,12 @@ protected:
 
        void setTitle(int chanNum);
 
-public:
-
-       gdMidiOutputBase(int w, int h);
+       std::vector<geMidiLearner*> m_learners;
+       
+       geButton* m_close;
+       geCheck*  m_enableLightning;
 };
+}} // giada::v::
 
 
 #endif
index c4b0753bbd121eedd38cf2525e77049fb8890480..806e1be107176e24b539df37d05244767e028455 100644 (file)
@@ -1,7 +1,6 @@
-
 /* -----------------------------------------------------------------------------
  *
-, ch * Giada - Your Hardcore Loopmachine
+ * Giada - Your Hardcore Loopmachine
  *
  * -----------------------------------------------------------------------------
  *
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/midiChannel.h"
-#include "../../../utils/gui.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 "core/channels/midiChannel.h"
+#include "core/model/model.h"
+#include "utils/gui.h"
+#include "gui/elems/midiLearner.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
 #include "midiOutputMidiCh.h"
 
 
-using namespace giada;
-
-
-gdMidiOutputMidiCh::gdMidiOutputMidiCh(m::MidiChannel* ch)
-       : gdMidiOutputBase(300, 168), ch(ch)
+namespace giada {
+namespace v 
 {
-       setTitle(ch->index+1);
+gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
+: gdMidiOutputBase(300, 168), 
+  m_channelId     (channelId)
+{
+       m::model::ChannelsLock l(m::model::channels);
+       m::MidiChannel& c = static_cast<m::MidiChannel&>(m::model::get(m::model::channels, m_channelId));
+       
+       setTitle(m_channelId + 1);
        begin();
 
-       enableOut   = new geCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
-       chanListOut = new geChoice(w()-108, y()+8, 100, 20);
+       m_enableOut   = new geCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
+       m_chanListOut = new geChoice(w()-108, y()+8, 100, 20);
 
-       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, ch);
-       new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",    
-               cb_learn, &ch->midiOutLmute, ch);
-       new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",    
-               cb_learn, &ch->midiOutLsolo, ch);
+       m_enableLightning = new geCheck(x()+8, m_chanListOut->y()+m_chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
+       m_learners.push_back(new geMidiLearner(x()+8, m_enableLightning->y()+m_enableLightning->h()+8,  
+               w()-16, "playing", c.midiOutLplaying, m_channelId));
+       m_learners.push_back(new geMidiLearner(x()+8, m_enableLightning->y()+m_enableLightning->h()+32, 
+               w()-16, "mute", c.midiOutLmute, m_channelId));
+       m_learners.push_back(new geMidiLearner(x()+8, m_enableLightning->y()+m_enableLightning->h()+56, 
+               w()-16, "solo", c.midiOutLsolo, m_channelId));
 
-       close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
+       m_close = new geButton(w()-88, m_enableLightning->y()+m_enableLightning->h()+84, 80, 20, "Close");
 
        end();
 
-       chanListOut->add("Channel 1");
-       chanListOut->add("Channel 2");
-       chanListOut->add("Channel 3");
-       chanListOut->add("Channel 4");
-       chanListOut->add("Channel 5");
-       chanListOut->add("Channel 6");
-       chanListOut->add("Channel 7");
-       chanListOut->add("Channel 8");
-       chanListOut->add("Channel 9");
-       chanListOut->add("Channel 10");
-       chanListOut->add("Channel 11");
-       chanListOut->add("Channel 12");
-       chanListOut->add("Channel 13");
-       chanListOut->add("Channel 14");
-       chanListOut->add("Channel 15");
-       chanListOut->add("Channel 16");
-       chanListOut->value(0);
-
-       if (ch->midiOut)
-               enableOut->value(1);
+       m_chanListOut->add("Channel 1");
+       m_chanListOut->add("Channel 2");
+       m_chanListOut->add("Channel 3");
+       m_chanListOut->add("Channel 4");
+       m_chanListOut->add("Channel 5");
+       m_chanListOut->add("Channel 6");
+       m_chanListOut->add("Channel 7");
+       m_chanListOut->add("Channel 8");
+       m_chanListOut->add("Channel 9");
+       m_chanListOut->add("Channel 10");
+       m_chanListOut->add("Channel 11");
+       m_chanListOut->add("Channel 12");
+       m_chanListOut->add("Channel 13");
+       m_chanListOut->add("Channel 14");
+       m_chanListOut->add("Channel 15");
+       m_chanListOut->add("Channel 16");
+       m_chanListOut->value(0);
+
+       if (c.midiOut)
+               m_enableOut->value(1);
        else
-               chanListOut->deactivate();
+               m_chanListOut->deactivate();
 
-       if (ch->midiOutL)
-               enableLightning->value(1);
+       if (c.midiOutL)
+               m_enableLightning->value(1);
 
-       chanListOut->value(ch->midiOutChan);
+       m_chanListOut->value(c.midiOutChan);
 
-       enableOut->callback(cb_enableChanList, (void*)this);
-       close->callback(cb_close, (void*)this);
+       m_enableOut->callback(cb_enableChanList, (void*)this);
+       m_close->callback(cb_close, (void*)this);
 
        set_modal();
        u::gui::setFavicon(this);
@@ -100,27 +104,32 @@ gdMidiOutputMidiCh::gdMidiOutputMidiCh(m::MidiChannel* ch)
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiOutputMidiCh::cb_close         (Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_close(); }
-void gdMidiOutputMidiCh::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_enableChanList(); }
+void gdMidiOutputMidiCh::cb_close         (Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->cb_close(); }
+void gdMidiOutputMidiCh::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->cb_enableChanList(); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiOutputMidiCh::__cb_enableChanList()
+void gdMidiOutputMidiCh::cb_enableChanList()
 {
-       enableOut->value() ? chanListOut->activate() : chanListOut->deactivate();
+       m_enableOut->value() ? m_chanListOut->activate() : m_chanListOut->deactivate();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdMidiOutputMidiCh::__cb_close()
+void gdMidiOutputMidiCh::cb_close()
 {
-       ch->midiOut     = enableOut->value();
-       ch->midiOutChan = chanListOut->value();
-       ch->midiOutL    = enableLightning->value();
-       ch->guiChannel->update();
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               m::MidiChannel& mc = static_cast<m::MidiChannel&>(c);
+               mc.midiOut     = m_enableOut->value();
+               mc.midiOutChan = m_chanListOut->value();
+               mc.midiOutL    = m_enableLightning->value();
+       });
        do_callback();
 }
+
+}} // giada::v::
index a5bfd5a0c621b002eed1e0440a5e903e98cb7d46..9686a50893e1324a9c8ab40533df8eb293bd1fcc 100644 (file)
 #include "midiOutputBase.h"
 
 
+namespace giada {
+namespace v 
+{
 class gdMidiOutputMidiCh : public gdMidiOutputBase
 {
-private:
+public:
 
-       static void cb_enableChanList  (Fl_Widget *w, void *p);
-       inline void __cb_enableChanList();
+       gdMidiOutputMidiCh(ID channelId);
 
-       /* __cb_close
-       override parent method, we need to do more stuff on close. */
+private:
 
-       static void cb_close  (Fl_Widget *w, void *p);
-       inline void __cb_close();
+       static void cb_enableChanList(Fl_Widget* w, void* p);
+       void cb_enableChanList();
 
-       class geCheck  *enableOut;
-       class geChoice *chanListOut;
+       /* cb_close
+       override parent method, we need to do more stuff on close. */
 
-       class giada::m::MidiChannel *ch;
+       static void cb_close(Fl_Widget* w, void* p);
+       void cb_close();
 
-public:
+       geCheck*  m_enableOut;
+       geChoice* m_chanListOut;
 
-       gdMidiOutputMidiCh(class giada::m::MidiChannel *ch);
+       ID m_channelId;
 };
+}} // giada::v::
 
 
 #endif
index 50f675f1645c1b50314677f2146edca3fb1c967c..1e8f0679c580d62bb975642ae22c27aca316b238 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/sampleChannel.h"
-#include "../../../utils/gui.h"
-#include "../../elems/midiLearner.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/check.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "utils/gui.h"
+#include "gui/elems/midiLearner.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/check.h"
 #include "midiOutputSampleCh.h"
 
 
-using namespace giada;
-
-
-gdMidiOutputSampleCh::gdMidiOutputSampleCh(m::SampleChannel* ch)
-       : gdMidiOutputBase(300, 140), ch(ch)
+namespace giada {
+namespace v 
 {
-       setTitle(ch->index+1);
+gdMidiOutputSampleCh::gdMidiOutputSampleCh(ID channelId)
+: gdMidiOutputBase(300, 140), 
+  m_channelId     (channelId)
+{
+       m::model::ChannelsLock l(m::model::channels);
+       m::Channel& c = m::model::get(m::model::channels, m_channelId);
+       
+       setTitle(c.id);
 
-       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, ch);
-       new geMidiLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",   
-               cb_learn, &ch->midiOutLmute, ch);
-       new geMidiLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",   
-               cb_learn, &ch->midiOutLsolo, ch);
+       m_enableLightning = new geCheck(8, 8, 120, 20, "Enable MIDI lightning output");
+       m_learners.push_back(new geMidiLearner(8, m_enableLightning->y()+m_enableLightning->h()+8, w()-16, "playing", 
+               c.midiOutLplaying, m_channelId));
+       m_learners.push_back(new geMidiLearner(8, m_enableLightning->y()+m_enableLightning->h()+32, w()-16, "mute",   
+               c.midiOutLmute, m_channelId));
+       m_learners.push_back(new geMidiLearner(8, m_enableLightning->y()+m_enableLightning->h()+56, w()-16, "solo",   
+               c.midiOutLsolo, m_channelId));
 
-       close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
-       close->callback(cb_close, (void*)this);
+       m_close = new geButton(w()-88, m_enableLightning->y()+m_enableLightning->h()+84, 80, 20, "Close");
+       m_close->callback(cb_close, (void*)this);
 
-       enableLightning->value(ch->midiOutL);
-       enableLightning->callback(cb_enableLightning, (void*)this);
+       m_enableLightning->value(c.midiOutL);
+       m_enableLightning->callback(cb_enableLightning, (void*)this);
 
        set_modal();
        u::gui::setFavicon(this);
@@ -72,6 +77,11 @@ void gdMidiOutputSampleCh::cb_close(Fl_Widget* w, void* p) { ((gdMidiOutputSampl
 
 void gdMidiOutputSampleCh::cb_close()
 {
-       ch->midiOutL = enableLightning->value();
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               c.midiOutL = m_enableLightning->value();
+       });
        do_callback();
 }
+
+}} // giada::v::
index dea121fde12df3a8872cc56583659720906621f3..605d050e4768046bbcd38a92e1853ed1f6f5fd5e 100644 (file)
 #include "midiOutputBase.h"
 
 
+namespace giada {
+namespace v 
+{
 class gdMidiOutputSampleCh : public gdMidiOutputBase
 {
-private:
+public:
+
+    gdMidiOutputSampleCh(ID channelId);
 
-       giada::m::SampleChannel* ch;
+private:
 
        /* cb_close
        Override parent method, we need to do more stuff on close. */
 
        static void cb_close(Fl_Widget* w, void* p);
-       inline void cb_close();
-
-public:
+       void cb_close();
 
-       gdMidiOutputSampleCh(giada::m::SampleChannel* ch);
+       ID m_channelId;
 };
+}} // giada::v::
+
 
 #endif
index f8dcb26e6064192a6d5c36c2e427422dabc3b6a2..bdf3143795977ce75f1f44fd9758c370756f9732 100644 (file)
 #ifdef WITH_VST
 
 
-#include "../../glue/plugin.h"
-#include "../../utils/gui.h"
-#include "../../core/channel.h"
-#include "../../core/conf.h"
-#include "../../core/pluginManager.h"
-#include "../../core/pluginHost.h"
-#include "../elems/plugin/pluginBrowser.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/choice.h"
-#include "../elems/basics/box.h"
+#include "glue/plugin.h"
+#include "utils/gui.h"
+#include "core/channels/channel.h"
+#include "core/conf.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
+#include "gui/elems/plugin/pluginBrowser.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/box.h"
 #include "pluginChooser.h"
 
 
-using namespace giada;
-using namespace giada::m;
-using namespace giada::c;
-
-
-gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, pluginHost::StackType t, Channel* ch)
-  : gdWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(t)
+namespace giada {
+namespace v
+{
+gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID chanID)
+: gdWindow(X, Y, W, H, "Available plugins"),
+  m_chanID(chanID)
 {
-  /* top area */
-  Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
-  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();
-
-  /* center browser */
-  browser = new gePluginBrowser(8, 36, w()-16, h()-70);
-
-  /* ok/cancel buttons */
-  Fl_Group *group_btn = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, h()-browser->h()-16);
-    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();
-
-  end();
-
-  sortMethod->add("Name");
-  sortMethod->add("Category");
-  sortMethod->add("Manufacturer");
-  sortMethod->callback(cb_sort, (void*) this);
-  sortMethod->value(conf::pluginSortMethod);
-
-  addBtn->callback(cb_add, (void*) this);
-  addBtn->shortcut(FL_Enter);
-  cancelBtn->callback(cb_close, (void*) this);
-
-  resizable(browser);
+       /* top area */
+       Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
+       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();
+
+       /* center browser */
+       browser = new v::gePluginBrowser(8, 36, w()-16, h()-70);
+
+       /* ok/cancel buttons */
+       Fl_Group *group_btn = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, h()-browser->h()-16);
+               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();
+
+       end();
+
+       sortMethod->add("Name");
+       sortMethod->add("Category");
+       sortMethod->add("Manufacturer");
+       sortMethod->callback(cb_sort, (void*) this);
+       sortMethod->value(m::conf::pluginSortMethod);
+
+       addBtn->callback(cb_add, (void*) this);
+       addBtn->shortcut(FL_Enter);
+       cancelBtn->callback(cb_close, (void*) this);
+
+       resizable(browser);
        u::gui::setFavicon(this);
-  show();
+       show();
 }
 
 
@@ -90,11 +89,11 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, pluginHost::StackTy
 
 gdPluginChooser::~gdPluginChooser()
 {
-  conf::pluginChooserX = x();
-  conf::pluginChooserY = y();
-  conf::pluginChooserW = w();
-  conf::pluginChooserH = h();
-  conf::pluginSortMethod = sortMethod->value();
+       m::conf::pluginChooserX = x();
+       m::conf::pluginChooserY = y();
+       m::conf::pluginChooserW = w();
+       m::conf::pluginChooserH = h();
+       m::conf::pluginSortMethod = sortMethod->value();
 }
 
 
@@ -120,8 +119,8 @@ void gdPluginChooser::cb_close()
 
 void gdPluginChooser::cb_sort()
 {
-       pluginManager::sortPlugins(static_cast<pluginManager::SortMethod>(sortMethod->value()));
-  browser->refresh();
+       m::pluginManager::sortPlugins(static_cast<m::pluginManager::SortMethod>(sortMethod->value()));
+       browser->refresh();
 }
 
 
@@ -130,11 +129,13 @@ void gdPluginChooser::cb_sort()
 
 void gdPluginChooser::cb_add()
 {
-  int index = browser->value() - 3; // subtract header lines
-  if (index < 0)
-    return;
-  plugin::addPlugin(ch, index, stackType);
-  do_callback();
+       int pluginIndex = browser->value() - 3; // subtract header lines
+       if (pluginIndex < 0)
+               return;
+       c::plugin::addPlugin(pluginIndex, m_chanID);
+       do_callback();
 }
+}} // giada::v::
+
 
 #endif // #ifdef WITH_VST
index d8bb3b2e63d6edcb47c00bcadb303ee333febf9f..d5aed3c28bd06e9f770b14e124af110e9c9c890c 100644 (file)
 class geChoice;
 class geButton;
 class geButton;
-class gePluginBrowser;
 
+namespace giada {
+namespace v
+{
+class gePluginBrowser;
 
 class gdPluginChooser : public gdWindow
 {
-private:
+public:
 
-       giada::m::Channel* ch;      // ch == nullptr ? masterOut
-       giada::m::pluginHost::StackType stackType;
+       gdPluginChooser(int x, int y, int w, int h, ID chanID);
+       ~gdPluginChooser();
 
-       geChoice*        sortMethod;
-       geButton*        addBtn;
-       geButton*        cancelBtn;
-       gePluginBrowser* browser;
+private:
 
        static void cb_close(Fl_Widget* w, void* p);
        static void cb_add  (Fl_Widget* w, void* p);
@@ -61,12 +61,14 @@ private:
        void cb_add  ();
        void cb_sort ();
 
-public:
+       geChoice*        sortMethod;
+       geButton*        addBtn;
+       geButton*        cancelBtn;
+       gePluginBrowser* browser;
 
-       gdPluginChooser(int x, int y, int w, int h, giada::m::pluginHost::StackType t, 
-               giada::m::Channel* ch=nullptr);
-       ~gdPluginChooser();
+       ID m_chanID;
 };
+}} // giada::v::
 
 
 #endif
index 7353e28768f96a8376e2fadbd3a57bd6c4438c09..02a70c386568d97311a1810e43be5c0489da256c 100644 (file)
 #ifdef WITH_VST
 
 
+#include <cassert>
 #include <string>
-#include <FL/Fl_Scroll.H>
-#include "../../utils/gui.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/pluginHost.h"
-#include "../../core/channel.h"
-#include "../../utils/string.h"
-#include "../elems/basics/boxtypes.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/statusButton.h"
-#include "../elems/mainWindow/mainIO.h"
-#include "../elems/mainWindow/keyboard/channel.h"
-#include "../elems/plugin/pluginElement.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/pluginHost.h"
+#include "utils/string.h"
+#include "utils/gui.h"
+#include "gui/elems/basics/liquidScroll.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/plugin/pluginElement.h"
 #include "pluginChooser.h"
 #include "mainWindow.h"
 #include "pluginList.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
-using std::string;
-using namespace giada;
-
-
-gdPluginList::gdPluginList(m::pluginHost::StackType t, m::Channel* ch)
-       : gdWindow(468, 204), ch(ch), stackType(t)
+namespace giada {
+namespace v
 {
-       using namespace giada::m;
-
-       if (conf::pluginListX)
-               resize(conf::pluginListX, conf::pluginListY, w(), h());
-
-       list = new Fl_Scroll(8, 8, 476, 188);
-       list->type(Fl_Scroll::VERTICAL);
-       list->scrollbar.color(G_COLOR_GREY_2);
-       list->scrollbar.selection_color(G_COLOR_GREY_4);
-       list->scrollbar.labelcolor(G_COLOR_LIGHT_1);
-       list->scrollbar.slider(G_CUSTOM_BORDER_BOX);
+gdPluginList::gdPluginList(ID chanID)
+: gdWindow(m::conf::pluginListX, m::conf::pluginListY, 468, 204), 
+  m_channelId(chanID)
+{
+       end();
 
-       list->begin();
-               refreshList();
+       list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 
+               w() - (G_GUI_OUTER_MARGIN*2), h() - (G_GUI_OUTER_MARGIN*2));
        list->end();
+       add(list);
 
-       end();
-       set_non_modal();
-
-       /* TODO - awful stuff... we should subclass into gdPluginListChannel and
-       gdPluginListMaster */
+       rebuild();
 
-       if (stackType == pluginHost::StackType::MASTER_OUT)
-               label("Master Out Plugins");
+       if (m_channelId == m::mixer::MASTER_OUT_CHANNEL_ID)
+               label("Master Out Plug-ins");
        else
-       if (stackType == pluginHost::StackType::MASTER_IN)
-               label("Master In Plugins");
+       if (m_channelId == m::mixer::MASTER_IN_CHANNEL_ID)
+               label("Master In Plug-ins");
        else {
-               string l = "Channel " + u::string::iToString(ch->index+1) + " Plugins";
+               std::string l = "Channel " + u::string::iToString(m_channelId + 1) + " Plug-ins";
                copy_label(l.c_str());
        }
 
        u::gui::setFavicon(this);
+       set_non_modal();
        show();
 }
 
@@ -113,22 +103,23 @@ void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_
 /* -------------------------------------------------------------------------- */
 
 
-void gdPluginList::cb_refreshList(Fl_Widget* v, void* p)
+void gdPluginList::rebuild()
 {
-       /* Note: this callback is fired by gdBrowser. Close its window first, by 
-       calling the parent (pluginList) and telling it to delete its subwindow 
-       (i.e. gdBrowser). */
+       /* Clear the previous list. */
 
-       gdWindow* child = static_cast<gdWindow*>(v);
-       if (child->getParent() != nullptr)
-               (child->getParent())->delSubWindow(child);
+       list->clear();
+       list->scroll_to(0, 0);
 
-       /* Finally refresh plugin list: void *p is a pointer to gdPluginList. This 
-       callback works even when you click 'x' to close the window... well, it does
-       not matter. */
+       m::model::ChannelsLock l(m::model::channels);
 
-       ((gdPluginList*)p)->refreshList();
-       ((gdPluginList*)p)->redraw();
+       const m::Channel& ch = m::model::get(m::model::channels, m_channelId);
+
+       for (ID pluginId : ch.pluginIds)
+               list->addWidget(new gePluginElement(pluginId, m_channelId, 0, 0, 0));
+       
+       addPlugin = list->addWidget(new geButton(0, 0, 0, G_GUI_UNIT, "-- add new plugin --"));
+       
+       addPlugin->callback(cb_addPlugin, (void*)this);
 }
 
 
@@ -137,80 +128,37 @@ void gdPluginList::cb_refreshList(Fl_Widget* v, void* p)
 
 void gdPluginList::cb_addPlugin()
 {
-       using namespace giada::m;
-
-       /* 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(conf::pluginChooserX,
-                       conf::pluginChooserY, conf::pluginChooserW, conf::pluginChooserH,
-                       stackType, ch);
-       addSubWindow(pc);
-       pc->callback(cb_refreshList, (void*)this);      // 'this' refers to gdPluginList
+       int wx = m::conf::pluginChooserX;
+       int wy = m::conf::pluginChooserY;
+       int ww = m::conf::pluginChooserW;
+       int wh = m::conf::pluginChooserH;
+       u::gui::openSubWindow(G_MainWin, new v::gdPluginChooser(wx, wy, ww, wh, 
+               m_channelId), WID_FX_CHOOSER);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdPluginList::refreshList()
+const gePluginElement& gdPluginList::getNextElement(const gePluginElement& currEl) const
 {
-       using namespace giada::m;
-
-       /* delete the previous list */
-
-       list->clear();
-       list->scroll_to(0, 0);
-
-       /* add new buttons, as many as the plugin in pluginHost::stack + 1,
-        * the 'add new' button. Warning: if ch == nullptr we are working with
-        * master in/master out stacks. */
-
-       int numPlugins = pluginHost::countPlugins(stackType, ch);
-       int i = 0;
-
-       while (i<numPlugins) {
-               Plugin*          plugin = pluginHost::getPluginByIndex(i, stackType, ch);
-               gePluginElement* gdpe   = new gePluginElement(this, plugin, list->x(), 
-                       list->y()-list->yposition()+(i*24), 800);
-               list->add(gdpe);
-               i++;
-       }
-
-       int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24);
-       addPlugin = new geButton(8, addPlugY, 452, 20, "-- add new plugin --");
-       addPlugin->callback(cb_addPlugin, (void*)this);
-       list->add(addPlugin);
-
-       /* if num(plugins) > 7 make room for the side scrollbar.
-        * Scrollbar.width = 20 + 4(margin) */
-
-       if (i>7)
-               size(492, h());
-       else
-               size(468, h());
-
-       redraw();
-
-       /* set 'full' flag to FX button */
+       int curr = list->find(currEl);
+       int next = curr + 1;
+       if (next > list->countChildren() - 2)
+               next = list->countChildren() - 2;
+       return *static_cast<gePluginElement*>(list->child(next));
+}
 
-       /* TODO - awful stuff... we should subclass into gdPluginListChannel and
-       gdPluginListMaster */
 
-       if (stackType == pluginHost::StackType::MASTER_OUT) {
-               G_MainWin->mainIO->setMasterFxOutFull(pluginHost::countPlugins(stackType, ch) > 0);
-       }
-       else
-       if (stackType == pluginHost::StackType::MASTER_IN) {
-               G_MainWin->mainIO->setMasterFxInFull(pluginHost::countPlugins(stackType, ch) > 0);
-       }
-       else {
-               ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0;
-               ch->guiChannel->fx->redraw();
-       }
+const gePluginElement& gdPluginList::getPrevElement(const gePluginElement& currEl) const
+{
+       int curr = list->find(currEl);
+       int prev = curr - 1;
+       if (prev < 0)
+               prev = 0;
+       return *static_cast<gePluginElement*>(list->child(prev));
 }
+}} // giada::v::
 
 
 #endif // #ifdef WITH_VST
index 3b89961fb4b1936856c0d45f2bd41eddfd5cfcda..655db5231c4483a00a2875f67a9946bbe55b285a 100644 (file)
 
 #ifdef WITH_VST
 
+
 #ifndef GD_PLUGINLIST_H
 #define GD_PLUGINLIST_H
 
 
-#include "../../core/pluginHost.h"
+#include "core/pluginHost.h"
 #include "window.h"
 
 
-class Fl_Scroll;
+class geLiquidScroll;
 class geButton;
 
 
+namespace giada {
+namespace v
+{
+class gePluginElement;
 class gdPluginList : public gdWindow
 {
-private:
-
-       geButton*  addPlugin;
-       Fl_Scroll* list;
+public:
 
-       static void cb_addPlugin(Fl_Widget* v, void* p);
-       void cb_addPlugin();
+       gdPluginList(ID chanID);
+       ~gdPluginList();
 
-public:
+       void rebuild() override;
 
-       giada::m::Channel* ch;      // ch == nullptr ? masterOut
-       giada::m::pluginHost::StackType stackType;
+       const gePluginElement& getNextElement(const gePluginElement& curr) const;
+       const gePluginElement& getPrevElement(const gePluginElement& curr) const;
 
-       gdPluginList(giada::m::pluginHost::StackType t, giada::m::Channel* ch=nullptr);
-       ~gdPluginList();
+private:
 
-       /* special callback, passed to browser. When closed (i.e. plugin
-        * has been selected) the same browser will refresh this window. */
+       static void cb_addPlugin(Fl_Widget* v, void* p);
+       void cb_addPlugin();
 
-       static void cb_refreshList(Fl_Widget*, void*);
+       geButton*       addPlugin;
+       geLiquidScroll* list;   
 
-       void refreshList();
+       ID m_channelId;
 };
 
+}} // giada::v::
+
 
 #endif
 
index 9c4ab8a0c89fa77a77ec91d6f11d572eccdc6b2e..0f6e8433c3b64b378cc1c1bc32b54114af1ecfac 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../utils/gui.h"
-#include "../../core/plugin.h"
-#include "../../core/const.h"
-#include "../elems/basics/liquidScroll.h"
-#include "../elems/plugin/pluginParameter.h"
+#include "utils/gui.h"
+#include "core/plugin.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "gui/elems/basics/liquidScroll.h"
+#include "gui/elems/plugin/pluginParameter.h"
 #include "pluginWindow.h"
 
 
-using namespace giada;
-
-
-gdPluginWindow::gdPluginWindow(m::Plugin* p)
- : gdWindow(450, 156), m_plugin(p)
+namespace giada {
+namespace v
+{
+gdPluginWindow::gdPluginWindow(ID pluginId)
+: gdWindow  (450, 156), 
+  m_pluginId(pluginId)
 {
        set_non_modal();
 
        m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 
                w()-(G_GUI_OUTER_MARGIN*2), h()-(G_GUI_OUTER_MARGIN*2));
 
+       m::model::PluginsLock l(m::model::plugins);
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+       
        m_list->type(Fl_Scroll::VERTICAL_ALWAYS);
        m_list->begin();
                int labelWidth = getLabelWidth();
-               int numParams = m_plugin->getNumParameters();
+               int numParams = p.getNumParameters();
                for (int i=0; i<numParams; i++) {
                        int py = m_list->y() + (i * (G_GUI_UNIT + G_GUI_INNER_MARGIN));
                        int pw = m_list->w() - m_list->scrollbar_size() - (G_GUI_OUTER_MARGIN*3);
-                       new gePluginParameter(i, m_plugin, m_list->x(), py, pw, labelWidth);
+                       new v::gePluginParameter(i, m_pluginId, m_list->x(), py, pw, labelWidth);
                }
        m_list->end();
 
        end();
 
-       label(m_plugin->getName().c_str());
+       label(p.getName().c_str());
 
        size_range(450, (G_GUI_UNIT + (G_GUI_OUTER_MARGIN*2)));
        resizable(m_list);
@@ -76,15 +81,18 @@ gdPluginWindow::gdPluginWindow(m::Plugin* p)
 
 void gdPluginWindow::updateParameter(int index, bool changeSlider)
 {
-       static_cast<gePluginParameter*>(m_list->child(index))->update(changeSlider);
+       static_cast<v::gePluginParameter*>(m_list->child(index))->update(changeSlider);
 }
 
 
 void gdPluginWindow::updateParameters(bool changeSlider)
 {
-       for (int i=0; i<m_plugin->getNumParameters(); i++) {
-               static_cast<gePluginParameter*>(m_list->child(i))->update(changeSlider);
-       }
+       m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
+       {
+               for (int i=0; i<p.getNumParameters(); i++) {
+                       static_cast<v::gePluginParameter*>(m_list->child(i))->update(changeSlider);
+               }
+       });
 }
 
 
@@ -93,16 +101,20 @@ void gdPluginWindow::updateParameters(bool changeSlider)
 
 int gdPluginWindow::getLabelWidth() const
 {
+       m::model::PluginsLock l(m::model::plugins);
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+
        int width = 0;
-       int numParams = m_plugin->getNumParameters();
+       int numParams = p.getNumParameters();
        for (int i=0; i<numParams; i++) {
                int wl = 0, hl = 0;   
-               fl_measure(m_plugin->getParameterName(i).c_str(), wl, hl);
+               fl_measure(p.getParameterName(i).c_str(), wl, hl);
                if (wl > width)
                        width = wl;
        }
        return width;
 }
+}} // giada::v::
 
 
 #endif // #ifdef WITH_VST
index dcd81bb8e4a6bc32848c518b20b2ccf950579529..e4b3fba6a905fd99b6d65a04eb8b7d7206d7df6c 100644 (file)
 
 #ifdef WITH_VST
 
+
 #ifndef GD_PLUGIN_WINDOW_H
 #define GD_PLUGIN_WINDOW_H
 
 
-#include "../../core/plugin.h"
 #include "window.h"
 
 
@@ -40,23 +40,31 @@ class geSlider;
 class geLiquidScroll;
 
 
+namespace giada {
+namespace m
+{
+class Plugin;
+}
+namespace v
+{
 class gdPluginWindow : public gdWindow
 {
-private:
-
-       giada::m::Plugin* m_plugin;
-
-       geLiquidScroll* m_list;
-
-       int getLabelWidth() const;
-
 public:
 
-       gdPluginWindow(giada::m::Plugin* p);
+       gdPluginWindow(ID pluginId);
 
        void updateParameter(int index, bool changeSlider=false);
        void updateParameters(bool changeSlider=false);
+
+private:
+
+       int getLabelWidth() const;
+       
+       ID m_pluginId;
+               
+       geLiquidScroll* m_list;
 };
+}} // giada::v::
 
 
 #endif
index 6eb8181a669a90bc276ef97f510ec6c7a783c057..3394456eea3bb6a58f912ad087931b61e468df71 100644 (file)
 
 
 #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 "utils/log.h"
+#include "utils/gui.h"
+#include "core/pluginHost.h"
+#include "core/model/model.h"
+#include "core/plugin.h"
+#include "core/const.h"
 #include "pluginWindowGUI.h"
-
 #ifdef G_OS_MAC
-#import "../../utils/cocoa.h" // objective-c
+#import "utils/cocoa.h" // objective-c
 #endif
 
 
-using namespace giada::m;
-
-
-gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin)
+namespace giada {
+namespace v
+{
+gdPluginWindowGUI::gdPluginWindowGUI(ID pluginId)
 #ifdef G_OS_MAC
- : gdWindow(Fl::w(), Fl::h()), m_plugin(plugin)
+: gdWindow     (Fl::w(), Fl::h()),
 #else
- : gdWindow(320, 200), m_plugin(plugin)
+: gdWindow     (320, 200),
 #endif
+  m_pluginId(pluginId),
+  m_ui         (nullptr)
 {
-  show();
+       show();
 
-#if defined(G_OS_LINUX) || defined(G_OS_MAC)
+#if defined(G_OS_LINUX) || defined(G_OS_MAC) || defined(G_OS_FREEBSD)
 
-  /*  Fl_Window::show() is not guaranteed to show and draw the window on all 
-  platforms immediately. Instead this is done in the background; particularly on 
-  X11 it will take a few messages (client server roundtrips) to display the 
-  window. Usually this small delay doesn't matter, but in some cases you may 
-  want to have the window instantiated and displayed synchronously. Currently 
-  (as of FLTK 1.3.4) this method has an effect on X11 and Mac OS. 
+       /*  Fl_Window::show() is not guaranteed to show and draw the window on all 
+       platforms immediately. Instead this is done in the background; particularly on 
+       X11 it will take a few messages (client server roundtrips) to display the 
+       window. Usually this small delay doesn't matter, but in some cases you may 
+       want to have the window instantiated and displayed synchronously. Currently 
+       (as of FLTK 1.3.4) this method has an effect on X11 and Mac OS. 
 
-  http://www.fltk.org/doc-1.3/classFl__Window.html#aafbec14ca8ff8abdaff77a35ebb23dd8 */
+       http://www.fltk.org/doc-1.3/classFl__Window.html#aafbec14ca8ff8abdaff77a35ebb23dd8 */
 
-  wait_for_expose();
-  Fl::flush();
+       wait_for_expose();
+       Fl::flush();
 
 #endif
 
-  gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
-    (void*) this, (void*) fl_xid(this));
+       u::log::print("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
+               (void*) this, (void*) fl_xid(this));
 
 #ifdef G_OS_MAC
 
-  void* cocoaWindow = (void*) fl_xid(this);
-  m_plugin->showEditor(cocoa_getViewFromWindow(cocoaWindow));
+       void* cocoaWindow = (void*) fl_xid(this);
+       openEditor(cocoa_getViewFromWindow(cocoaWindow));
 
 #else
 
-  m_plugin->showEditor((void*) fl_xid(this));
+       openEditor((void*) fl_xid(this));
 
-  int pluginW = m_plugin->getEditorW();
-  int pluginH = m_plugin->getEditorH();
+       int pluginW = m_ui->getWidth();
+       int pluginH = m_ui->getHeight();
 
-  resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
+       resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
 
 
 #endif
 
-  Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+       Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+
+       m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
+       {
+               copy_label(p.getName().c_str());
+       });
+}
 
-  copy_label(m_plugin->getName().c_str());
 
+/* -------------------------------------------------------------------------- */
+
+
+gdPluginWindowGUI::~gdPluginWindowGUI()
+{
+       cb_close();
 }
 
 
@@ -108,9 +121,9 @@ void gdPluginWindowGUI::cb_refresh(void* data) { ((gdPluginWindowGUI*)data)->cb_
 
 void gdPluginWindowGUI::cb_close()
 {
-  Fl::remove_timeout(cb_refresh);
-  m_plugin->closeEditor();
-  gu_log("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
+       Fl::remove_timeout(cb_refresh);
+       closeEditor();
+       u::log::print("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
 }
 
 
@@ -119,17 +132,39 @@ void gdPluginWindowGUI::cb_close()
 
 void gdPluginWindowGUI::cb_refresh()
 {
-  pluginHost::runDispatchLoop();
-  Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+       m::pluginHost::runDispatchLoop();
+       Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-gdPluginWindowGUI::~gdPluginWindowGUI()
+void gdPluginWindowGUI::openEditor(void* parent)
+{
+       m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
+       {
+               m_ui = p.createEditor();
+       });
+       if (m_ui == nullptr) {
+               u::log::print("[gdPluginWindowGUI::openEditor] unable to create editor!\n");
+               return;
+       }
+       m_ui->setOpaque(true);
+       m_ui->addToDesktop(0, parent);  
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPluginWindowGUI::closeEditor()
 {
-  cb_close();
+       delete m_ui;
+       m_ui = nullptr;
 }
 
+}} // giada::v::
+
+
 #endif // #ifdef WITH_VST
index 605e670aae753c1cdcdc7759b1832f37a5cf1134..915051c0b39cb6493f650ffe8365c278c94c808b 100644 (file)
 #include <FL/Fl.H>
 #include <FL/Fl_Window.H>
 #include "window.h"
-#if defined(__APPLE__)
-       #include <Carbon/Carbon.h>
-#endif
 
 
+namespace giada {
+namespace v
+{
 class gdPluginWindowGUI : public gdWindow
 {
-private:
+public:
+
+       gdPluginWindowGUI(ID pluginId);
+       ~gdPluginWindowGUI();
 
-       giada::m::Plugin* m_plugin;
+private:
 
-       static void cb_close  (Fl_Widget* v, void* p);
+       static void cb_close(Fl_Widget* v, void* p);
        static void cb_refresh(void* data);
-       inline void cb_close  ();
-       inline void cb_refresh();
+       void cb_close();
+       void cb_refresh();
 
-public:
+       void openEditor(void* parent); 
+       void closeEditor(); 
 
-       gdPluginWindowGUI(giada::m::Plugin* p);
-       ~gdPluginWindowGUI();
+       ID m_pluginId;
+
+       juce::AudioProcessorEditor* m_ui;
 };
+}} // giada::v::
 
 
 #endif // include guard
 
+
 #endif // #ifdef WITH_VST
index 8715da71a74f9df1925079787787f9654ca5683e..75555be8eefb7940de0c80880cf7e8db30168ff1 100644 (file)
 
 
 #include <cmath>
+#include <cassert>
 #include <FL/Fl.H>
 #include <FL/Fl_Group.H>
-//#include <FL/fl_draw.H>
-#include "../../glue/channel.h"
-#include "../../glue/sampleEditor.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 "../../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/sampleEditor/shiftTool.h"
-#include "../elems/mainWindow/keyboard/channel.h"
-#include "warnings.h"
+#include "glue/channel.h"
+#include "glue/sampleEditor.h"
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "core/waveFx.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/graphics.h"
+#include "core/mixer.h"
+#include "core/wave.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/check.h"
+#include "gui/elems/sampleEditor/waveform.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/shiftTool.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/dialogs/warnings.h"
 #include "sampleEditor.h"
 
 
-using std::string;
-using namespace giada;
+namespace giada {
+namespace v 
+{
+gdSampleEditor::gdSampleEditor(ID channelId, ID waveId)
+: gdWindow   (m::conf::sampleEditorX, m::conf::sampleEditorY, 
+                 m::conf::sampleEditorW, m::conf::sampleEditorH),
+  m_channelId(channelId),
+  m_waveId   (waveId)
+{
+       Fl_Group* upperBar = createUpperBar();
+       
+       waveTools = new geWaveTools(channelId, waveId, G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN, 
+               w()-16, h()-128);
+       
+       Fl_Group* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN, 
+               h()-waveTools->h()-upperBar->h()-32);
+
+       end();
+
+       add(upperBar);
+       add(waveTools);
+       add(bottomBar);
+
+       resizable(waveTools);
+
+       u::gui::setFavicon(this);
+
+       size_range(720, 480);
+       set_non_modal();
+       rebuild();
+       show();
+}
+
+
+/* -------------------------------------------------------------------------- */
 
 
-gdSampleEditor::gdSampleEditor(m::SampleChannel* ch)
-  : gdWindow(640, 480),
-    ch(ch)
+gdSampleEditor::~gdSampleEditor()
 {
-  using namespace giada::m;
-
-  Fl_Group* upperBar = createUpperBar();
-  
-  waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN, 
-    w()-16, h()-128, ch);
-  
-  Fl_Group* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN, 
-       h()-waveTools->h()-upperBar->h()-32);
-
-  add(upperBar);
-  add(waveTools);
-  add(bottomBar);
-
-  resizable(waveTools);
-
-  u::gui::setFavicon(this);
-  set_non_modal();
-  copy_label(ch->name.c_str());
-
-  size_range(720, 480);
-  if (conf::sampleEditorX)
-    resize(conf::sampleEditorX, conf::sampleEditorY, conf::sampleEditorW, 
-       conf::sampleEditorH);
-  
-  show();
+       m::conf::sampleEditorX = x();
+       m::conf::sampleEditorY = y();
+       m::conf::sampleEditorW = w();
+       m::conf::sampleEditorH = h();
+       m::conf::sampleEditorGridVal = atoi(grid->text());
+       m::conf::sampleEditorGridOn  = snap->value();
+       
+       c::sampleEditor::setPreview(m_channelId, PreviewMode::NONE);
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::rebuild()
+{
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               copy_label(c.name.c_str());
+       });
+       
+       volumeTool->rebuild();
+       waveTools->rebuild();
+       panTool->rebuild();
+       pitchTool->rebuild();
+       rangeTool->rebuild();
+       shiftTool->rebuild();
+
+       m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w)
+       {
+               updateInfo(w);
+               if (w.isLogical()) // Logical samples (aka takes) cannot be reloaded.
+                       reload->deactivate();
+       });
+}
+
 
 /* -------------------------------------------------------------------------- */
 
 
-gdSampleEditor::~gdSampleEditor()
+void gdSampleEditor::refresh()
 {
-  m::conf::sampleEditorX = x();
-  m::conf::sampleEditorY = y();
-  m::conf::sampleEditorW = w();
-  m::conf::sampleEditorH = h();
-  m::conf::sampleEditorGridVal = atoi(grid->text());
-  m::conf::sampleEditorGridOn  = snap->value();
-  c::sampleEditor::setPreview(ch, PreviewMode::NONE);
+       waveTools->refresh();
+       
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               play->setStatus(c.previewMode == PreviewMode::LOOP || c.previewMode == PreviewMode::NORMAL ? 1 : 0);
+       });
 }
 
 
@@ -117,42 +156,40 @@ gdSampleEditor::~gdSampleEditor()
 
 Fl_Group* gdSampleEditor::createUpperBar()
 {
-  using namespace giada::m;
-
-  Fl_Group* g = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT);
-  g->begin();
-    grid    = new geChoice(g->x(), g->y(), 50, G_GUI_UNIT);
-    snap    = new geCheck(grid->x()+grid->w()+4, g->y()+3, 12, 12, "Snap");
-    sep1    = new geBox(snap->x()+snap->w()+4, g->y(), 506, G_GUI_UNIT);
-    zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
-    zoomIn  = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
-  g->end();
-  g->resizable(sep1);
-
-  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");
-  if (conf::sampleEditorGridVal == 0)
-    grid->value(0);
-  else 
-    grid->value(grid->find_item(u::string::iToString(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) */
-
-  zoomOut->callback(cb_zoomOut, (void*)this);
-  zoomIn->callback(cb_zoomIn, (void*)this);
-
-  return g;
+       Fl_Group* g = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT);
+       g->begin();
+               grid    = new geChoice(g->x(), g->y(), 50, G_GUI_UNIT);
+               snap    = new geCheck(grid->x()+grid->w()+4, g->y(), 12, G_GUI_UNIT, "Snap");
+               sep1    = new geBox(snap->x()+snap->w()+4, g->y(), g->w() - 118, G_GUI_UNIT);
+               zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
+               zoomIn  = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+       g->end();
+       g->resizable(sep1);
+
+       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");
+       if (m::conf::sampleEditorGridVal == 0)
+               grid->value(0);
+       else 
+               grid->value(grid->find_item(u::string::iToString(m::conf::sampleEditorGridVal).c_str()));
+       grid->callback(cb_changeGrid, (void*)this);
+
+       snap->value(m::conf::sampleEditorGridOn);
+       snap->callback(cb_enableSnap, (void*)this);
+
+       /* TODO - redraw grid if != (off) */
+
+       zoomOut->callback(cb_zoomOut, (void*)this);
+       zoomIn->callback(cb_zoomIn, (void*)this);
+
+       return g;
 }
 
 
@@ -161,26 +198,22 @@ Fl_Group* gdSampleEditor::createUpperBar()
 
 Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h)
 {
-  Fl_Group* g = new Fl_Group(x, y, 572, h);
-  g->begin();
-  g->resizable(0);
-    volumeTool = new geVolumeTool(g->x(), g->y(), ch);
-    boostTool  = new geBoostTool(volumeTool->x()+volumeTool->w()+4, g->y(), ch);
-    panTool    = new gePanTool(boostTool->x()+boostTool->w()+4, g->y(), ch);
-   
-    pitchTool = new gePitchTool(g->x(), panTool->y()+panTool->h()+8, ch);
-
-    rangeTool = new geRangeTool(g->x(), pitchTool->y()+pitchTool->h()+8, ch);
-    shiftTool = new geShiftTool(rangeTool->x()+rangeTool->w()+4, pitchTool->y()+pitchTool->h()+8, ch);
-    reload    = new geButton(g->x()+g->w()-70, shiftTool->y(), 70, 20, "Reload");
-  g->end();
-
-  if (ch->wave->isLogical()) // Logical samples (aka takes) cannot be reloaded.
-    reload->deactivate();
-
-  reload->callback(cb_reload, (void*)this);
-
-  return g;
+       Fl_Group* g = new Fl_Group(x, y, 572, h);
+       g->begin();
+       g->resizable(0);
+               volumeTool = new geVolumeTool(m_channelId, g->x(), g->y());
+               panTool    = new gePanTool(m_channelId, volumeTool->x()+volumeTool->w()+4, g->y());
+        
+               pitchTool = new gePitchTool(m_channelId, g->x(), panTool->y()+panTool->h()+8);
+
+               rangeTool = new geRangeTool(m_channelId, m_waveId, g->x(), pitchTool->y()+pitchTool->h()+8);
+               shiftTool = new geShiftTool(m_channelId, m_waveId, rangeTool->x()+rangeTool->w()+4, pitchTool->y()+pitchTool->h()+8);
+               reload    = new geButton(g->x()+g->w()-70, shiftTool->y(), 70, 20, "Reload");
+       g->end();
+
+       reload->callback(cb_reload, (void*)this);
+
+       return g;
 }
 
 
@@ -189,21 +222,17 @@ Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h)
 
 Fl_Group* gdSampleEditor::createPreviewBox(int x, int y, int h)
 {
-  Fl_Group* g = new Fl_Group(x, y, 110, h);
-  g->begin();
-    rewind = new geButton(g->x(), g->y()+(g->h()/2)-12, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
-    play   = new geButton(rewind->x()+rewind->w()+4, g->y()+(g->h()/2)-12, 25, 25, "", play_xpm, pause_xpm);
-    loop   = new geCheck(play->x()+play->w()+6, g->y()+(g->h()/2)-6, 12, 12, "Loop");
-  g->end();
-
-  play->callback(cb_togglePreview, (void*)this);
-  rewind->callback(cb_rewindPreview, (void*)this);
+       Fl_Group* g = new Fl_Group(x, y, 110, h);
+       g->begin();
+               rewind = new geButton(g->x(), g->y()+(g->h()/2)-12, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+               play   = new geStatusButton(rewind->x()+rewind->w()+4, rewind->y(), 25, 25, play_xpm, pause_xpm);
+               loop   = new geCheck(play->x()+play->w()+4, play->y(), 12, 25, "Loop");
+       g->end();
 
-  ch->onPreviewEnd = [this] { 
-       play->value(0);
-  };
+       play->callback(cb_togglePreview, (void*)this);
+       rewind->callback(cb_rewindPreview, (void*)this);
 
-  return g;
+       return g;
 }
 
 
@@ -212,16 +241,14 @@ Fl_Group* gdSampleEditor::createPreviewBox(int x, int y, int h)
 
 Fl_Group* gdSampleEditor::createInfoBox(int x, int y, int h)
 {
-  Fl_Group* g = new Fl_Group(x, y, 400, h);
-  g->begin();
-    info = new geBox(g->x(), g->y(), g->w(), g->h());
-  g->end();    
+       Fl_Group* g = new Fl_Group(x, y, 400, h);
+       g->begin();
+               info = new geBox(g->x(), g->y(), g->w(), g->h());
+       g->end();       
 
-  info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
-  
-  updateInfo();
+       info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
 
-  return g;
+       return g;
 }
 
 
@@ -230,24 +257,24 @@ Fl_Group* gdSampleEditor::createInfoBox(int x, int y, int h)
 
 Fl_Group* gdSampleEditor::createBottomBar(int x, int y, int h)
 {
-  Fl_Group* g = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, h);
-  g->begin();
-    Fl_Group* previewBox = createPreviewBox(g->x(), g->y(), g->h());
+       Fl_Group* g = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, h);
+       g->begin();
+               Fl_Group* previewBox = createPreviewBox(g->x(), g->y(), g->h());
 
-    geBox* divisor1 = new geBox(previewBox->x()+previewBox->w()+8, g->y(), 1, g->h());
-    divisor1->box(FL_BORDER_BOX);
+               geBox* divisor1 = new geBox(previewBox->x()+previewBox->w()+8, g->y(), 1, g->h());
+               divisor1->box(FL_BORDER_BOX);
 
-    Fl_Group* opTools = createOpTools(divisor1->x()+divisor1->w()+12, g->y(), g->h());
+               Fl_Group* opTools = createOpTools(divisor1->x()+divisor1->w()+12, g->y(), g->h());
 
-    geBox* divisor2 = new geBox(opTools->x()+opTools->w()+8, g->y(), 1, g->h());
-    divisor2->box(FL_BORDER_BOX);
+               geBox* divisor2 = new geBox(opTools->x()+opTools->w()+8, g->y(), 1, g->h());
+               divisor2->box(FL_BORDER_BOX);
 
-    createInfoBox(divisor2->x()+divisor2->w()+8, g->y(), g->h());
+               createInfoBox(divisor2->x()+divisor2->w()+8, g->y(), g->h());
 
-  g->end();
-  g->resizable(0);
+       g->end();
+       g->resizable(0);
 
-  return g;
+       return g;
 }
 
 
@@ -268,7 +295,7 @@ void gdSampleEditor::cb_rewindPreview(Fl_Widget* w, void* p) { ((gdSampleEditor*
 
 void gdSampleEditor::cb_enableSnap()
 {
-  waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
+       waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
 }
 
 
@@ -277,18 +304,16 @@ void gdSampleEditor::cb_enableSnap()
 
 void gdSampleEditor::cb_togglePreview()
 {
-  using namespace giada::c;
-
-       if (play->value())
-       sampleEditor::setPreview(ch, PreviewMode::NONE);
+       if (play->getStatus())
+               c::sampleEditor::setPreview(m_channelId, PreviewMode::NONE);
        else
-       sampleEditor::setPreview(ch, loop->value() ? PreviewMode::LOOP : PreviewMode::NORMAL);
+               c::sampleEditor::setPreview(m_channelId, loop->value() ? PreviewMode::LOOP : PreviewMode::NORMAL);
 }
 
 
 void gdSampleEditor::cb_rewindPreview()
 {
-       c::sampleEditor::rewindPreview(ch);
+       c::sampleEditor::rewindPreview(m_channelId);
 }
 
 
@@ -297,28 +322,8 @@ void gdSampleEditor::cb_rewindPreview()
 
 void gdSampleEditor::cb_reload()
 {
-  using namespace giada::c;
-
-  /* TODO - move to glue::sampleEditor */
-  if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
-    return;
-
-  if (channel::loadChannel(ch, ch->wave->getPath()) != G_RES_OK)
-    return;
-
-  channel::setBoost(ch, G_DEFAULT_BOOST);
-  channel::setPitch(ch, G_DEFAULT_PITCH);
-  channel::setPanning(ch, 0.5f);
-
-  panTool->refresh();
-  boostTool->refresh();
-
-  waveTools->waveform->stretchToWindow();
-  waveTools->updateWaveform();
-
-  sampleEditor::setBeginEnd(ch, 0, ch->wave->getSize());
-
-  redraw();
+       c::sampleEditor::reload(m_channelId, m_waveId);
+       redraw();
 }
 
 
@@ -327,8 +332,8 @@ void gdSampleEditor::cb_reload()
 
 void gdSampleEditor::cb_zoomIn()
 {
-  waveTools->waveform->setZoom(-1);
-  waveTools->redraw();
+       waveTools->waveform->setZoom(geWaveform::Zoom::IN);
+       waveTools->redraw();
 }
 
 
@@ -337,8 +342,8 @@ void gdSampleEditor::cb_zoomIn()
 
 void gdSampleEditor::cb_zoomOut()
 {
-  waveTools->waveform->setZoom(0);
-  waveTools->redraw();
+       waveTools->waveform->setZoom(geWaveform::Zoom::OUT);
+       waveTools->redraw();
 }
 
 
@@ -347,21 +352,34 @@ void gdSampleEditor::cb_zoomOut()
 
 void gdSampleEditor::cb_changeGrid()
 {
-  waveTools->waveform->setGridLevel(atoi(grid->text()));
+       waveTools->waveform->setGridLevel(atoi(grid->text()));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdSampleEditor::updateInfo()
+void gdSampleEditor::updateInfo(const m::Wave& w)
 {
-       string bitDepth = ch->wave->getBits() != 0 ? u::string::iToString(ch->wave->getBits()) : "(unknown)";
-       string infoText = 
-               "File: "  + ch->wave->getPath() + "\n"
-               "Size: " + u::string::iToString(ch->wave->getSize()) + " frames\n"
-               "Duration: " + u::string::iToString(ch->wave->getDuration()) + " seconds\n"
+       std::string bitDepth = w.getBits() != 0 ? u::string::iToString(w.getBits()) : "(unknown)";
+       std::string infoText = 
+               "File: "      + w.getPath() + "\n"
+               "Size: "      + u::string::iToString(w.getSize()) + " frames\n"
+               "Duration: "  + u::string::iToString(w.getDuration()) + " seconds\n"
                "Bit depth: " + bitDepth + "\n"
-               "Frequency: " + u::string::iToString(ch->wave->getRate()) + " Hz\n";
+               "Frequency: " + u::string::iToString(w.getRate()) + " Hz\n";
+
        info->copy_label(infoText.c_str());
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::setWaveId(ID id)
+{
+       m_waveId = id;
+       waveTools->waveId = id;
+       waveTools->waveform->setWaveId(id);
+}
+}} // giada::v::
index 6373bd580620b405fb790258a1f8617bd7d431bd..df7cf4250012ad963e9dd0e06d8340ac684df584 100644 (file)
 #define GD_EDITOR_H
 
 
-#include "../../core/sampleChannel.h"
+#include "core/types.h"
 #include "window.h"
 
 
-class SampleChannel;
 class geButton;
-class geWaveTools;
+class geChoice;
+class geCheck;
+class geBox;
+class geButton;
+class geStatusButton;
+
+
+namespace giada {
+namespace m
+{
+class SampleChannel;
+class Wave;
+}
+namespace v 
+{
 class geVolumeTool;
+class geWaveTools;
 class geBoostTool;
 class gePanTool;
 class gePitchTool;
 class geRangeTool;
-class geSampleTool;
 class geShiftTool;
-class geChoice;
-class geCheck;
-class geBox;
-class geButton;
-
 
 class gdSampleEditor : public gdWindow
 {
 friend class geWaveform;
 
+public:
+
+       gdSampleEditor(ID channelId, ID waveId);
+       ~gdSampleEditor();
+
+       void rebuild() override;
+       void refresh() override;
+
+       void updateInfo(const m::Wave& w);
+       void setWaveId(ID id);
+
+       geChoice* grid;
+       geCheck*  snap;
+       geBox*    sep1;
+       geButton* zoomIn;
+       geButton* zoomOut;
+       
+       geWaveTools* waveTools;
+
+       geVolumeTool* volumeTool;
+       gePanTool*    panTool;
+
+       gePitchTool* pitchTool;
+
+       geRangeTool* rangeTool;
+       geShiftTool* shiftTool;
+       geButton*    reload;
+
+       geStatusButton* play;
+       geButton*       rewind;
+       geCheck*        loop;
+       geBox*          info;
+
+
 private:
 
        Fl_Group* createUpperBar();
@@ -76,39 +118,11 @@ private:
        void cb_enableSnap();
        void cb_togglePreview();
        void cb_rewindPreview();
-
-public:
-
-       gdSampleEditor(giada::m::SampleChannel* ch);
-       ~gdSampleEditor();
-
-       void updateInfo();
-
-       geChoice* grid;
-       geCheck*  snap;
-       geBox*    sep1;
-       geButton* zoomIn;
-       geButton* zoomOut;
        
-       geWaveTools* waveTools;
-
-       geVolumeTool* volumeTool;
-       geBoostTool*  boostTool;
-       gePanTool*    panTool;
-
-       gePitchTool* pitchTool;
-
-       geRangeTool* rangeTool;
-       geShiftTool* shiftTool;
-       geButton*    reload;
-
-       geButton* play;
-       geButton* rewind;
-       geCheck* loop;
-       geBox* info;
-
-       giada::m::SampleChannel* ch;
+       ID m_channelId;
+       ID m_waveId;
 };
+}} // giada::v::
 
 
 #endif
index 204971ef80533e42d0b552692eb895401e311c11..77b5b9bc4c673b8e84587149224542a458152391 100644 (file)
 
 #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 "utils/gui.h"
+#include "core/const.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/box.h"
 #include "window.h"
 #include "warnings.h"
 
 
-using namespace giada;
-
-
-void gdAlert(const char *c)
+namespace giada {
+namespace v 
 {
-       Fl_Window *modal = new Fl_Window(
-                       (Fl::w() / 2) - 150,
-                       (Fl::h() / 2) - 47,
-                       300, 90, "Alert");
+void gdAlert(const char* c)
+{
+       gdWindow* modal = new gdWindow(
+               (Fl::w() / 2) - 150,
+               (Fl::h() / 2) - 47,
+               300, 90, "Alert");
        modal->set_modal();
        modal->begin();
-               geBox *box = new geBox(10, 10, 280, 40, c);
-               geButton *b = new geButton(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(G_GUI_FONT_SIZE_BASE);
-       b->callback(__cb_window_closer, (void *)modal);
+       b->callback(cb_window_closer, (void *)modal);
        b->shortcut(FL_Enter);
        u::gui::setFavicon(modal);
        modal->show();
 }
 
 
-int gdConfirmWin(const char *title, const char *msg)
+int gdConfirmWin(const char* title, const char* msg)
 {
-       Fl_Window *win = new Fl_Window(
+       gdWindow* win = new gdWindow(
                        (Fl::w() / 2) - 150,
                        (Fl::h() / 2) - 47,
                        300, 90, title);
        win->set_modal();
        win->begin();
                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");
+               geButtonok = new geButton(212, 62, 80, 20, "Ok");
+               geButtonko = new geButton(124, 62, 80, 20, "Cancel");
        win->end();
        ok->shortcut(FL_Enter);
        u::gui::setFavicon(win);
@@ -77,7 +77,7 @@ int gdConfirmWin(const char *title, const char *msg)
 
        int r = 0;
        while (true) {
-               Fl_Widget *o = Fl::readqueue();
+               Fl_Widgeto = Fl::readqueue();
                if (!o) Fl::wait();
                else if (o == ok) {r = 1; break;}
                else if (o == ko) {r = 0; break;}
@@ -86,3 +86,4 @@ int gdConfirmWin(const char *title, const char *msg)
        win->hide();
        return r;
 }
+}} // giada::v::
\ No newline at end of file
index 564db6f06095ac2b6942fd5db6db594db4829701..a0baa1b1dd2ea5f28c8f92fa134dea1b6e1def57 100644 (file)
 #define GD_WARNINGS_H
 
 
+namespace giada {
+namespace v 
+{
 void gdAlert(const char *c);
 int  gdConfirmWin(const char *title, const char *msg);
-
+}} // giada::v::
 
 #endif
index ca5c914590781d0a8d24332a4d6b40fa8e58bd43..f4913a1ff5d3e225eee8617e95b256f4b4ef68c1 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../utils/log.h"
+#include "utils/log.h"
 #include "window.h"
 
 
-void __cb_window_closer(Fl_Widget *v, void *p)
+namespace giada {
+namespace v 
+{
+void cb_window_closer(Fl_Widget* v, void* p)
 {
   delete (Fl_Window*) p;
 }
@@ -38,7 +41,7 @@ void __cb_window_closer(Fl_Widget *v, void *p)
 /* -------------------------------------------------------------------------- */
 
 
-gdWindow::gdWindow(int x, int y, int w, int h, const char *title, int id)
+gdWindow::gdWindow(int x, int y, int w, int h, const chartitle, int id)
        : Fl_Double_Window(x, y, w, h, title), id(id), parent(nullptr)
 {
 }
@@ -47,7 +50,7 @@ gdWindow::gdWindow(int x, int y, int w, int h, const char *title, int id)
 /* -------------------------------------------------------------------------- */
 
 
-gdWindow::gdWindow(int w, int h, const char *title, int id)
+gdWindow::gdWindow(int w, int h, const chartitle, int id)
        : Fl_Double_Window(w, h, title), id(id), parent(nullptr)
 {
 }
@@ -71,9 +74,9 @@ gdWindow::~gdWindow()
 /* 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)
+void gdWindow::cb_closeChild(Fl_Widget* v, void* p)
 {
-       gdWindow *child = (gdWindow*) v;
+       gdWindowchild = (gdWindow*) v;
        if (child->getParent() != nullptr)
                (child->getParent())->delSubWindow(child);
 }
@@ -82,12 +85,12 @@ void gdWindow::cb_closeChild(Fl_Widget *v, void *p)
 /* -------------------------------------------------------------------------- */
 
 
-void gdWindow::addSubWindow(gdWindow *w)
+void gdWindow::addSubWindow(gdWindoww)
 {
        /** 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());
+                       //u::log::print("[gdWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
                        delete w;
                        return;
                }
@@ -103,7 +106,7 @@ void gdWindow::addSubWindow(gdWindow *w)
 /* -------------------------------------------------------------------------- */
 
 
-void gdWindow::delSubWindow(gdWindow *w)
+void gdWindow::delSubWindow(gdWindoww)
 {
        for (unsigned i=0; i<subWindows.size(); i++)
                if (w->getId() == subWindows.at(i)->getId()) {
@@ -135,7 +138,7 @@ void gdWindow::delSubWindow(int id)
 /* -------------------------------------------------------------------------- */
 
 
-int gdWindow::getId()
+int gdWindow::getId() const
 {
        return id;
 }
@@ -150,25 +153,25 @@ void gdWindow::setId(int id)
 /* -------------------------------------------------------------------------- */
 
 
-void gdWindow::debug()
+void gdWindow::debug() const
 {
-       gu_log("---- window stack (id=%d): ----\n", getId());
+       u::log::print("---- 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");
+               u::log::print("[gdWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
+       u::log::print("----\n");
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-gdWindow *gdWindow::getParent()
+gdWindowgdWindow::getParent()
 {
        return parent;
 }
 
 
-void gdWindow::setParent(gdWindow *w)
+void gdWindow::setParent(gdWindoww)
 {
        parent = w;
 }
@@ -177,7 +180,7 @@ void gdWindow::setParent(gdWindow *w)
 /* -------------------------------------------------------------------------- */
 
 
-bool gdWindow::hasWindow(int id)
+bool gdWindow::hasWindow(int id) const
 {
        for (unsigned i=0; i<subWindows.size(); i++)
                if (id == subWindows.at(i)->getId())
@@ -189,10 +192,11 @@ bool gdWindow::hasWindow(int id)
 /* -------------------------------------------------------------------------- */
 
 
-gdWindow *gdWindow::getChild(int id)
+gdWindowgdWindow::getChild(int id)
 {
        for (unsigned i=0; i<subWindows.size(); i++)
                if (id == subWindows.at(i)->getId())
                        return subWindows.at(i);
        return nullptr;
 }
+}} // giada::v::
\ No newline at end of file
index 1b6044950805aacbac5d9dd0e9c440723bc6be0c..bf9da657a149fc854bc352627daa2cae4e10e172 100644 (file)
 #include <FL/Fl_Double_Window.H>
 
 
+namespace giada {
+namespace v 
+{
 /* cb_window_closer
- * callback for when closing windows. Deletes the widget (delete). */
-
-void __cb_window_closer(Fl_Widget* v, void* p);
+Callback for 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);
@@ -55,23 +51,37 @@ public:
 
        static void cb_closeChild(Fl_Widget* v, void* p);
 
+       /* rebuild, refresh
+       Rebuild() is called by the View Updater when something structural changes
+       (e.g. a new channel added). Refresh() is called periodically by the View 
+       Updater during the refresh loop. */
+       
+       virtual void rebuild() {};
+       virtual void refresh() {};
+
+       /* hasWindow
+       True if the window with id 'id' exists in the stack. */
+
+       bool hasWindow(int id) const;
+       
+       int  getId() const;
+       void debug() const;
+
        void addSubWindow(gdWindow* w);
        void delSubWindow(gdWindow* w);
        void delSubWindow(int id);
-
-       int  getId();
        void setId(int id);
-       void debug();
-
        void setParent(gdWindow* w);
        gdWindow* getParent();
        gdWindow* getChild(int id);
 
-       /* hasWindow
-        * true if the window with id 'id' exists in the stack. */
+protected:
 
-       bool hasWindow(int id);
+       std::vector<gdWindow*> subWindows;
+       int id;
+       gdWindow* parent;
 };
+}} // giada::v::
 
 
 #endif
index fae699259ce3e90e6b25a10c80586f7bdbaba36b..123bef5e4e318656095c2081a46876289302a72e 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl.H>
-#include "../core/init.h"
-#include "../core/const.h"
-#include "../core/mixer.h"
-#include "../core/channel.h"
-#include "../glue/transport.h"
-#include "../glue/io.h"
-#include "elems/mainWindow/keyboard/channel.h"
+#include "core/channels/channel.h"
+#include "core/init.h"
+#include "core/const.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/recManager.h"
+#include "core/conf.h"
+#include "glue/io.h"
+#include "glue/main.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
 #include "dispatcher.h"
 
 
+extern giada::v::gdMainWindow* G_MainWin;
+
+
 namespace giada {
 namespace v {
 namespace dispatcher
@@ -47,6 +56,8 @@ bool end_       = false;
 bool enter_     = false;
 bool space_     = false;
 bool esc_       = false;
+bool key_       = false;
+
 
 std::function<void()> signalCb_ = nullptr;
 
@@ -54,13 +65,13 @@ std::function<void()> signalCb_ = nullptr;
 /* -------------------------------------------------------------------------- */
 
 
-void perform_(m::Channel* ch, int event)
+void perform_(const geChannel* gch, int event)
 {
        if (event == FL_KEYDOWN)
-               c::io::keyPress(ch, Fl::event_ctrl(), Fl::event_shift(), G_MAX_VELOCITY);
+               c::io::keyPress(gch->channelId, Fl::event_ctrl(), Fl::event_shift(), G_MAX_VELOCITY);
        else
        if (event == FL_KEYUP)  
-               c::io::keyRelease(ch, Fl::event_ctrl(), Fl::event_shift());
+               c::io::keyRelease(gch->channelId, Fl::event_ctrl(), Fl::event_shift());
 }
 
 
@@ -68,13 +79,15 @@ void perform_(m::Channel* ch, int event)
 
 
 /* Walk channels array, trying to match button's bound key with the event. If 
-found, trigger the key-press function. */
+found, trigger the key-press/key-release function. */
 
 void dispatchChannels_(int event)
 {
-       for (m::Channel* ch : m::mixer::channels)
-               if (ch->guiChannel->handleKey(event))
-                       perform_(ch, event);
+       G_MainWin->keyboard->forEachChannel([=](geChannel* c)
+       {
+               if (c->handleKey(event))
+                       perform_(c, event);
+       });
 }
 
 
@@ -101,31 +114,32 @@ void dispatchKey(int event)
        /* These events come from the keyboard, not from a direct interaction on the 
        UI with the mouse/touch. So the 'gui' parameter is set to false. */
 
-       const bool gui = false;
-
        if (event == FL_KEYDOWN) {
                if (Fl::event_key() == FL_BackSpace && !backspace_) {
                        backspace_ = true;
-                       c::transport::rewindSeq(gui);
+                       m::mh::rewindSequencer();
                }
                else if (Fl::event_key() == FL_End && !end_) {
                        end_ = true;
-                       c::io::toggleInputRec(gui);
+                       c::main::toggleInputRec();
                }
                else if (Fl::event_key() == FL_Enter && !enter_) {
                        enter_ = true;
-                       c::io::toggleActionRec(gui);
+                       m::recManager::toggleActionRec(static_cast<RecTriggerMode>(m::conf::recTriggerMode));
                }
                else if (Fl::event_key() == ' ' && !space_) {
                        space_ = true;
-                       c::transport::startStopSeq(gui);
+                       m::mh::toggleSequencer();
                }
                else if (Fl::event_key() == FL_Escape && !esc_) {
                        esc_ = true;
                        m::init::closeMainWindow();
                }
-               else
+               else if (!key_) {
+                       key_ = true;
                        triggerSignalCb_();
+                       dispatchChannels_(event);
+               }
        }
        else if (event == FL_KEYUP) {
                if (Fl::event_key() == FL_BackSpace)
@@ -138,19 +152,21 @@ void dispatchKey(int event)
                        enter_ = false;
                else if (Fl::event_key() == FL_Escape)
                        esc_ = false;
+               else {
+                       key_ = false;
+                       dispatchChannels_(event);
+               }
        }
-
-       dispatchChannels_(event);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void dispatchTouch(m::Channel* ch, bool status)
+void dispatchTouch(const geChannel* gch, bool status)
 {
        triggerSignalCb_();
-       perform_(ch, status ? FL_KEYDOWN : FL_KEYUP);
+       perform_(gch, status ? FL_KEYDOWN : FL_KEYUP);
 }
 
 
index 580dc4e4d5d309064e98205ada1e0064593bb700..fc5b9d40a091dfbe7f4c432a7f59bfb5455ced14 100644 (file)
 
 
 namespace giada {
-namespace m 
+namespace v 
 {
-    class Channel;
-}
-namespace v {
+class geChannel;
+
 namespace dispatcher
 {
+/* dispatchKey
+Processes a key pressed on the physical keyboard. */
+
 void dispatchKey(int event);
-void dispatchTouch(m::Channel* ch, bool status);
+
+/* dispatchTouch
+Processes a mouse click/touch event. */
+
+void dispatchTouch(const geChannel* gch, bool status);
+
 void setSignalCallback(std::function<void()> f);
 }}} // giada::v::dispatcher
 
index d62523b751b6b7d1c326a125aa774c49197e9c42..11502df8020da350f77ff41e3114b4f2f6e1b5bc 100644 (file)
@@ -34,7 +34,7 @@ namespace giada {
 namespace v
 {
 geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable,
-       const m::Action* a1, const m::Action* a2)
+       m::Action a1, m::Action a2)
 : Fl_Box     (X, Y, W, H),
   m_resizable(resizable),
   onRightEdge(false),
index 1d77df43da64ea8f3e2da36de9ee98b9f966f4a8..accdd607bca131855866956e420eb0ab98e03367 100644 (file)
@@ -30,8 +30,8 @@
 
 
 #include <FL/Fl_Box.H>
-#include "../../../core/recorder.h"
-#include "../../../core/types.h"
+#include "core/recorder.h"
+#include "core/types.h"
 
 
 namespace giada {
@@ -43,17 +43,13 @@ namespace v
 {
 class geBaseAction : public Fl_Box
 {
-protected:
-       
-       bool m_resizable;
-
 public:
 
        static const Pixel MIN_WIDTH    = 12;
        static const Pixel HANDLE_WIDTH = 6;
 
        geBaseAction(Pixel x, Pixel y, Pixel w, Pixel h, bool resizable, 
-               const m::Action* a1, const m::Action* a2);
+               m::Action a1, m::Action a2);
 
        int handle(int e) override;
 
@@ -73,8 +69,12 @@ public:
        bool altered;
        Pixel pick;
 
-       const m::Action* a1;
-       const m::Action* a2;
+       m::Action a1;
+       m::Action a2;
+       
+protected:
+       
+       bool m_resizable;
 };
 }} // giada::v::
 
index c615ffc5c66863f7db16340dbfb3678a69544669..eb3e1ab50d3f7850b47bd35f2b6419ddb454d08d 100644 (file)
@@ -27,9 +27,9 @@
 
 #include <FL/Fl.H>
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
-#include "../../../core/clock.h"
-#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
 #include "gridTool.h"
 #include "baseAction.h"
 #include "baseActionEditor.h"
@@ -38,9 +38,8 @@
 namespace giada {
 namespace v
 {
-geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, m::Channel* ch)
+geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h)
 :      Fl_Group(x, y, w, h),
-       m_ch    (ch),
        m_base  (static_cast<gdBaseActionEditor*>(window())),
        m_action(nullptr)
 {
index 2db996c47c2834624220cba375066134c5954563..c77885fa8308d2c206e4b3f481e1ccd1018f0629 100644 (file)
@@ -41,20 +41,27 @@ class geBaseAction;
 
 class geBaseActionEditor : public Fl_Group
 {
-private:
-       
-       /* drawVerticals
-       Draws generic vertical lines (beats, bars, grid lines...). */
-       
-       void drawVerticals(int steps) const;
+public:
+
+       geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h);
+
+  /* updateActions
+  Rebuild the actions widgets from scratch. */
+  
+       virtual void rebuild() = 0;
+
+       /* handle
+       Override base FL_Group events. */
        
-       int push();
-       int drag();
-       int release();
+       int handle(int e) override;
 
-protected:
+       /* getActionAtCursor
+       Returns the action under the mouse. nullptr if nothing found. Why not using
+       Fl::belowmouse? It would require a boring dynamic_cast. */
+
+       geBaseAction* getActionAtCursor() const;
 
-       m::Channel* m_ch;
+protected:
 
        gdBaseActionEditor* m_base;
 
@@ -74,25 +81,16 @@ protected:
        virtual void onResizeAction()  = 0;
        virtual void onRefreshAction() = 0;
 
-public:
-
-       geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, m::Channel* ch);
-
-  /* updateActions
-  Rebuild the actions widgets from scratch. */
-  
-       virtual void rebuild() = 0;
-
-       /* handle
-       Override base FL_Group events. */
+private:
        
-       int handle(int e) override;
-
-       /* getActionAtCursor
-       Returns the action under the mouse. nullptr if nothing found. Why not using
-       Fl::belowmouse? It would require a boring dynamic_cast. */
-
-       geBaseAction* getActionAtCursor() const;        
+       /* drawVerticals
+       Draws generic vertical lines (beats, bars, grid lines...). */
+       
+       void drawVerticals(int steps) const;
+       
+       int push();
+       int drag();
+       int release();
 };
 }} // giada::v::
 
index 3f2e1314dc1aab32e482be3a30260d0ee9825af8..663302d7af2be98d11c56261f2cfe1d8d2387fe5 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl.H>
 #include <FL/fl_draw.H>
-#include "../../../utils/log.h"
-#include "../../../utils/math.h"
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/action.h"
-#include "../../../core/recorder.h"
-#include "../../../core/sampleChannel.h"
-#include "../../../glue/actionEditor.h"
-#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/action.h"
+#include "core/recorder.h"
+#include "glue/actionEditor.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
 #include "envelopePoint.h"
 #include "envelopeEditor.h"
 
 
-using std::vector;
-
-
 namespace giada {
 namespace v
 {
-geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l, 
-       m::SampleChannel* ch)
-:      geBaseActionEditor(x, y, 200, m::conf::envelopeEditorH, ch)
+geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l)
+:      geBaseActionEditor(x, y, 200, m::conf::envelopeEditorH)
 {
        copy_label(l);
 }
@@ -94,7 +91,7 @@ void geEnvelopeEditor::draw()
        for (int i=0; i<children(); i++) {
                geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
                if (m_action == nullptr)
-                       p->position(p->x(), valueToY(p->a1->event.getVelocity()));
+                       p->position(p->x(), valueToY(p->a1.event.getVelocity()));
                if (i > 0) {
                        x2 = p->x() + side;
                        y2 = p->y() + side;
@@ -122,10 +119,10 @@ void geEnvelopeEditor::rebuild()
        clear();
        size(m_base->fullWidth, h());
 
-       for (const m::Action* a : m_base->getActions()) {
-               if (a->event.getStatus() != m::MidiEvent::ENVELOPE)
+       for (const m::Action& a : m_base->getActions()) {
+               if (a.event.getStatus() != m::MidiEvent::ENVELOPE)
                        continue;
-               add(new geEnvelopePoint(frameToX(a->frame), valueToY(a->event.getVelocity()), a));              
+               add(new geEnvelopePoint(frameToX(a.frame), valueToY(a.event.getVelocity()), a));                
        }
 
        resizable(nullptr);
@@ -178,7 +175,7 @@ void geEnvelopeEditor::onAddAction()
        Frame f = m_base->pixelToFrame(Fl::event_x() - x());
        int   v = yToValue(Fl::event_y() - y());
        
-       c::actionEditor::recordEnvelopeAction(m_ch, f, v);
+       c::actionEditor::recordEnvelopeAction(m_base->channelId, f, v);
        
        m_base->rebuild();
 }
@@ -189,7 +186,7 @@ void geEnvelopeEditor::onAddAction()
 
 void geEnvelopeEditor::onDeleteAction()  
 {
-       c::actionEditor::deleteEnvelopeAction(m_ch, m_action->a1);
+       c::actionEditor::deleteEnvelopeAction(m_base->channelId, m_action->a1);
                
        m_base->rebuild();
 }
@@ -228,8 +225,8 @@ void geEnvelopeEditor::onRefreshAction()
 {
        Frame f = m_base->pixelToFrame((m_action->x() - x()) + geEnvelopePoint::SIDE / 2);
        float v = yToValue(m_action->y() - y(), geEnvelopePoint::SIDE);
-       c::actionEditor::updateEnvelopeAction(m_ch, m_action->a1, f, v);
+       c::actionEditor::updateEnvelopeAction(m_base->channelId, m_action->a1, f, v);
 
        m_base->rebuild();
 }
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 192a4a1c3ca6f4ee3c8e631b04ffbde77cd9f204..ea0c4ecb86b4a95cf4243c492cf7d257ec84baec 100644 (file)
@@ -32,8 +32,6 @@
 #include "baseActionEditor.h"
 
 
-
-
 namespace giada {
 namespace m
 {
@@ -46,6 +44,15 @@ class geEnvelopePoint;
 
 class geEnvelopeEditor : public geBaseActionEditor
 {
+public:
+
+       geEnvelopeEditor(Pixel x, Pixel y, const char* l);
+       ~geEnvelopeEditor();
+
+       void draw() override;
+
+       void rebuild() override;
+
 private:
 
        void onAddAction()     override;
@@ -60,15 +67,6 @@ private:
 
        bool isFirstPoint() const;
        bool isLastPoint()  const;
-
-public:
-
-       geEnvelopeEditor(Pixel x, Pixel y, const char* l, m::SampleChannel* ch);
-       ~geEnvelopeEditor();
-
-       void draw() override;
-
-       void rebuild() override;
 };
 }} // giada::v::
 
index 68e3ceca4dc4ce398896855ec5cb4a4621bbaf31..2ef7e07e806624772456248723b84089bbf5ac79 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
+#include "core/const.h"
 #include "envelopePoint.h"
 
 
 namespace giada {
 namespace v
 {
-geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, const m::Action* a)
-       : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, nullptr)
+geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, m::Action a)
+       : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, {})
 {
 }
 
index 3724fa72f794f30289e7969e9cd592006b7259ad..8f788964d0322cc7a2d48a84072aad064368c218 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_ENVELOPE_POINT_H
 
 
-#include "../../../core/recorder.h"
+#include "core/recorder.h"
 #include "baseAction.h"
 
 
@@ -42,7 +42,7 @@ public:
 
        static const Pixel SIDE = 12;
 
-       geEnvelopePoint(Pixel x, Pixel y, const m::Action* a);
+       geEnvelopePoint(Pixel x, Pixel y, m::Action a);
 
        void draw() override;
 };
index 4c4b021e681037e8ecfedafd29f8e4068a1ab1a5..3b3b52b451783f98422747f151a8ade945b695a4 100644 (file)
 
 
 #include <FL/Fl_Double_Window.H>
-#include "../../../core/conf.h"
-#include "../../../core/clock.h"
-#include "../../../utils/math.h"
-#include "../basics/choice.h"
-#include "../basics/check.h"
+#include "core/conf.h"
+#include "core/clock.h"
+#include "utils/math.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/check.h"
 #include "gridTool.h"
 
 
@@ -52,7 +52,7 @@ geGridTool::geGridTool(Pixel x, Pixel y)
        gridType->value(0);
        gridType->callback(cb_changeType, (void*)this);
 
-       active = new geCheck(gridType->x() + gridType->w() + 4, y+4, 12, 12);
+       active = new geCheck(gridType->x() + gridType->w() + 4, y, 20, 20);
 
        gridType->value(m::conf::actionEditorGridVal);
        active->value(m::conf::actionEditorGridOn);
index 90690c7760d8b2110c20249761baafc5e01e498b..24102c25dbef81f0f1bf822a6837eec5b1d1c218 100644 (file)
@@ -30,7 +30,7 @@
 
 
 #include <FL/Fl_Group.H>
-#include "../../../core/types.h"
+#include "core/types.h"
 
 
 class geChoice;
@@ -42,14 +42,6 @@ namespace v
 {
 class geGridTool : public Fl_Group
 {
-private:
-
-  geChoice* gridType;
-       geCheck*  active;
-
-       static void cb_changeType(Fl_Widget* w, void* p);
-       inline void cb_changeType();
-
 public:
 
        geGridTool(Pixel x, Pixel y);
@@ -65,6 +57,13 @@ public:
 
        Frame getCellSize() const;
 
+private:
+
+       geChoice* gridType;
+       geCheck*  active;
+
+       static void cb_changeType(Fl_Widget* w, void* p);
+       void cb_changeType();
 };
 }} // giada::v::
 
index 6f4b44f5dd789a523d338cf2b05fc0335d697e27..0e283bf5a7b4abb6de25e87f8175cbd5412e499f 100644 (file)
 
 
 #include <FL/Fl.H>
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/midiChannel.h"
-#include "../../dialogs/actionEditor/midiActionEditor.h"
+#include "core/channels/midiChannel.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "gui/dialogs/actionEditor/midiActionEditor.h"
 #include "pianoRoll.h"
 #include "noteEditor.h"
 
@@ -39,9 +39,9 @@ namespace v
 {
 geNoteEditor::geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base)
 : geScroll(x, y, 200, 422),
-       m_base  (base)
+  m_base  (base)
 {
-       pianoRoll = new gePianoRoll(x, y, m_base->fullWidth, static_cast<m::MidiChannel*>(m_base->ch));
+       pianoRoll = new gePianoRoll(x, y, m_base->fullWidth);
        
        size(m_base->fullWidth, m::conf::pianoRollH);
        
index ffe57c806ec8ab760f114e7f14b1b4a02c81a093..9184c00c2a44f2da5e584ad388704100b6f4c036 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_NOTE_EDITOR_H
 
 
-#include "../basics/scroll.h"
+#include "gui/elems/basics/scroll.h"
 
 
 namespace giada {
@@ -41,10 +41,6 @@ class gePianoRoll;
 
 class geNoteEditor : public geScroll
 {
-private:
-
-       gdMidiActionEditor* m_base;
-
 public:
 
        geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base);
@@ -54,6 +50,10 @@ public:
        void scroll();
 
        gePianoRoll* pianoRoll;
+
+private:
+
+    gdMidiActionEditor* m_base;
 };
 }} // giada::v::
 
index 361e422ce2a68c1d20152086a40167211fb33290..6bd64710cb0abe3d05e4c2993b788259a72dc845 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
-#include "../../../core/action.h"
-#include "../../../core/midiEvent.h"
-#include "../../../utils/math.h"
+#include "core/const.h"
+#include "core/action.h"
+#include "core/midiEvent.h"
+#include "utils/math.h"
 #include "pianoItem.h"
 
 
 namespace giada {
 namespace v
 {
-gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, const m::Action* a1,
-       const m::Action* a2)
+gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, m::Action a1,
+       m::Action a2)
 : geBaseAction(X, Y, W, H, /*resizable=*/true, a1, a2),
-  m_ringLoop  (a2 != nullptr && a1->frame > a2->frame),
-  m_orphaned  (a2 == nullptr)
+  m_ringLoop  (a2.isValid() && a1.frame > a2.frame),
+  m_orphaned  (!a2.isValid())
 {
        m_resizable = isResizable();
 }
@@ -89,7 +89,7 @@ void gePianoItem::draw()
 
 Pixel gePianoItem::calcVelocityH() const
 {
-       int v = a1->event.getVelocity();
+       int v = a1.event.getVelocity();
        return u::math::map<int, Pixel>(v, 0, G_MAX_VELOCITY, 0, h() - 3);
 }
 }} // giada::v::
\ No newline at end of file
index dd9d3f6fed378536ac69d912e22074ccc80d338f..01dc5bf66752c4383cefb786d244828159147ec2 100644 (file)
@@ -41,23 +41,22 @@ namespace v
 {
 class gdActionEditor;
 
-
 class gePianoItem : public geBaseAction
 {
-private:
-
-       bool m_ringLoop;
-       bool m_orphaned;
-
-       Pixel calcVelocityH() const;
-
 public:
 
-       gePianoItem(int x, int y, int w, int h, const m::Action* a1, const m::Action* a2);
+       gePianoItem(int x, int y, int w, int h, m::Action a1, m::Action a2);
  
        void draw() override;
 
        bool isResizable() const;
+
+private:
+
+    bool m_ringLoop;
+    bool m_orphaned;
+
+    Pixel calcVelocityH() const;
 };
 }} // giada::v::
 
index 5bb6b316a78ba65a12bd112cf5c0cb3751b05050..0f3471df14ae9b3b04d400584e339c0e6ec72305 100644 (file)
 
 #include <cassert>
 #include <FL/Fl.H>
-#include "../../../core/conf.h"
-#include "../../../core/const.h"
-#include "../../../core/clock.h"
-#include "../../../core/action.h"
-#include "../../../core/midiEvent.h"
-#include "../../../core/midiChannel.h"
-#include "../../../utils/log.h"
-#include "../../../utils/string.h"
-#include "../../../utils/math.h"
-#include "../../../glue/actionEditor.h"
-#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "core/channels/midiChannel.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "core/action.h"
+#include "core/midiEvent.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include "utils/math.h"
+#include "glue/actionEditor.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
 #include "pianoItem.h"
 #include "noteEditor.h"
 #include "pianoRoll.h"
 
 
-using std::string;
-using std::vector;
-
-
 namespace giada {
 namespace v
 {
-gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, m::MidiChannel* ch)
-       : geBaseActionEditor(X, Y, W, 40, ch),
+gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W)
+       : geBaseActionEditor(X, Y, W, 40),
          pick              (0)
 {
        position(x(), m::conf::pianoRollY == -1 ? y()-(h()/2) : m::conf::pianoRollY);
@@ -80,7 +76,7 @@ void gePianoRoll::drawSurface1()
 
                /* print key note label. C C# D D# E F F# G G# A A# B */
 
-               string note = u::string::iToString(octave);
+               std::string note = u::string::iToString(octave);
                switch (i % KEYS) {
                        case (int) Notes::G:
                                fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
@@ -220,8 +216,9 @@ void gePianoRoll::onAddAction()
 {
        Frame frame = m_base->pixelToFrame(Fl::event_x() - x());
        int   note  = yToNote(Fl::event_y() - y());
-       c::actionEditor::recordMidiAction(static_cast<m::MidiChannel*>(m_ch), note, G_MAX_VELOCITY, frame);
-       
+       c::actionEditor::recordMidiAction(m_base->channelId, note, G_MAX_VELOCITY, 
+               frame);
+
        m_base->rebuild();  // Rebuild velocityEditor as well
 }
 
@@ -231,7 +228,7 @@ void gePianoRoll::onAddAction()
 
 void gePianoRoll::onDeleteAction()
 {
-       c::actionEditor::deleteMidiAction(static_cast<m::MidiChannel*>(m_ch), m_action->a1);    
+       c::actionEditor::deleteMidiAction(m_base->channelId, m_action->a1);     
        
        m_base->rebuild();  // Rebuild velocityEditor as well
 }
@@ -301,12 +298,12 @@ void gePianoRoll::onRefreshAction()
        }       
        else if (m_action->onLeftEdge) {
                f1 = m_base->pixelToFrame(p1);
-               f2 = m_action->a2->frame;
+               f2 = m_action->a2.frame;
                if (f1 == f2) // If snapping makes an action fall onto the other
                        f1 -= G_DEFAULT_ACTION_SIZE;
        }
        else if (m_action->onRightEdge) {
-               f1 = m_action->a1->frame;
+               f1 = m_action->a1.frame;
                f2 = m_base->pixelToFrame(p2);
                if (f1 == f2) // If snapping makes an action fall onto the other
                        f2 += G_DEFAULT_ACTION_SIZE;
@@ -315,10 +312,9 @@ void gePianoRoll::onRefreshAction()
        assert(f2 != 0);
 
        int note     = yToNote(m_action->y() - y());
-       int velocity = m_action->a1->event.getVelocity();
+       int velocity = m_action->a1.event.getVelocity();
 
-       ca::updateMidiAction(static_cast<m::MidiChannel*>(m_ch), m_action->a1, note, 
-               velocity, f1, f2);
+       ca::updateMidiAction(m_base->channelId, m_action->a1, note, velocity, f1, f2);
 
        m_base->rebuild();  // Rebuild velocityEditor as well
 }
@@ -345,12 +341,12 @@ Pixel gePianoRoll::snapToY(Pixel p) const
 }
 
 
-Pixel gePianoRoll::getPianoItemW(Pixel px, const m::Action* a1, const m::Action* a2) const
+Pixel gePianoRoll::getPianoItemW(Pixel px, const m::Action& a1, const m::Action& a2) const
 {
-       if (a2 != nullptr) {            // Regular
-               if (a1->frame > a2->frame)  // Ring-loop
+       if (a2.isValid()) {             // Regular
+               if (a1.frame > a2.frame)    // Ring-loop
                        return m_base->loopWidth - (px - x());
-               return m_base->frameToPixel(a2->frame - a1->frame);
+               return m_base->frameToPixel(a2.frame - a1.frame);
        }
        return geBaseAction::MIN_WIDTH; // Orphaned
 }
@@ -369,18 +365,17 @@ void gePianoRoll::rebuild()
        clear();
        size(m_base->fullWidth, (MAX_KEYS + 1) * CELL_H);
 
-       for (const m::Action* action : m_base->getActions())
+       for (const m::Action& a1 : m_base->getActions())
        {
-               if (action->event.getStatus() == m::MidiEvent::NOTE_OFF)
+               if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
                        continue;
 
-               const m::Action* a1 = action;
-               const m::Action* a2 = action->next;
+               assert(a1.isValid());  // a2 might be null if orphaned
 
-               assert(a1 != nullptr);  // a2 might be null if orphaned
+               const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{};
 
-               Pixel px = x() + m_base->frameToPixel(a1->frame);
-               Pixel py = y() + noteToY(a1->event.getNote());
+               Pixel px = x() + m_base->frameToPixel(a1.frame);
+               Pixel py = y() + noteToY(a1.event.getNote());
                Pixel ph = CELL_H;
                Pixel pw = getPianoItemW(px, a1, a2);
 
@@ -392,4 +387,4 @@ void gePianoRoll::rebuild()
 
        redraw();
 }
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index aecd56edbfe86279bea6b0c9c15716c9b14d2bb0..b5e9ab81e79e44194e3422131908eda09fb4bf11 100644 (file)
@@ -33,8 +33,6 @@
 #include "baseActionEditor.h"
 
 
-
-
 namespace giada {
 namespace m
 {
@@ -44,6 +42,23 @@ namespace v
 {
 class gePianoRoll : public geBaseActionEditor
 {
+public:
+
+       static const int MAX_KEYS    = 127;
+       static const int MAX_OCTAVES = 9;
+       static const int KEYS        = 12;
+       static const Pixel CELL_H    = 20;
+       static const Pixel CELL_W    = 40;
+
+       gePianoRoll(Pixel x, Pixel y, Pixel w);
+
+       void draw() override;
+       int  handle(int e) override;
+
+       void rebuild() override;
+
+       Pixel pick;
+
 private:
 
        enum class Notes
@@ -75,24 +90,7 @@ private:
        Pixel snapToY(Pixel p) const;
        int   yToNote(Pixel y) const;
        Pixel noteToY(int n) const;
-       Pixel getPianoItemW(Pixel x, const m::Action* a1, const m::Action* a2) const;
-
-public:
-
-       static const int MAX_KEYS    = 127;
-       static const int MAX_OCTAVES = 9;
-       static const int KEYS        = 12;
-       static const Pixel CELL_H    = 20;
-       static const Pixel CELL_W    = 40;
-
-       gePianoRoll(Pixel x, Pixel y, Pixel w, m::MidiChannel* ch);
-
-       void draw() override;
-       int  handle(int e) override;
-
-       void rebuild() override;
-
-       Pixel pick;
+       Pixel getPianoItemW(Pixel x, const m::Action& a1, const m::Action& a2) const;
 };
 }} // giada::v::
 
index a488736bd390adcb48016db45c923a9088ebcea0..3df6f69485af9913b310a7a40da8c9710bbc29a8 100644 (file)
@@ -26,9 +26,9 @@
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
-#include "../../../core/action.h"
-#include "../../../core/sampleChannel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/action.h"
 #include "sampleAction.h"
 
 
@@ -36,9 +36,9 @@ namespace giada {
 namespace v
 {
 geSampleAction::geSampleAction(Pixel X, Pixel Y, Pixel W, Pixel H, 
-       const m::SampleChannel* ch, const m::Action* a1, const m::Action* a2)
-: geBaseAction(X, Y, W, H, ch->mode == ChannelMode::SINGLE_PRESS, a1, a2),
-  m_ch        (ch)
+       bool singlePress, m::Action a1, m::Action a2)
+: geBaseAction (X, Y, W, H, singlePress, a1, a2),
+  m_singlePress(singlePress)
 {
 }
 
@@ -50,20 +50,20 @@ void geSampleAction::draw()
 {
        Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1; 
 
-       if (m_ch->mode == ChannelMode::SINGLE_PRESS) {
+       if (m_singlePress) {
                fl_rectf(x(), y(), w(), h(), color);
        }
        else {
-               if (a1->event.getStatus() == m::MidiEvent::NOTE_KILL)
+               if (a1.event.getStatus() == m::MidiEvent::NOTE_KILL)
                        fl_rect(x(), y(), MIN_WIDTH, h(), color);
                else {
                        fl_rectf(x(), y(), MIN_WIDTH, h(), color);
-                       if (a1->event.getStatus() == m::MidiEvent::NOTE_ON)
+                       if (a1.event.getStatus() == m::MidiEvent::NOTE_ON)
                                fl_rectf(x()+3, y()+h()-11, w()-6, 8, G_COLOR_GREY_4);
                        else
-                       if (a1->event.getStatus() == m::MidiEvent::NOTE_OFF)
+                       if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
                                fl_rectf(x()+3, y()+3, w()-6, 8, G_COLOR_GREY_4);
                }
        }
 }
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 6b4fc0a76b1c2ddc81da4f49b78c5b60359d9e9e..1d4d2e29dff9a909f260175f5e96e00848107ea6 100644 (file)
 #define GE_SAMPLE_ACTION_H
 
 
-#include "../../../core/recorder.h"
+#include "core/recorder.h"
 #include "baseAction.h"
 
 
-
-
 namespace giada {
 namespace m
 {
 class SampleChannel;
-struct Action;
 }
 namespace v
 {
 class geSampleAction : public geBaseAction
 {
-private:
-
-       const m::SampleChannel* m_ch;
-
 public:
 
-       geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, const m::SampleChannel* ch,
-               const m::Action* a1, const m::Action* a2);
+       geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, bool singlePress, 
+               m::Action a1, m::Action a2);
 
        void draw() override;
+
+private:
+
+       bool m_singlePress;
 };
 }} // giada::v::
 
index 31f73192fe4391e89cb559b8951808811221797b..3e2ed5f7d9b3c2ad02f85a5ff47378316806361a 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl.H>
 #include <FL/fl_draw.H>
-#include "../../../core/recorder.h"
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/action.h"
-#include "../../../core/sampleChannel.h"
-#include "../../../utils/log.h"
-#include "../../../glue/actionEditor.h"
-#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/recorder.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/action.h"
+#include "utils/log.h"
+#include "glue/actionEditor.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
 #include "sampleAction.h"
 #include "sampleActionEditor.h"
 
 
-using std::vector;
-
-
 namespace giada {
 namespace v
 {
-geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, m::SampleChannel* ch)
-: geBaseActionEditor(x, y, 200, m::conf::sampleActionEditorH, ch)
+geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y)
+: geBaseActionEditor(x, y, 200, m::conf::sampleActionEditorH)
 {
 }
 
@@ -68,29 +67,37 @@ void geSampleActionEditor::rebuild()
        namespace mr = m::recorder;
        namespace ca = c::actionEditor;
 
-       const m::SampleChannel* ch = static_cast<const m::SampleChannel*>(m_ch);
+       bool isSinglePressMode;
+       bool isAnyLoopMode;
+       
+       m::model::onGet(m::model::channels, m_base->channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               isSinglePressMode = sc.mode == ChannelMode::SINGLE_PRESS;
+               isAnyLoopMode     = sc.isAnyLoopMode();
+       });
 
        /* Remove all existing actions and set a new width, according to the current
        zoom level. */
 
        clear();
        size(m_base->fullWidth, h());
+       
+       for (const m::Action& a1 : m_base->getActions()) {
 
-       for (const m::Action* a1 : m_base->getActions()) {
-
-               if (a1->event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1))
+               if (a1.event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1))
                        continue;
 
-               const m::Action* a2 = a1->next;
+               m::Action a2 = a1.next != nullptr ? *a1.next : m::Action{};
 
-               Pixel px = x() + m_base->frameToPixel(a1->frame);
+               Pixel px = x() + m_base->frameToPixel(a1.frame);
                Pixel py = y() + 4;
                Pixel pw = 0;
                Pixel ph = h() - 8;
-               if (a2 != nullptr && ch->mode == ChannelMode::SINGLE_PRESS)
-                       pw = m_base->frameToPixel(a2->frame - a1->frame);
+               if (a2.isValid() && isSinglePressMode)
+                       pw = m_base->frameToPixel(a2.frame - a1.frame);
 
-               geSampleAction* gsa = new geSampleAction(px, py, pw, ph, ch, a1, a2);
+               geSampleAction* gsa = new geSampleAction(px, py, pw, ph, isSinglePressMode, a1, a2);
                add(gsa);
                resizable(gsa);
        }
@@ -98,7 +105,7 @@ void geSampleActionEditor::rebuild()
        /* If channel is LOOP_ANY, deactivate it: a loop mode channel cannot hold 
        keypress/keyrelease actions. */
        
-       ch->isAnyLoopMode() ? deactivate() : activate();
+       isAnyLoopMode ? deactivate() : activate();
 
        redraw();
 }
@@ -133,8 +140,7 @@ void geSampleActionEditor::draw()
 void geSampleActionEditor::onAddAction()     
 {
        Frame f = m_base->pixelToFrame(Fl::event_x() - x());
-       c::actionEditor::recordSampleAction(static_cast<m::SampleChannel*>(m_ch), 
-               m_base->getActionType(), f);
+       c::actionEditor::recordSampleAction(m_base->channelId, m_base->getActionType(), f);
        
        m_base->rebuild();
 }
@@ -145,7 +151,7 @@ void geSampleActionEditor::onAddAction()
 
 void geSampleActionEditor::onDeleteAction()  
 {
-       c::actionEditor::deleteSampleAction(static_cast<m::SampleChannel*>(m_ch), m_action->a1);
+       c::actionEditor::deleteSampleAction(m_base->channelId, m_action->a1);
        
        m_base->rebuild();
 }
@@ -193,13 +199,11 @@ void geSampleActionEditor::onRefreshAction()
 {
        namespace ca = c::actionEditor;
 
-       m::SampleChannel* ch = static_cast<m::SampleChannel*>(m_ch);
-
        Pixel p1   = m_action->x() - x();
        Pixel p2   = m_action->x() + m_action->w() - x();
        Frame f1   = 0;
        Frame f2   = 0;
-       int   type = m_action->a1->event.getStatus();
+       int   type = m_action->a1.event.getStatus();
 
        if (!m_action->isOnEdges()) {
                f1 = m_base->pixelToFrame(p1);
@@ -207,14 +211,14 @@ void geSampleActionEditor::onRefreshAction()
        }       
        else if (m_action->onLeftEdge) {
                f1 = m_base->pixelToFrame(p1);
-               f2 = m_action->a2->frame;
+               f2 = m_action->a2.frame;
        }
        else if (m_action->onRightEdge) {
-               f1 = m_action->a1->frame;
+               f1 = m_action->a1.frame;
                f2 = m_base->pixelToFrame(p2);
        }
 
-       ca::updateSampleAction(ch, m_action->a1, type, f1, f2);
+       ca::updateSampleAction(m_base->channelId, m_action->a1, type, f1, f2);
                        
        m_base->rebuild();
 }
@@ -223,10 +227,14 @@ void geSampleActionEditor::onRefreshAction()
 /* -------------------------------------------------------------------------- */
 
 
-bool geSampleActionEditor::isNoteOffSinglePress(const m::Action* a)
+bool geSampleActionEditor::isNoteOffSinglePress(const m::Action& a)
 {
-       const m::SampleChannel* ch = static_cast<const m::SampleChannel*>(m_ch);
-       return ch->mode == ChannelMode::SINGLE_PRESS && a->event.getStatus() == m::MidiEvent::NOTE_OFF;
+       bool res;
+       m::model::onGet(m::model::channels, m_base->channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               res = sc.mode == ChannelMode::SINGLE_PRESS && a.event.getStatus() == m::MidiEvent::NOTE_OFF;
+       });
+       return res;
 }
-
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 59e5ea31a74eba13a32374dad84664e91c638f5f..40c424e39da0fc2f02524c75b742e8d52f33732f 100644 (file)
@@ -44,6 +44,15 @@ class geSampleAction;
 
 class geSampleActionEditor : public geBaseActionEditor
 {
+public:
+
+       geSampleActionEditor(Pixel x, Pixel y);
+       ~geSampleActionEditor();
+
+       void draw() override;
+
+       void rebuild() override;
+
 private:
 
        void onAddAction()     override;
@@ -52,16 +61,7 @@ private:
        void onResizeAction()  override;
        void onRefreshAction() override;
 
-    bool isNoteOffSinglePress(const m::Action* a);
-
-public:
-
-       geSampleActionEditor(Pixel x, Pixel y, m::SampleChannel* ch);
-       ~geSampleActionEditor();
-
-       void draw() override;
-
-       void rebuild() override;
+    bool isNoteOffSinglePress(const m::Action& a);
 };
 }} // giada::v::
 
index 030af9a619b5965657561326821fbd127eb73451..28b6cfdbdbfd304d3af6f9d23e67ad0fe2f2bfbd 100644 (file)
 #include <cassert>
 #include <FL/Fl.H>
 #include <FL/fl_draw.H>
-#include "../../../utils/log.h"
-#include "../../../utils/math.h"
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/action.h"
-#include "../../../core/clock.h"
-#include "../../../core/midiChannel.h"
-#include "../../../glue/actionEditor.h"
-#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "core/channels/midiChannel.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/action.h"
+#include "core/clock.h"
+#include "glue/actionEditor.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
 #include "envelopePoint.h"
 #include "velocityEditor.h"
 
 
-using std::vector;
-
-
 namespace giada {
 namespace v
 {
-geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, m::MidiChannel* ch)
-:      geBaseActionEditor(x, y, 200, m::conf::velocityEditorH, ch)
+geVelocityEditor::geVelocityEditor(Pixel x, Pixel y)
+:      geBaseActionEditor(x, y, 200, m::conf::velocityEditorH)
 {
 }
 
@@ -83,7 +80,7 @@ void geVelocityEditor::draw()
        for (int i=0; i<children(); i++) {
                geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
                if (m_action == nullptr)
-                       p->position(p->x(), valueToY(p->a1->event.getVelocity()));
+                       p->position(p->x(), valueToY(p->a1.event.getVelocity()));
                Pixel x1 = p->x() + side;
                Pixel y1 = p->y();
                Pixel y2 = y() + h();
@@ -123,15 +120,13 @@ void geVelocityEditor::rebuild()
        clear();
        size(m_base->fullWidth, h());
 
-       for (const m::Action* action : m_base->getActions())
+       for (const m::Action& action : m_base->getActions())
        {
-               if (action->event.getStatus() == m::MidiEvent::NOTE_OFF)
+               if (action.event.getStatus() == m::MidiEvent::NOTE_OFF)
                        continue;
-               
-               //gu_log("[geVelocityEditor::rebuild] f=%d\n", action->frame);
 
-               Pixel px = x() + m_base->frameToPixel(action->frame);
-               Pixel py = y() + valueToY(action->event.getVelocity());
+               Pixel px = x() + m_base->frameToPixel(action.frame);
+               Pixel py = y() + valueToY(action.event.getVelocity());
 
                add(new geEnvelopePoint(px, py, action));
        }
@@ -163,9 +158,8 @@ void geVelocityEditor::onMoveAction()
 
 void geVelocityEditor::onRefreshAction() 
 {
-       c::actionEditor::updateVelocity(static_cast<m::MidiChannel*>(m_ch), m_action->a1, 
-               yToValue(m_action->y() - y()));
+       c::actionEditor::updateVelocity(m_action->a1, yToValue(m_action->y() - y()));
 
        m_base->rebuild();  // Rebuild pianoRoll as well
 }
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 456ae58ba5d4398cdb5f520ffdc8eb5bbdb510d7..b504622e9e0d61eaf1eff9a43dd851f716f4d59f 100644 (file)
@@ -32,8 +32,6 @@
 #include "baseActionEditor.h"
 
 
-
-
 namespace giada {
 namespace m
 {
@@ -46,6 +44,15 @@ class geEnvelopePoint;
 
 class geVelocityEditor : public geBaseActionEditor
 {
+public:
+
+       geVelocityEditor(Pixel x, Pixel y);
+       ~geVelocityEditor();
+
+       void draw() override;
+
+       void rebuild() override;
+
 private:
 
        void onMoveAction()    override;
@@ -56,15 +63,6 @@ private:
 
        Pixel valueToY(int v)   const;
        int   yToValue(Pixel y) const;
-
-public:
-
-       geVelocityEditor(Pixel x, Pixel y, m::MidiChannel* ch);
-       ~geVelocityEditor();
-
-       void draw() override;
-
-       void rebuild() override;
 };
 }} // giada::v::
 
index b4ed84f7d185ba1a354492ab201f45673df422d3..21904a9d6b6794ddb98f7beb75da2c28c2e6af5b 100644 (file)
 #include "baseButton.h"
 
 
-geBaseButton::geBaseButton(int x, int y, int w, int h, const char *l)
-  : Fl_Button(x, y, w, h, l)
+geBaseButton::geBaseButton(int x, int y, int w, int h, const charl)
+: Fl_Button(x, y, w, h, l)
 {
-  initLabel = l ? l : "";
+       initLabel = l != nullptr ? l : "";
 }
 
 
@@ -44,47 +44,47 @@ geBaseButton::geBaseButton(int x, int y, int w, int h, const char *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());
+       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)
+void geBaseButton::label(const charl)
 {
-  Fl_Button::label(l);
-  initLabel = l;
-  trimLabel();
+       Fl_Button::label(l);
+       initLabel = l;
+       trimLabel();
 }
 
 
-const char *geBaseButton::label()
+const chargeBaseButton::label()
 {
-  return Fl_Button::label();
+       return Fl_Button::label();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geBaseButton::resize(int X, int Y, int W, int H)
+void geBaseButton::draw()
 {
-  trimLabel();
-  Fl_Button::resize(X, Y, W, H);
+       trimLabel();
+       Fl_Button::draw();
 }
index cb8e1dd42eac63c5b9a9f05493666ef033360228..d3948f8b6d9664f2b43c1a3b6c40b33c1761a7f8 100644 (file)
 
 class geBaseButton : public Fl_Button
 {
-private:
+public:
 
-       std::string initLabel;
+       geBaseButton(int x, int y, int w, int h, const char* l=nullptr);
 
-       void trimLabel();
+       void draw() override;
+       void label(const char* l);
+       const char* label();
 
-public:
+private:
 
-  geBaseButton(int x, int y, int w, int h, const char *l=0);
+       std::string initLabel;
 
-  void resize(int x, int y, int w, int h) override;
-  void label(const char *l);
-       const char *label();
+       void trimLabel();
 };
 
 
index b113b6c055597e914ec3f4230ad737e8e0c94570..0b4b8e5751f76e22b5b44e6a62d4809c95c77c7d 100644 (file)
@@ -2,9 +2,6 @@
  *
  * Giada - Your Hardcore Loopmachine
  *
- * geButton
- * A regular button.
- *
  * -----------------------------------------------------------------------------
  *
  * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.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),
+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    (G_COLOR_GREY_2),
@@ -51,25 +48,27 @@ geButton::geButton(int x, int y, int w, int h, const char *L,
 
 void geButton::draw()
 {
-  if (!active()) txtColor = bdColor;
-  else           txtColor = G_COLOR_LIGHT_2;
+       geBaseButton::draw();
+       
+       if (!active()) txtColor = bdColor;
+       else           txtColor = G_COLOR_LIGHT_2;
 
-  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_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, G_GUI_FONT_SIZE_BASE);
-  fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
+       fl_color(txtColor);
+       fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+       fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
 }
index 4f51e3a28b47a9c0de868c20298519e9abde90d3..b749cca41b88b85548faaeec6655a27b41d48d3f 100644 (file)
@@ -39,13 +39,13 @@ 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);
+       geButton(int x, int y, int w, int h, const char *l=nullptr,
+               const char** imgOff=nullptr, const char** imgOn=nullptr);
 
-  void draw() override;
+       void draw() override;
 
-  const char **imgOff;
-       const char **imgOn;
+       const char** imgOff;
+       const char** imgOn;
        Fl_Color bgColor0;   // background not clicked
        Fl_Color bgColor1;   // background clicked
        Fl_Color bdColor;    // border
index aa2a0beb95c46f487834560bdc65f9492944763a..462f146273de4f9f3c632f5b0c7ae3e08e9b3774 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
+#include "core/const.h"
 #include "check.h"
 
 
-geCheck::geCheck(int x, int y, int w, int h, const char *l)
+geCheck::geCheck(int x, int y, int w, int h, const charl)
 : Fl_Check_Button(x, y, w, h, l)
 {
 }
@@ -41,19 +41,19 @@ geCheck::geCheck(int x, int y, int w, int h, const char *l)
 
 void geCheck::draw()
 {
-  int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
-
-  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, G_GUI_FONT_SIZE_BASE);
-  fl_color(!active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2);
-  fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+       int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
+
+       if (value()) {
+               fl_rect(x(), y(), 12, h(), (Fl_Color) color);
+               fl_rectf(x(), y(), 12, h(), (Fl_Color) color);
+       }
+       else {
+               fl_rectf(x(), y(), 12, h(), FL_BACKGROUND_COLOR);
+               fl_rect(x(), y(), 12, h(), (Fl_Color) color);
+       }
+
+       fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
+       fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+       fl_color(!active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2);
+       fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
 }
index 486466c03354246a11dc722a8889e89841efc295..27100fa56fb4cc8c23e46c2bd1d59e7cf62e8e1d 100644 (file)
@@ -36,9 +36,9 @@ class geCheck : public Fl_Check_Button
 {
 public:
 
-  geCheck(int x, int y, int w, int h, const char *l=0);
+       geCheck(int x, int y, int w, int h, const char *l=0);
 
-  void draw();
+       void draw() override;
 };
 
 
index cfc561554be8c1eff867d04e691f5da6ecfdf675..28b98bed0d11c2e4f3165a23da78640c9ea5bfde 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/const.h"
+#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)
+geLiquidScroll::geLiquidScroll(int x, int y, int w, int h)
+: geScroll(x, y, w, h, Fl_Scroll::VERTICAL_ALWAYS)
 {
-       type(Fl_Scroll::VERTICAL);
-       scrollbar.color(G_COLOR_GREY_2);
-       scrollbar.selection_color(G_COLOR_GREY_4);
-       scrollbar.labelcolor(G_COLOR_LIGHT_1);
-       scrollbar.slider(G_CUSTOM_BORDER_BOX);
 }
 
 
@@ -51,11 +46,13 @@ geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, const char* l)
 
 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
+       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);
 }
+
+
index e803120cbca14a730b4b4ae48048ed2be6d870ae..46f27f1cffa86b248a5bdf8fbcf20e9f50b27f45 100644 (file)
 #define GE_LIQUID_SCROLL_H
 
 
-#include <FL/Fl_Scroll.H>
+#include "scroll.h"
 
 
-class geLiquidScroll : public Fl_Scroll
+class geLiquidScroll : public geScroll
 {
 public:
 
-       geLiquidScroll(int x, int y, int w, int h, const char *l=0);
+       geLiquidScroll(int x, int y, int w, int h);
 
-       void resize(int x, int y, int w, int h);
+       void resize(int x, int y, int w, int h) override;
+
+    /* addWidget
+    Adds a new widget to the bottom, with proper spacing. */
+    
+    template<typename T>
+    T* addWidget(T* wg)
+    {
+        int numChildren = countChildren();
+
+        int wx = x();
+        int wy = y() - yposition() + (numChildren * (wg->h() + G_GUI_INNER_MARGIN));
+        int ww = w() - 24;
+        int wh = wg->h();
+
+        wg->resize(wx, wy, ww, wh);
+        add(wg);
+        redraw();    
+
+        return wg;
+    }   
 };
 
 
index dbbc651f70e5e87d9556b83b0d1aa60f9fa318f6..740204e9cf3e945e3837ac26949a482f104a8764 100644 (file)
@@ -26,7 +26,7 @@
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
+#include "core/const.h"
 #include "radio.h"
 
 
@@ -41,19 +41,19 @@ geRadio::geRadio(int x, int y, int w, int h, const char *l)
 
 void geRadio::draw()
 {
- int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
-
- 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, G_GUI_FONT_SIZE_BASE);
- fl_color(G_COLOR_LIGHT_2);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
      int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
+
      if (value()) {
+               fl_rect(x(), y(), 12, h(), (Fl_Color) color);
+               fl_rectf(x(), y(), 12, h(), (Fl_Color) color);
      }
      else {
+               fl_rectf(x(), y(), 12, h(), FL_BACKGROUND_COLOR);
+               fl_rect(x(), y(), 12, h(), (Fl_Color) color);
      }
+
      fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR);  // clearer
      fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
      fl_color(G_COLOR_LIGHT_2);
      fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
 }
index d8bcb4207e9c0ba4eeb9687cf695f65978a9e7d0..a9a588a8c1978ea3c7681fb2223db49bc3afce81 100644 (file)
@@ -36,9 +36,10 @@ class geRadio : public Fl_Radio_Button
 {
 public:
 
-  geRadio(int x, int y, int w, int h, const char *l=0);
+       geRadio(int x, int y, int w, int h, const char *l=0);
 
-  void draw();
+       void draw() override;
 };
 
+
 #endif
index 9d4b66bb86dae451b4854c3b232a4ca04c3e9f7b..9dba569f0a92cf5cf31a03833cd8cf50d4a9c0bb 100644 (file)
@@ -103,7 +103,7 @@ void geResizerBar::handleDrag(int diff)
                        if ((wd->x()+wd->w()) == top) {                          // found widget directly above?
                                if ((wd->w()+diff) < m_minSize)
                                        diff = wd->w() - m_minSize;                          // clamp
-                               wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h());   // change height
+                               wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h());   // change width
                                break;                                                 // done with first pass
                        }
                }
index 578b5b72afe4622f6cf0aeb0d53799f87cea0bd1..29ed9947cb39cc66c7ce2d33bc803d6504531d79 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/const.h"
+#include <cassert>
+#include "core/const.h"
 #include "boxtypes.h"
 #include "scroll.h"
 
 
 geScroll::geScroll(int x, int y, int w, int h, int t)
-  : Fl_Scroll(x, y, w, h)
+       : Fl_Scroll(x, y, w, h)
 {
-  type(t);
+       type(t);
 
-  scrollbar.color(G_COLOR_GREY_2);
-  scrollbar.selection_color(G_COLOR_GREY_4);
-  scrollbar.labelcolor(G_COLOR_LIGHT_1);
-  scrollbar.slider(G_CUSTOM_BORDER_BOX);
+       scrollbar.color(G_COLOR_GREY_2);
+       scrollbar.selection_color(G_COLOR_GREY_4);
+       scrollbar.labelcolor(G_COLOR_LIGHT_1);
+       scrollbar.slider(G_CUSTOM_BORDER_BOX);
 
-  hscrollbar.color(G_COLOR_GREY_2);
-  hscrollbar.selection_color(G_COLOR_GREY_4);
-  hscrollbar.labelcolor(G_COLOR_LIGHT_1);
-  hscrollbar.slider(G_CUSTOM_BORDER_BOX);
+       hscrollbar.color(G_COLOR_GREY_2);
+       hscrollbar.selection_color(G_COLOR_GREY_4);
+       hscrollbar.labelcolor(G_COLOR_LIGHT_1);
+       hscrollbar.slider(G_CUSTOM_BORDER_BOX);
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geScroll::countChildren() const
+{
+       return children() - 2; // Exclude scrollbars
+}
\ No newline at end of file
index 31549744ae931fd7fb982360002a7a9ac99c984d..875ea93b7974c29e6c4dcfe17d2ca5508f0dca76 100644 (file)
@@ -40,6 +40,8 @@ class geScroll : public Fl_Scroll
 public:
 
        geScroll(int x, int y, int w, int h, int type=Fl_Scroll::BOTH);
+
+    int countChildren() const;
 };
 
 
index 64623d3d7a0b95068dea212f937a74de0202bb54..d71a51309c1ea6be1d8bc021741ec0fd5a0ffeb9 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.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)
+geStatusButton::geStatusButton(int x, int y, int w, int h, const char** imgOff,
+       const char** imgOn)
+: geButton(x, y, w, h, nullptr, imgOff, imgOn),
+  m_status(false)
 {
 }
 
@@ -46,7 +46,25 @@ geStatusButton::geStatusButton(int x, int y, int w, int h, const char **imgOff,
 
 void geStatusButton::draw()
 {
-  geButton::draw();
-  if (status)
-    fl_draw_pixmap(imgOn, x()+1, y()+1, G_COLOR_GREY_4);
+       geButton::draw();
+       if (m_status)
+               fl_draw_pixmap(imgOn, x()+1, y()+1, G_COLOR_GREY_4);
+       else
+               fl_draw_pixmap(imgOff, x()+1, y()+1, G_COLOR_GREY_4);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geStatusButton::setStatus(bool s)
+{
+    m_status = s;
+    redraw();
+}
+
+
+bool geStatusButton::getStatus() const
+{
+    return m_status;
 }
index 1f8f9242873e05a4fa0eec6d1f7e76f6efb9f8ea..c648d258a2c7d529169c3c5e324d5ed95dd73fbb 100644 (file)
@@ -39,12 +39,18 @@ class geStatusButton : public geButton
 {
 public:
 
-       geStatusButton(int x, int y, int w, int h, const char **imgOff=nullptr,
-    const char **imgOn=nullptr);
+       geStatusButton(int x, int y, int w, int h, const char** imgOff=nullptr,
+    const char** imgOn=nullptr);
 
        void draw() override;
 
-       bool status;
+    bool getStatus() const;
+
+    void setStatus(bool s);
+
+private:
+
+       bool m_status;
 };
 
 
index 9a2512f5f07257ba60fd653ec0b25be2fbd41240..462c56b194894a8bd1033824ea7f8306c73fa1e7 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../core/const.h"
-#include "../../utils/string.h"
-#include "../../utils/fs.h"
-#include "../dialogs/browser/browserBase.h"
+#include "core/const.h"
+#include "utils/string.h"
+#include "utils/fs.h"
+#include "gui/dialogs/browser/browserBase.h"
 #include "basics/boxtypes.h"
 #include "browser.h"
 
 
-using std::string;
-using namespace giada;
-
-
+namespace giada {
+namespace v
+{
 geBrowser::geBrowser(int x, int y, int w, int h)
- : Fl_File_Browser(x, y, w, h),
-   m_showHiddenFiles(false)
+: Fl_File_Browser  (x, y, w, h),
+  m_showHiddenFiles(false)
 {
        box(G_CUSTOM_BORDER_BOX);
        textsize(G_GUI_FONT_SIZE_BASE);
        textcolor(G_COLOR_LIGHT_2);
        selection_color(G_COLOR_GREY_4);
        color(G_COLOR_GREY_2);
-  type(FL_SELECT_BROWSER);
+       type(FL_SELECT_BROWSER);
 
        this->scrollbar.color(G_COLOR_GREY_2);
        this->scrollbar.selection_color(G_COLOR_GREY_4);
@@ -58,7 +57,7 @@ geBrowser::geBrowser(int x, int y, int w, int h)
        this->hscrollbar.labelcolor(G_COLOR_LIGHT_1);
        this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
 
-  take_focus();  // let it have focus on startup
+       take_focus();  // let it have focus on startup
 }
 
 
@@ -67,28 +66,28 @@ geBrowser::geBrowser(int x, int y, int w, int h)
 
 void geBrowser::toggleHiddenFiles()
 {
-  m_showHiddenFiles = !m_showHiddenFiles;
-  loadDir(m_currentDir);
+       m_showHiddenFiles = !m_showHiddenFiles;
+       loadDir(m_currentDir);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geBrowser::loadDir(const string& dir)
+void geBrowser::loadDir(const std::string& dir)
 {
-  m_currentDir = dir;
-  load(m_currentDir.c_str());
-
-  /* Clean up unwanted elements. Hide "../" first, it just screws up things.
-  Also remove hidden files, if requested. */
-
-  for (int i=size(); i>=0; i--) {
-    if (text(i) == nullptr)
-      continue;
-    if (strcmp(text(i), "../") == 0 || (!m_showHiddenFiles && strncmp(text(i), ".", 1) == 0))
-      remove(i);
-  }
+       m_currentDir = dir;
+       load(m_currentDir.c_str());
+
+       /* Clean up unwanted elements. Hide "../" first, it just screws up things.
+       Also remove hidden files, if requested. */
+
+       for (int i=size(); i>=0; i--) {
+               if (text(i) == nullptr)
+                       continue;
+               if (strcmp(text(i), "../") == 0 || (!m_showHiddenFiles && strncmp(text(i), ".", 1) == 0))
+                       remove(i);
+       }
 }
 
 
@@ -97,70 +96,70 @@ void geBrowser::loadDir(const string& dir)
 int geBrowser::handle(int e)
 {
        int ret = Fl_File_Browser::handle(e);
-  switch (e) {
-    case FL_FOCUS:
+       switch (e) {
+               case FL_FOCUS:
                case FL_UNFOCUS:
                        ret = 1;                        // enables receiving Keyboard events
                        break;
-    case FL_KEYDOWN:  // keyboard
-      if (Fl::event_key(FL_Down))
-        select(value() + 1);
-      else
-      if (Fl::event_key(FL_Up))
-        select(value() - 1);
-      else
-      if (Fl::event_key(FL_Enter))
-        static_cast<gdBrowserBase*>(parent())->fireCallback();
-      ret = 1;
-      break;
-    case FL_PUSH:    // mouse
-      if (Fl::event_clicks() > 0)  // double click
-        static_cast<gdBrowserBase*>(parent())->fireCallback();
-      ret = 1;
-      break;
-    case FL_RELEASE: // mouse
-      /* nasty trick to keep the selection on mouse release */
-      if (value() > 1) {
-        select(value() - 1);
-        select(value() + 1);
-      }
-      else {
-        select(value() + 1);
-        select(value() - 1);
-      }
-      ret = 1;
-      break;
-  }
+               case FL_KEYDOWN:  // keyboard
+                       if (Fl::event_key(FL_Down))
+                               select(value() + 1);
+                       else
+                       if (Fl::event_key(FL_Up))
+                               select(value() - 1);
+                       else
+                       if (Fl::event_key(FL_Enter))
+                               static_cast<v::gdBrowserBase*>(parent())->fireCallback();
+                       ret = 1;
+                       break;
+               case FL_PUSH:    // mouse
+                       if (Fl::event_clicks() > 0)  // double click
+                               static_cast<v::gdBrowserBase*>(parent())->fireCallback();
+                       ret = 1;
+                       break;
+               case FL_RELEASE: // mouse
+                       /* nasty trick to keep the selection on mouse release */
+                       if (value() > 1) {
+                               select(value() - 1);
+                               select(value() + 1);
+                       }
+                       else {
+                               select(value() + 1);
+                               select(value() - 1);
+                       }
+                       ret = 1;
+                       break;
+       }
        return ret;
 }
 
 /* -------------------------------------------------------------------------- */
 
 
-string geBrowser::getCurrentDir()
+std::string geBrowser::getCurrentDir()
 {
-  return normalize(u::string::getRealPath(m_currentDir));
+       return normalize(u::string::getRealPath(m_currentDir));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string geBrowser::getSelectedItem(bool fullPath)
+std::string geBrowser::getSelectedItem(bool fullPath)
 {
-  if (!fullPath)     // no full path requested? return the selected text
-    return normalize(text(value()));
-  else
-  if (value() == 0)  // no rows selected? return current directory
-    return normalize(m_currentDir);
-  else {
+       if (!fullPath)     // no full path requested? return the selected text
+               return normalize(text(value()));
+       else
+       if (value() == 0)  // no rows selected? return current directory
+               return normalize(m_currentDir);
+       else {
 #ifdef G_OS_WINDOWS
-    string sep = m_currentDir != "" ? G_SLASH_STR : "";
+               std::string sep = m_currentDir != "" ? G_SLASH_STR : "";
 #else
-    string sep = G_SLASH_STR;
+               std::string sep = G_SLASH_STR;
 #endif
-    return normalize(u::string::getRealPath(m_currentDir + sep + normalize(text(value()))));
-  }
+               return normalize(u::string::getRealPath(m_currentDir + sep + normalize(text(value()))));
+       }
 }
 
 
@@ -169,23 +168,24 @@ string geBrowser::getSelectedItem(bool fullPath)
 
 void geBrowser::preselect(int pos, int line)
 {
-  position(pos);
-  select(line);
+       position(pos);
+       select(line);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-string geBrowser::normalize(const string& s)
+std::string geBrowser::normalize(const std::string& s)
 {
-  string out = s;
+       std::string out = s;
 
-  /* If string ends with G_SLASH, remove it. Don't do it if is the root dir, 
-  that is '/' on Unix or '[x]:\' on Windows. */
+       /* If std::string ends with G_SLASH, remove it. Don't do it if is the root dir, 
+       that is '/' on Unix or '[x]:\' on Windows. */
 
-  //if (out.back() == G_SLASH && out.length() > 1)
-  if (out.back() == G_SLASH && !gu_isRootDir(s))
-    out = out.substr(0, out.size()-1);
-  return out;
+       //if (out.back() == G_SLASH && out.length() > 1)
+       if (out.back() == G_SLASH && !u::fs::isRootDir(s))
+               out = out.substr(0, out.size()-1);
+       return out;
 }
+}} // giada::v::
\ No newline at end of file
index 533d1a3f63b6424f0cb44e342821cf4dc383c111..d4a78b07b00ce91f0acbceb0bda85bf599327066 100644 (file)
 #include <FL/Fl_File_Browser.H>
 
 
+namespace giada {
+namespace v
+{
 class geBrowser : public Fl_File_Browser
 {
-private:
-
-       std::string m_currentDir;
-       bool m_showHiddenFiles;
-
-       /* normalize
-       Makes sure the std::string never ends with a trailing slash. */
-
-       std::string normalize(const std::string& s);
-
 public:
 
        geBrowser(int x, int y, int w, int h);
 
-  void toggleHiddenFiles();
+       void toggleHiddenFiles();
 
        /* init
        Initializes browser and show 'dir' as initial directory. */
@@ -69,6 +62,18 @@ public:
        void preselect(int position, int line);
 
        int handle(int e);
+
+private:
+
+       /* normalize
+       Makes sure the std::string never ends with a trailing slash. */
+
+       std::string normalize(const std::string& s);
+
+       std::string m_currentDir;
+       bool m_showHiddenFiles;
 };
+}} // giada::v::
+
 
 #endif
index bb192f8220228b6e2fe2074ec6f32900a7ccbc7f..7517b61f73fe62931f43f06e7cea9ac5d0832717 100644 (file)
 
 
 #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/devInfo.h"
-#include "../basics/box.h"
-#include "../basics/choice.h"
-#include "../basics/check.h"
-#include "../basics/input.h"
-#include "../basics/button.h"
+#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/devInfo.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
 #include "tabAudio.h"
 
 
-using std::string;
-using namespace giada;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v
+{
 geTabAudio::geTabAudio(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Sound System")
+: Fl_Group(X, Y, W, H, "Sound System")
 {
        begin();
        soundsys        = new geChoice(x()+114, y()+9,  250, 20, "System");
@@ -55,7 +53,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        sounddevOut     = new geChoice(x()+114, y()+65, 222, 20, "Output device");
        devOutInfo      = new geButton(x()+344, y()+65, 20,  20, "?");
        channelsOut     = new geChoice(x()+114, y()+93, 55,  20, "Output channels");
-       limitOutput     = new geCheck (x()+177, y()+97, 55,  20, "Limit output");
+       limitOutput     = new geCheck (x()+177, y()+93, 55,  20, "Limit output");
        sounddevIn      = new geChoice(x()+114, y()+121, 222, 20, "Input device");
        devInInfo       = new geButton(x()+344, y()+121, 20,  20, "?");
        channelsIn      = new geChoice(x()+114, y()+149, 55,  20, "Input channels");
@@ -71,14 +69,14 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
 
 #if defined(__linux__)
 
-       if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+       if (m::kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
                soundsys->add("ALSA");
-       if (kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+       if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
                soundsys->add("Jack");
-       if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+       if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
                soundsys->add("PulseAudio");
 
-       switch (conf::soundSystem) {
+       switch (m::conf::soundSystem) {
                case G_SYS_API_NONE:
                        soundsys->showItem("(none)");
                        break;
@@ -95,16 +93,37 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
                        break;
        }
 
+#elif defined(__FreeBSD__)
+
+       if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+               soundsys->add("Jack");
+       if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+               soundsys->add("PulseAudio");
+
+       switch (m::conf::soundSystem) {
+               case G_SYS_API_NONE:
+                       soundsys->showItem("(none)");
+                       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))
+       if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
                soundsys->add("DirectSound");
-       if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+       if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
                soundsys->add("ASIO");
-       if (kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
+       if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
                soundsys->add("WASAPI");
 
-       switch (conf::soundSystem) {
+       switch (m::conf::soundSystem) {
                case G_SYS_API_NONE:
                        soundsys->showItem("(none)");
                        break;
@@ -121,10 +140,10 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
 
 #elif defined (__APPLE__)
 
-       if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+       if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
                soundsys->add("CoreAudio");
 
-       switch (conf::soundSystem) {
+       switch (m::conf::soundSystem) {
                case G_SYS_API_NONE:
                        soundsys->showItem("(none)");
                        break;
@@ -145,7 +164,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        devOutInfo->callback(cb_showOutputInfo, this);
        devInInfo->callback(cb_showInputInfo, this);
 
-       if (conf::soundSystem != G_SYS_API_NONE) {
+       if (m::conf::soundSystem != G_SYS_API_NONE) {
                fetchSoundDevs();
                fetchOutChans(sounddevOut->value());
                fetchInChans(sounddevIn->value());
@@ -153,11 +172,11 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
                /* fill frequency dropdown menu */
                /* TODO - add fetchFrequencies() */
 
-               int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
+               int nfreq = m::kernelAudio::getTotalFreqs(sounddevOut->value());
                for (int i=0; i<nfreq; i++) {
-                       int freq = kernelAudio::getFreq(sounddevOut->value(), i);
+                       int freq = m::kernelAudio::getFreq(sounddevOut->value(), i);
                        samplerate->add(u::string::iToString(freq).c_str());
-                       if (freq == conf::samplerate)
+                       if (freq == m::conf::samplerate)
                                samplerate->value(i);
                }
        }
@@ -181,18 +200,18 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        buffersize->add("1024");
        buffersize->add("2048");
        buffersize->add("4096");
-       buffersize->showItem(u::string::iToString(conf::buffersize).c_str());
+       buffersize->showItem(u::string::iToString(m::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);
+       rsmpQuality->value(m::conf::rsmpQuality);
 
-       recTriggerLevel->value(u::string::fToString(conf::recTriggerLevel, 1).c_str());
+       recTriggerLevel->value(u::string::fToString(m::conf::recTriggerLevel, 1).c_str());
 
-       limitOutput->value(conf::limitOutput);
+       limitOutput->value(m::conf::limitOutput);
 }
 
 
@@ -231,8 +250,8 @@ void geTabAudio::cb_fetchOutChans()
 
 void geTabAudio::cb_showInputInfo()
 {
-       unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
-       new gdDevInfo(dev);
+       unsigned dev = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       new v::gdDevInfo(dev);
 }
 
 
@@ -241,8 +260,8 @@ void geTabAudio::cb_showInputInfo()
 
 void geTabAudio::cb_showOutputInfo()
 {
-       unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
-       new gdDevInfo(dev);
+       unsigned dev = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       new v::gdDevInfo(dev);
 }
 
 
@@ -253,8 +272,8 @@ 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! */
+        * querying m::kernelAudio. Watch out if soundsysInitValue == 0: you don't want
+        * to query m::kernelAudio for '(none)' soundsystem! */
 
        if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
                sounddevOut->clear();
@@ -312,8 +331,8 @@ void geTabAudio::fetchInChans(int menuItem)
 
        channelsIn->clear();
 
-       unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
-       unsigned chs = kernelAudio::getMaxInChans(dev);
+       unsigned dev = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       unsigned chs = m::kernelAudio::getMaxInChans(dev);
 
        if (chs == 0) {
                channelsIn->add("none");
@@ -321,10 +340,10 @@ void geTabAudio::fetchInChans(int menuItem)
                return;
        }
        for (unsigned i=0; i<chs; i+=2) {
-               string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
+               std::string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
                channelsIn->add(tmp.c_str());
        }
-       channelsIn->value(conf::channelsIn);
+       channelsIn->value(m::conf::channelsIn);
 }
 
 
@@ -335,8 +354,8 @@ void geTabAudio::fetchOutChans(int menuItem)
 {
        channelsOut->clear();
 
-       unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
-       unsigned chs = kernelAudio::getMaxOutChans(dev);
+       unsigned dev = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       unsigned chs = m::kernelAudio::getMaxOutChans(dev);
 
        if (chs == 0) {
                channelsOut->add("none");
@@ -344,10 +363,10 @@ void geTabAudio::fetchOutChans(int menuItem)
                return;
        }
        for (unsigned i=0; i<chs; i+=2) {
-               string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
+               std::string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
                channelsOut->add(tmp.c_str());
        }
-       channelsOut->value(conf::channelsOut);
+       channelsOut->value(m::conf::channelsOut);
 }
 
 
@@ -356,18 +375,15 @@ void geTabAudio::fetchOutChans(int menuItem)
 
 int geTabAudio::findMenuDevice(geChoice *m, int device)
 {
-       if (device == -1)
-               return 0;
-
-       if (kernelAudio::getStatus() == false)
+       if (device == -1 || !m::kernelAudio::isReady())
                return 0;
 
        for (int i=0; i<m->size(); i++) {
-               if (kernelAudio::getDeviceName(device) == "")
+               if (m::kernelAudio::getDeviceName(device) == "")
                        continue;
                if (m->text(i) == nullptr)
                        continue;
-               if (m->text(i) == kernelAudio::getDeviceName(device))
+               if (m->text(i) == m::kernelAudio::getDeviceName(device))
                        return i;
        }
 
@@ -380,7 +396,7 @@ int geTabAudio::findMenuDevice(geChoice *m, int device)
 
 void geTabAudio::fetchSoundDevs()
 {
-       if (kernelAudio::countDevices() == 0) {
+       if (m::kernelAudio::countDevices() == 0) {
                sounddevOut->add("-- no devices found --");
                sounddevOut->value(0);
                sounddevIn->add("-- no devices found --");
@@ -398,11 +414,11 @@ void geTabAudio::fetchSoundDevs()
 
                sounddevIn->add("(disabled)");
 
-               for (unsigned i=0; i<kernelAudio::countDevices(); i++) {
+               for (unsigned i=0; i<m::kernelAudio::countDevices(); i++) {
 
                        /* escaping '/', very dangerous in FLTK (it creates a submenu) */
 
-                       string tmp = kernelAudio::getDeviceName(i);
+                       std::string tmp = m::kernelAudio::getDeviceName(i);
                        for (unsigned k=0; k<tmp.size(); k++)
                                if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
                                        tmp[k] = '-';
@@ -411,10 +427,10 @@ void geTabAudio::fetchSoundDevs()
                         * way we can filter devices only for input or output, e.g. an input
                         * devices has 0 output channels. */
 
-                       if (kernelAudio::getMaxOutChans(i) > 0)
+                       if (m::kernelAudio::getMaxOutChans(i) > 0)
                                sounddevOut->add(tmp.c_str());
 
-                       if (kernelAudio::getMaxInChans(i) > 0)
+                       if (m::kernelAudio::getMaxInChans(i) > 0)
                                sounddevIn->add(tmp.c_str());
                }
 
@@ -426,7 +442,7 @@ void geTabAudio::fetchSoundDevs()
                        devOutInfo->deactivate();
                }
                else {
-                       int outMenuValue = findMenuDevice(sounddevOut, conf::soundDeviceOut);
+                       int outMenuValue = findMenuDevice(sounddevOut, m::conf::soundDeviceOut);
                        sounddevOut->value(outMenuValue);
                }
 
@@ -436,7 +452,7 @@ void geTabAudio::fetchSoundDevs()
                        devInInfo->deactivate();
                }
                else {
-                       int inMenuValue = findMenuDevice(sounddevIn, conf::soundDeviceIn);
+                       int inMenuValue = findMenuDevice(sounddevIn, m::conf::soundDeviceIn);
                        sounddevIn->value(inMenuValue);
                }
        }
@@ -448,58 +464,66 @@ void geTabAudio::fetchSoundDevs()
 
 void geTabAudio::save()
 {
-       string text = soundsys->text(soundsys->value());
+       std::string text = soundsys->text(soundsys->value());
 
        if (text == "(none)") {
-               conf::soundSystem = G_SYS_API_NONE;
+               m::conf::soundSystem = G_SYS_API_NONE;
                return;
        }
 
 #if defined(__linux__)
 
        else if (text == "ALSA")
-               conf::soundSystem = G_SYS_API_ALSA;
+               m::conf::soundSystem = G_SYS_API_ALSA;
+       else if (text == "Jack")
+               m::conf::soundSystem = G_SYS_API_JACK;
+       else if (text == "PulseAudio")
+               m::conf::soundSystem = G_SYS_API_PULSE;
+
+#elif defined(__FreeBSD__)
+
        else if (text == "Jack")
-               conf::soundSystem = G_SYS_API_JACK;
+               m::conf::soundSystem = G_SYS_API_JACK;
        else if (text == "PulseAudio")
-               conf::soundSystem = G_SYS_API_PULSE;
+               m::conf::soundSystem = G_SYS_API_PULSE;
 
 #elif defined(_WIN32)
 
        else if (text == "DirectSound")
-               conf::soundSystem = G_SYS_API_DS;
+               m::conf::soundSystem = G_SYS_API_DS;
        else if (text == "ASIO")
-               conf::soundSystem = G_SYS_API_ASIO;
+               m::conf::soundSystem = G_SYS_API_ASIO;
        else if (text == "WASAPI")
-               conf::soundSystem = G_SYS_API_WASAPI;
+               m::conf::soundSystem = G_SYS_API_WASAPI;
 
 #elif defined (__APPLE__)
 
        else if (text == "CoreAudio")
-               conf::soundSystem = G_SYS_API_CORE;
+               m::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();
+       m::conf::soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+       m::conf::soundDeviceIn  = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+       m::conf::channelsOut    = channelsOut->value();
+       m::conf::channelsIn     = channelsIn->value();
+       m::conf::limitOutput    = limitOutput->value();
+       m::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;
+       if (m::conf::soundDeviceOut == -1)
+               m::conf::soundDeviceOut = 0;
 
-       conf::buffersize = std::atoi(buffersize->text());
-       conf::recTriggerLevel = std::atof(recTriggerLevel->value());
+       m::conf::buffersize = std::atoi(buffersize->text());
+       m::conf::recTriggerLevel = std::atof(recTriggerLevel->value());
 
        const Fl_Menu_Item* i = nullptr;
        i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
        if (i != nullptr)
-               conf::samplerate = std::atoi(i->label());
+               m::conf::samplerate = std::atoi(i->label());
 }
+}} // giada::v::
index 65f0cc5dbcc47660b237c614493a6c72965aaf42..3fe337cb6d79aedfaf0e0540642cb1e1dc64a76e 100644 (file)
@@ -38,8 +38,30 @@ class geButton;
 class geInput;
 
 
+namespace giada {
+namespace v
+{
 class geTabAudio : public Fl_Group
 {
+public:
+
+       geTabAudio(int x, int y, int w, int h);
+
+       void save();
+
+       geChoice* soundsys;
+       geChoice* buffersize;
+       geChoice* samplerate;
+       geChoice* sounddevOut;
+       geButton* devOutInfo;
+       geChoice* channelsOut;
+       geCheck*  limitOutput;
+       geChoice* sounddevIn;
+       geButton* devInInfo;
+       geChoice* channelsIn;
+       geInput*  recTriggerLevel;
+       geChoice* rsmpQuality;
+
 private:
 
        static void cb_deactivate_sounddev(Fl_Widget* w, void* p);
@@ -59,26 +81,8 @@ private:
        int  findMenuDevice(geChoice* m, int device);
 
        int soundsysInitValue;
-
-public:
-
-       geChoice* soundsys;
-       geChoice* buffersize;
-       geChoice* samplerate;
-       geChoice* sounddevOut;
-       geButton* devOutInfo;
-       geChoice* channelsOut;
-       geCheck*  limitOutput;
-       geChoice* sounddevIn;
-       geButton* devInInfo;
-       geChoice* channelsIn;
-       geInput*  recTriggerLevel;
-       geChoice* rsmpQuality;
-
-       geTabAudio(int x, int y, int w, int h);
-
-       void save();
 };
+}} // giada::v::
 
 
 #endif
index 3926e5c3c3483c4e75bb9f4365f74692df656bfa..931a319a3a02877b12555b9dd03d282f7b84dffe 100644 (file)
 
 
 #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 "core/const.h"
+#include "core/conf.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/radio.h"
+#include "gui/elems/basics/check.h"
 #include "tabBehaviors.h"
 
 
-using std::string;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v
+{
 geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Behaviors")
+: Fl_Group(X, Y, W, H, "Behaviors")
 {
-  begin();
+       begin();
 
-  Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
+       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");
+               recsStopOnChanHalt_0 = new geRadio(x()+25, y()+60, 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
+       Fl_Group* radioGrp_2 = new Fl_Group(x(), radioGrp_1->y()+radioGrp_1->h(), 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");
+               chansStopOnSeqHalt_0 = new geRadio(x()+25, y()+130, 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");
+       treatRecsAsLoops      = new geCheck(x(), radioGrp_2->y()+radioGrp_2->h() + 15, 280, 20, "Treat one shot channels with actions as loops");
+       inputMonitorDefaultOn = new geCheck(x(), treatRecsAsLoops->y()+treatRecsAsLoops->h() + 5, 280, 20, "New sample channels have input monitor on by default");
 
-  end();
+       end();
 
        labelsize(G_GUI_FONT_SIZE_BASE);
        selection_color(G_COLOR_GREY_4);
 
-       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);
+       m::conf::recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
+       m::conf::chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
+       treatRecsAsLoops->value(m::conf::treatRecsAsLoops);
+       inputMonitorDefaultOn->value(m::conf::inputMonitorDefaultOn);
 
        recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
        recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
@@ -78,15 +77,18 @@ geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
 /* -------------------------------------------------------------------------- */
 
 
-void geTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((geTabBehaviors*)p)->__cb_radio_mutex(w); }
+void geTabBehaviors::cb_radio_mutex(Fl_Widget* w, void* p) 
+{ 
+       static_cast<geTabBehaviors*>(p)->cb_radio_mutex(w); 
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
+void geTabBehaviors::cb_radio_mutex(Fl_Widget* w)
 {
-       ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
+       static_cast<Fl_Button*>(w)->type(FL_RADIO_BUTTON);
 }
 
 
@@ -95,8 +97,9 @@ void geTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
 
 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;
+       m::conf::recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
+       m::conf::chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
+       m::conf::treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
+       m::conf::inputMonitorDefaultOn = inputMonitorDefaultOn->value() == 1 ? 1 : 0;
 }
+}} // giada::v::
\ No newline at end of file
index 07f29f1ffeaa74f1a86a4b36701a36b7c7242741..58f3613cbac9af5bc37d571b470c204c6cac8131 100644 (file)
@@ -36,14 +36,16 @@ class geRadio;
 class geCheck;
 
 
+namespace giada {
+namespace v
+{
 class geTabBehaviors : public Fl_Group
 {
-private:
+public:
 
-       static void cb_radio_mutex  (Fl_Widget *w, void *p);
-       inline void __cb_radio_mutex(Fl_Widget *w);
+       geTabBehaviors(int x, int y, int w, int h);
 
-public:
+       void save();
 
        geRadio *recsStopOnChanHalt_1;
        geRadio *recsStopOnChanHalt_0;
@@ -52,11 +54,13 @@ public:
        geCheck *treatRecsAsLoops;
        geCheck *inputMonitorDefaultOn;
 
-       geTabBehaviors(int x, int y, int w, int h);
 
-       void save();
-};
+private:
 
+       static void cb_radio_mutex(Fl_Widget* w, void* p);
+       void cb_radio_mutex(Fl_Widget* w);
+};
+}} // giada::v::
 
 
 #endif
index 9af5b1a60dd6e20529e4b16f2a8f7eaa43ce743d..a7d91637168a5c2af1fcf582783bc9b41c8c7ebe 100644 (file)
 
 
 #include <string>
-#include "../../../core/const.h"
+#include "core/const.h"
 #ifdef G_OS_MAC
        #include <RtMidi.h>
 #else
        #include <rtmidi/RtMidi.h>
 #endif
-#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 "core/conf.h"
+#include "core/midiMapConf.h"
+#include "core/kernelMidi.h"
+#include "utils/gui.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/check.h"
 #include "tabMidi.h"
 
 
-using std::string;
-using namespace giada;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v
+{
 geTabMidi::geTabMidi(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "MIDI")
+: Fl_Group(X, Y, W, H, "MIDI")
 {
        begin();
-       system    = new geChoice(x()+w()-250, y()+9, 250, 20, "System");
-       portOut   = new geChoice(x()+w()-250, system->y()+system->h()+8, 250, 20, "Output port");
-       portIn    = new geChoice(x()+w()-250, portOut->y()+portOut->h()+8, 250, 20, "Input port");
-       midiMap   = new geChoice(x()+w()-250, portIn->y()+portIn->h()+8, 250, 20, "Output Midi Map");
-       sync        = new geChoice(x()+w()-250, midiMap->y()+midiMap->h()+8, 250, 20, "Sync");
+       system  = new geChoice(x()+w()-250, y()+9, 250, 20, "System");
+       portOut = new geChoice(x()+w()-250, system->y()+system->h()+8, 250, 20, "Output port");
+       portIn  = new geChoice(x()+w()-250, portOut->y()+portOut->h()+8, 250, 20, "Input port");
+       midiMap = new geChoice(x()+w()-250, portIn->y()+portIn->h()+8, 250, 20, "Output Midi Map");
+       sync    = new geChoice(x()+w()-250, midiMap->y()+midiMap->h()+8, 250, 20, "Sync");
        new geBox(x(), sync->y()+sync->h()+8, w(), h()-150, "Restart Giada for the changes to take effect.");
        end();
 
@@ -72,11 +70,11 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H)
        sync->add("(disabled)");
        sync->add("MIDI Clock (master)");
        sync->add("MTC (master)");
-       if      (conf::midiSync == MIDI_SYNC_NONE)
+       if      (m::conf::midiSync == MIDI_SYNC_NONE)
                sync->value(0);
-       else if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+       else if (m::conf::midiSync == MIDI_SYNC_CLOCK_M)
                sync->value(1);
-       else if (conf::midiSync == MIDI_SYNC_MTC_M)
+       else if (m::conf::midiSync == MIDI_SYNC_MTC_M)
                sync->value(2);
 
        systemInitValue = system->value();
@@ -88,7 +86,7 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H)
 
 void geTabMidi::fetchOutPorts()
 {
-       if (kernelMidi::countOutPorts() == 0) {
+       if (m::kernelMidi::countOutPorts() == 0) {
                portOut->add("-- no ports found --");
                portOut->value(0);
                portOut->deactivate();
@@ -97,10 +95,10 @@ void geTabMidi::fetchOutPorts()
 
                portOut->add("(disabled)");
 
-               for (unsigned i=0; i<kernelMidi::countOutPorts(); i++)
-                       portOut->add(u::gui::removeFltkChars(kernelMidi::getOutPortName(i)).c_str());
+               for (unsigned i=0; i<m::kernelMidi::countOutPorts(); i++)
+                       portOut->add(u::gui::removeFltkChars(m::kernelMidi::getOutPortName(i)).c_str());
 
-               portOut->value(conf::midiPortOut+1);    // +1 because midiPortOut=-1 is '(disabled)'
+               portOut->value(m::conf::midiPortOut+1);    // +1 because midiPortOut=-1 is '(disabled)'
        }
 }
 
@@ -109,7 +107,7 @@ void geTabMidi::fetchOutPorts()
 
 void geTabMidi::fetchInPorts()
 {
-       if (kernelMidi::countInPorts() == 0) {
+       if (m::kernelMidi::countInPorts() == 0) {
                portIn->add("-- no ports found --");
                portIn->value(0);
                portIn->deactivate();
@@ -118,10 +116,10 @@ void geTabMidi::fetchInPorts()
 
                portIn->add("(disabled)");
 
-               for (unsigned i=0; i<kernelMidi::countInPorts(); i++)
-                       portIn->add(u::gui::removeFltkChars(kernelMidi::getInPortName(i)).c_str());
+               for (unsigned i=0; i<m::kernelMidi::countInPorts(); i++)
+                       portIn->add(u::gui::removeFltkChars(m::kernelMidi::getInPortName(i)).c_str());
 
-               portIn->value(conf::midiPortIn+1);    // +1 because midiPortIn=-1 is '(disabled)'
+               portIn->value(m::conf::midiPortIn+1);    // +1 because midiPortIn=-1 is '(disabled)'
        }
 }
 
@@ -131,23 +129,23 @@ void geTabMidi::fetchInPorts()
 
 void geTabMidi::fetchMidiMaps()
 {
-       if (midimap::maps.size() == 0) {
+       if (m::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();
+       for (unsigned i=0; i<m::midimap::maps.size(); i++) {
+               const char *imap = m::midimap::maps.at(i).c_str();
                midiMap->add(imap);
-               if (conf::midiMapPath == imap)
+               if (m::conf::midiMapPath == imap)
                        midiMap->value(i);
        }
 
-       /* Preselect the 0 midimap if nothing is selected but midimaps exist. */
+       /* Preselect the 0 m::midimap if nothing is selected but midimaps exist. */
 
-       if (midiMap->value() == -1 && midimap::maps.size() > 0)
+       if (midiMap->value() == -1 && m::midimap::maps.size() > 0)
                midiMap->value(0);
 }
 
@@ -157,27 +155,27 @@ void geTabMidi::fetchMidiMaps()
 
 void geTabMidi::save()
 {
-       string text = system->text(system->value());
+       std::string text = system->text(system->value());
 
        if      (text == "ALSA")
-               conf::midiSystem = RtMidi::LINUX_ALSA;
+               m::conf::midiSystem = RtMidi::LINUX_ALSA;
        else if (text == "Jack")
-               conf::midiSystem = RtMidi::UNIX_JACK;
+               m::conf::midiSystem = RtMidi::UNIX_JACK;
        else if (text == "Multimedia MIDI")
-               conf::midiSystem = RtMidi::WINDOWS_MM;
+               m::conf::midiSystem = RtMidi::WINDOWS_MM;
        else if (text == "OSX Core MIDI")
-               conf::midiSystem = RtMidi::MACOSX_CORE;
+               m::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::midiMapPath = midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
+       m::conf::midiPortOut = portOut->value()-1;   // -1 because midiPortOut=-1 is '(disabled)'
+       m::conf::midiPortIn  = portIn->value()-1;    // -1 because midiPortIn=-1 is '(disabled)'
+       m::conf::midiMapPath = m::midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
 
        if      (sync->value() == 0)
-               conf::midiSync = MIDI_SYNC_NONE;
+               m::conf::midiSync = MIDI_SYNC_NONE;
        else if (sync->value() == 1)
-               conf::midiSync = MIDI_SYNC_CLOCK_M;
+               m::conf::midiSync = MIDI_SYNC_CLOCK_M;
        else if (sync->value() == 2)
-               conf::midiSync = MIDI_SYNC_MTC_M;
+               m::conf::midiSync = MIDI_SYNC_MTC_M;
 }
 
 
@@ -188,14 +186,19 @@ void geTabMidi::fetchSystems()
 {
 #if defined(__linux__)
 
-       if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
+       if (m::kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
                system->add("ALSA");
-       if (kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+       if (m::kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+               system->add("Jack");
+
+#elif defined(__FreeBSD__)
+
+       if (m::kernelMidi::hasAPI(RtMidi::UNIX_JACK))
                system->add("Jack");
 
 #elif defined(_WIN32)
 
-       if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
+       if (m::kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
                system->add("Multimedia MIDI");
 
 #elif defined (__APPLE__)
@@ -204,7 +207,7 @@ void geTabMidi::fetchSystems()
 
 #endif
 
-       switch (conf::midiSystem) {
+       switch (m::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;
@@ -227,7 +230,7 @@ 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. */
+        * querying m::kernelMidi. */
 
        if (systemInitValue == system->value()) {
                portOut->clear();
@@ -249,5 +252,5 @@ void geTabMidi::cb_changeSystem()
                portIn->value(0);
                sync->deactivate();
        }
-
 }
+}} // giada::v::
\ No newline at end of file
index c134aac84173ef190d40a105ce5651581f4905f6..090525a756fdb00bdfb4a81580d13d61b55a1e79 100644 (file)
@@ -36,21 +36,16 @@ class geChoice;
 class geCheck;
 
 
+namespace giada {
+namespace v
+{
 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();
+public:
 
-       int systemInitValue;
+       geTabMidi(int x, int y, int w, int h);
 
-public:
+       void save();
 
        geChoice* system;
        geChoice* portOut;
@@ -58,10 +53,19 @@ public:
        geChoice* midiMap;
        geChoice* sync;
 
-       geTabMidi(int x, int y, int w, int h);
+private:
 
-       void save();
+       void fetchSystems();
+       void fetchOutPorts();
+       void fetchInPorts();
+       void fetchMidiMaps();
+
+       static void cb_changeSystem(Fl_Widget* w, void* p);
+       void cb_changeSystem();
+
+       int systemInitValue;
 };
+}} // giada::v::
 
 
 #endif
index 0e2cc10097353c156938f6e46fe84a5b6488300b..6460b68eb40f8fc97b1d3e8857c6dd9749b995ee 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../basics/choice.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "gui/elems/basics/choice.h"
 #include "tabMisc.h"
 
 
-using namespace giada::m;
-
-
+namespace giada {
+namespace v
+{
 geTabMisc::geTabMisc(int X, int Y, int W, int H)
-       : Fl_Group(X, Y, W, H, "Misc")
+: Fl_Group(X, Y, W, H, "Misc")
 {
        begin();
        debugMsg = new geChoice(x()+w()-230, y()+9, 230, 20, "Debug messages");
@@ -48,7 +48,7 @@ geTabMisc::geTabMisc(int X, int Y, int W, int H)
        labelsize(G_GUI_FONT_SIZE_BASE);
        selection_color(G_COLOR_GREY_4);
 
-       switch (conf::logMode) {
+       switch (m::conf::logMode) {
                case LOG_MODE_MUTE:
                        debugMsg->value(0);
                        break;
@@ -69,13 +69,14 @@ void geTabMisc::save()
 {
        switch(debugMsg->value()) {
                case 0:
-                       conf::logMode = LOG_MODE_MUTE;
+                       m::conf::logMode = LOG_MODE_MUTE;
                        break;
                case 1:
-                       conf::logMode = LOG_MODE_STDOUT;
+                       m::conf::logMode = LOG_MODE_STDOUT;
                        break;
                case 2:
-                       conf::logMode = LOG_MODE_FILE;
+                       m::conf::logMode = LOG_MODE_FILE;
                        break;
        }
 }
+}} // giada::v::
\ No newline at end of file
index 138f6d1b173e14912282750bcec3237a3cd77e30..099d8a2ed9acddf47cb6a24275b575116638a504 100644 (file)
 class geChoice;
 
 
+namespace giada {
+namespace v
+{
 class geTabMisc : public Fl_Group
 {
 public:
 
-       geChoice *debugMsg;
-
        geTabMisc(int x, int y, int w, int h);
 
        void save();
+
+       geChoice* debugMsg;
 };
+}} // giada::v::
+
 
 #endif
index 343b2565b5dba1429114c52f35b8353ba01024c4..6f87b180aa8ef16761f728d981ed53052676db42 100644 (file)
 
 #include <functional>
 #include <FL/Fl.H>
-#include "../../../core/const.h"
-#include "../../../core/conf.h"
-#include "../../../core/graphics.h"
-#include "../../../core/pluginManager.h"
-#include "../../../glue/plugin.h"
-#include "../../../utils/string.h"
-#include "../../../utils/fs.h"
-#include "../../../utils/gui.h"
-#include "../../dialogs/window.h"
-#include "../../dialogs/mainWindow.h"
-#include "../../dialogs/browser/browserDir.h"
-#include "../basics/box.h"
-#include "../basics/radio.h"
-#include "../basics/check.h"
-#include "../basics/input.h"
-#include "../basics/button.h"
+#include "core/const.h"
+#include "core/conf.h"
+#include "core/graphics.h"
+#include "core/pluginManager.h"
+#include "glue/plugin.h"
+#include "utils/string.h"
+#include "utils/fs.h"
+#include "utils/gui.h"
+#include "gui/dialogs/window.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/browser/browserDir.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/radio.h"
+#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
 #include "tabPlugins.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using std::string;
-using namespace giada;
-using namespace giada::m;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
+namespace giada {
+namespace v
+{
 geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
        : Fl_Group(X, Y, W, H, "Plugins")
 {
@@ -73,7 +71,7 @@ geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
        m_info->label("Scan in progress. Please wait...");
        m_info->hide();
 
-       m_folderPath->value(conf::pluginPath.c_str());
+       m_folderPath->value(m::conf::pluginPath.c_str());
        m_folderPath->label("Plugins folder");
 
        m_browse->callback(cb_browse, (void*) this);
@@ -89,7 +87,7 @@ geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
 
 void geTabPlugins::refreshCount()
 {
-       string scanLabel = "Scan (" + u::string::iToString(pluginManager::countAvailablePlugins()) + " found)";
+       std::string scanLabel = "Scan (" + std::to_string(m::pluginManager::countAvailablePlugins()) + " found)";
        m_scanButton->label(scanLabel.c_str());
 }
 
@@ -106,10 +104,10 @@ void geTabPlugins::cb_browse(Fl_Widget* w, void* p) { ((geTabPlugins*)p)->cb_bro
 
 void geTabPlugins::cb_browse()
 {
-       gdBrowserDir* browser = new gdBrowserDir(0, 0, 800, 600, "Add plug-ins directory", 
-               conf::patchPath, giada::c::plugin::setPluginPathCb);
+       v::gdBrowserDir* browser = new v::gdBrowserDir("Add plug-ins directory", 
+               m::conf::patchPath, c::plugin::setPluginPathCb);
 
-       static_cast<gdWindow*>(top_window())->addSubWindow(browser);
+       static_cast<v::gdWindow*>(top_window())->addSubWindow(browser);
 }
 
 
@@ -120,14 +118,14 @@ void geTabPlugins::cb_scan()
 {
        std::function<void(float)> callback = [this] (float progress) 
        {
-               string l = "Scan in progress (" + u::string::iToString((int)(progress*100)) + "%). Please wait...";
+               std::string l = "Scan in progress (" + std::to_string((int)(progress*100)) + "%). Please wait...";
                m_info->label(l.c_str());
                Fl::wait();
        };
 
        m_info->show();
-       pluginManager::scanDirs(m_folderPath->value(), callback);
-       pluginManager::saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
+       m::pluginManager::scanDirs(m_folderPath->value(), callback);
+       m::pluginManager::saveList(u::fs::getHomePath() + G_SLASH + "plugins.xml");
        m_info->hide();
        refreshCount();
 }
@@ -138,7 +136,7 @@ void geTabPlugins::cb_scan()
 
 void geTabPlugins::save()
 {
-       conf::pluginPath = m_folderPath->value();
+       m::conf::pluginPath = m_folderPath->value();
 }
 
 
@@ -147,9 +145,10 @@ void geTabPlugins::save()
 
 void geTabPlugins::refreshVstPath()
 {
-       m_folderPath->value(conf::pluginPath.c_str());
+       m_folderPath->value(m::conf::pluginPath.c_str());
        m_folderPath->redraw();
 }
+}} // giada::v::
 
 
 #endif // WITH_VST
index 31c346da3d1a33f1b0ef0891ff042b50b1fbc75a..e0f844635a87b0e482dc90a09876bd5abec687e2 100644 (file)
@@ -40,14 +40,19 @@ class geButton;
 class geBox;
 
 
+namespace giada {
+namespace v
+{
 class geTabPlugins : public Fl_Group
 {
-private:
+public:
 
-       geInput* m_folderPath;
-       geButton* m_browse;
-       geButton* m_scanButton;
-       geBox* m_info;
+       geTabPlugins(int x, int y, int w, int h);
+
+       void save();
+       void refreshVstPath();
+
+private:
 
        static void cb_scan(Fl_Widget* w, void* p);
        static void cb_browse(Fl_Widget* w, void* p);
@@ -56,16 +61,15 @@ private:
 
        void refreshCount();
 
-public:
-
-       geTabPlugins(int x, int y, int w, int h);
-
-       void save();
-       void refreshVstPath();
+       geInput*  m_folderPath;
+       geButton* m_browse;
+       geButton* m_scanButton;
+       geBox*    m_info;
 };
+}} // giada::v::
 
 
-#endif
+#endif // WITH_VST
 
 
 #endif
index c1fe379680685eb128aecf6db8843fbab70d1658..6f187c0c7fcb71aab9c71175501bae3a29a77eec 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/const.h"
-#include "../../../core/recManager.h"
-#include "../../../core/mixer.h"
-#include "../../../core/clock.h"
-#include "../../../utils/gui.h"
+#include "core/const.h"
+#include "core/recManager.h"
+#include "core/mixer.h"
+#include "core/clock.h"
+#include "utils/gui.h"
 #include "beatMeter.h"
 
 
 namespace giada {
 namespace v
 {
-geBeatMeter::geBeatMeter(int x, int y, int w, int h, const char* l)
-: Fl_Box(x, y, w, h, l)
+geBeatMeter::geBeatMeter(int x, int y, int w, int h)
+: Fl_Box(x, y, w, h)
 {
 }
 
@@ -59,6 +59,15 @@ Fl_Color geBeatMeter::getCursorColor()
 /* -------------------------------------------------------------------------- */
 
 
+void geBeatMeter::refresh()
+{
+       redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void geBeatMeter::draw()
 {
        using namespace giada::m;
@@ -92,5 +101,4 @@ void geBeatMeter::draw()
 
        fl_rectf(x()+greyX+1, y()+1, w()-greyX-1,  h()-2, G_COLOR_GREY_4);
 }
-
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index d6214ca48026031c5ae85b5ecf0ef56435397b49..993d89066af3725ff1c95a8c3b04e20742eef655 100644 (file)
@@ -41,8 +41,11 @@ class geBeatMeter : public Fl_Box
 {
 public:
 
-       geBeatMeter(int x, int y, int w, int h, const char* l=nullptr);
-       void draw();
+       geBeatMeter(int x, int y, int w, int h);
+       
+       void draw() override;
+
+       void refresh();
 
 private:
 
index b99558cdbf3c25e6e046cbb64b877c9015fcb4a7..7237d8223680e0e3bb3c1ad4cb2dbd12fdccff4e 100644 (file)
 
 
 #include <FL/Fl.H>
-#include "../../../../core/const.h"
-#include "../../../../core/channel.h"
-#include "../../../../core/graphics.h"
-#include "../../../../core/pluginHost.h"
-#include "../../../../utils/gui.h"
-#include "../../../../glue/channel.h"
-#include "../../../dialogs/mainWindow.h"
-#include "../../../dialogs/pluginList.h"
-#include "../../basics/button.h"
-#include "../../basics/dial.h"
-#include "../../basics/statusButton.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/graphics.h"
+#include "core/pluginHost.h"
+#include "utils/gui.h"
+#include "glue/channel.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/pluginList.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/statusButton.h"
 #include "column.h"
 #include "channelStatus.h"
 #include "channelButton.h"
 #include "channel.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
-using namespace giada;
-
-
-geChannel::geChannel(int X, int Y, int W, int H, giada::m::Channel* ch)
-: Fl_Group    (X, Y, W, H, nullptr),
-  ch          (ch)
+namespace giada {
+namespace v
 {
+geChannel::geChannel(int X, int Y, int W, int H, ID channelId)
+: Fl_Pack  (X, Y, W, H),
+  channelId(channelId)
+{
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
 }
 
 
@@ -71,9 +74,27 @@ void geChannel::cb_openFxWindow(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_ope
 /* -------------------------------------------------------------------------- */
 
 
+void geChannel::refresh()
+{
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               if (mainButton->visible())
+                       mainButton->refresh();
+               if (c.recStatus == ChannelStatus::WAIT || c.playStatus == ChannelStatus::WAIT)
+                       blink();
+               playButton->setStatus(c.isPlaying());
+               mute->setStatus(c.mute);
+               solo->setStatus(c.solo);
+       });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void geChannel::cb_arm()
 {
-       c::channel::toggleArm(ch, true);
+       c::channel::setArm(channelId, arm->value());
 }
 
 
@@ -82,7 +103,7 @@ void geChannel::cb_arm()
 
 void geChannel::cb_mute()
 {
-       c::channel::toggleMute(ch);
+       c::channel::toggleMute(channelId);
 }
 
 
@@ -91,7 +112,7 @@ void geChannel::cb_mute()
 
 void geChannel::cb_solo()
 {
-       c::channel::toggleSolo(ch);
+       c::channel::toggleSolo(channelId);
 }
 
 
@@ -100,7 +121,7 @@ void geChannel::cb_solo()
 
 void geChannel::cb_changeVol()
 {
-       c::channel::setVolume(ch, vol->value());
+       c::channel::setVolume(channelId, vol->value());
 }
 
 
@@ -110,7 +131,7 @@ void geChannel::cb_changeVol()
 #ifdef WITH_VST
 void geChannel::cb_openFxWindow()
 {
-       u::gui::openSubWindow(G_MainWin, new gdPluginList(m::pluginHost::StackType::CHANNEL, ch), WID_FX_LIST);
+       u::gui::openSubWindow(G_MainWin, new v::gdPluginList(channelId), WID_FX_LIST);
 }
 #endif
 
@@ -119,9 +140,9 @@ void geChannel::cb_openFxWindow()
 
 
 
-int geChannel::getColumnIndex()
+int geChannel::getColumnId()
 {
-       return static_cast<geColumn*>(parent())->getIndex();
+       return static_cast<geColumn*>(parent())->id;
 }
 
 
@@ -140,48 +161,6 @@ void geChannel::blink()
 /* -------------------------------------------------------------------------- */
 
 
-void geChannel::setColorsByStatus()
-{
-       switch (ch->status) {
-               case ChannelStatus::OFF:
-               case ChannelStatus::EMPTY:
-                       mainButton->setDefaultMode();
-                       button->imgOn  = channelPlay_xpm;
-                       button->imgOff = channelStop_xpm;
-                       button->redraw();
-                       break;
-               case ChannelStatus::PLAY:
-                       mainButton->setPlayMode();
-                       if (!button->value()) { // If not manually pressed (it would interfere)
-                               button->imgOn  = channelStop_xpm;
-                               button->imgOff = channelPlay_xpm;
-                               button->redraw();                       
-                       }
-                       break;
-               case ChannelStatus::WAIT:
-                       blink();
-                       break;
-               case ChannelStatus::ENDING:
-                       mainButton->setEndingMode();
-                       break;
-               default: break;
-       }
-
-       switch (ch->recStatus) {
-               case ChannelStatus::WAIT:
-                       blink();
-                       break;
-               case ChannelStatus::ENDING:
-                       mainButton->setEndingMode();
-                       break;
-               default: break;
-       }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void geChannel::packWidgets()
 {
        /* Count visible widgets and resize mainButton according to how many widgets
@@ -218,17 +197,20 @@ void geChannel::packWidgets()
 
 bool geChannel::handleKey(int e)
 {
-       if (Fl::event_key() != ch->key) 
+       m::model::ChannelsLock l(m::model::channels);
+       const m::Channel& ch = m::model::get(m::model::channels, channelId);
+       
+       if (Fl::event_key() != ch.key) 
                return false;
 
-       if (e == FL_KEYDOWN && !button->value()) {  // Key not already pressed
-               button->take_focus();                   // Move focus to this button
-               button->value(1);
+       if (e == FL_KEYDOWN && !playButton->value()) {  // Key not already pressed
+               playButton->take_focus();                   // Move focus to this playButton
+               playButton->value(1);
                return true;
        }
 
        if (e == FL_KEYUP) {
-               button->value(0);
+               playButton->value(0);
                return true;
        }
        
@@ -245,14 +227,14 @@ void geChannel::changeSize(int H)
        
        int Y = y() + (H / 2 - (G_GUI_UNIT / 2));
 
-       button->resize(x(), Y, w(), G_GUI_UNIT);
-       arm->resize(x(), Y, w(), G_GUI_UNIT);   
-       mainButton->resize(x(), y(), w(), H);
-       mute->resize(x(), Y, w(), G_GUI_UNIT);
-       solo->resize(x(), Y, w(), G_GUI_UNIT);
-       vol->resize(x(), Y, w(), G_GUI_UNIT);
+       playButton->resize(playButton->x(), Y, playButton->w(), G_GUI_UNIT);
+       arm->resize(arm->x(), Y, arm->w(), G_GUI_UNIT);   
+       mainButton->resize(mainButton->x(), mainButton->y(), mainButton->w(), H);
+       mute->resize(mute->x(), Y, mute->w(), G_GUI_UNIT);
+       solo->resize(solo->x(), Y, solo->w(), G_GUI_UNIT);
+       vol->resize(vol->x(), Y, vol->w(), G_GUI_UNIT);
 #ifdef WITH_VST
-       fx->resize(x(), Y, w(), G_GUI_UNIT);
+       fx->resize(fx->x(), Y, fx->w(), G_GUI_UNIT);
 #endif
 }
 
@@ -264,3 +246,5 @@ int geChannel::getSize()
 {
        return h();
 }
+
+}} // giada::v::
index 6308f27c29b0bde0521e3c1d8c8e694f72ec827c..cee2430eff6c5f9a4ecd644f1895090a0ebb9094 100644 (file)
 #define GE_CHANNEL_H
 
 
-#include <FL/Fl_Group.H>
-#include "../../../../core/types.h"
-#include "../../../../core/channel.h"
+#include <FL/Fl_Pack.H>
+#include "core/types.h"
 
 
 class geChannelStatus;
 class geButton;
-class geChannelButton;
 class geDial;
-#ifdef WITH_VST
 class geStatusButton;
-#endif
 
 
-class geChannel : public Fl_Group
+namespace giada {
+namespace m
 {
-public:
-
-       geChannel(int x, int y, int w, int h, giada::m::Channel* ch);
-
-       /* reset
-       Resets channel to initial status. */
-
-       virtual void reset() = 0;
+class Channel;
+}
+namespace v
+{
+class geChannelButton;
 
-       /* update
-       Updates the label of sample button and everything else such as 'R' button, 
-       key box and so on, according to global values. */
+class geChannel : public Fl_Pack
+{
+public:
 
-       virtual void update() = 0;
+       geChannel(int x, int y, int w, int h, ID channelId);
 
        /* refresh
        Updates graphics. */
 
-       virtual void refresh() = 0;
+       virtual void refresh();
 
        /* changeSize
        Changes channel's size according to a template (x1, x2, ...). */
 
        virtual void changeSize(int h);
 
-       /* getColumnIndex
-       Returns the numeric index of the column in which this channel is located. */
+       /* getColumnId
+       Returns the ID of the column this channel resides in. */
 
-       int getColumnIndex();
+       ID getColumnId();
 
        int getSize();
 
@@ -83,14 +77,14 @@ public:
 
        bool handleKey(int e);
 
-       giada::m::Channel* ch;
+       ID channelId;
  
-       geButton*        button;
+       geStatusButton*  playButton;
        geChannelStatus* status;
        geButton*        arm;
        geChannelButton* mainButton;
-       geButton*        mute;
-       geButton*        solo;
+       geStatusButton*  mute;
+       geStatusButton*  solo;
        geDial*          vol;
 #ifdef WITH_VST
        geStatusButton*  fx;
@@ -134,16 +128,12 @@ protected:
 
        void blink();
 
-       /* setColorByStatus
-       Updates colors depending on channel status. */
-
-       void setColorsByStatus();
-
        /* packWidgets
        Spread widgets across available space. */
 
        void packWidgets();
 };
+}} // giada::v::
 
 
 #endif
index ff5232e850ef3eeee9326872aabd743048218515..3067ff471dcec9b53caf483dc9b17cde0df3a1f9 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../../core/const.h"
-#include "../../../../utils/string.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/recorder.h"
+#include "utils/string.h"
 #include "channelButton.h"
 
 
-using std::string;
-
-
-geChannelButton::geChannelButton(int x, int y, int w, int h, const char* l)
-       : geButton(x, y, w, h, l), 
-         m_key   ("") 
+namespace giada {
+namespace v
+{
+geChannelButton::geChannelButton(int x, int y, int w, int h, ID channelId)
+: geButton   (x, y, w, h), 
+  m_channelId(channelId),
+  m_key      ("")
 {
 }
 
@@ -44,9 +48,27 @@ geChannelButton::geChannelButton(int x, int y, int w, int h, const char* l)
 /* -------------------------------------------------------------------------- */
 
 
-void geChannelButton::setKey(const string& k)
+void geChannelButton::refresh()
 {
-       m_key = k;
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               switch (c.playStatus) {
+                       case ChannelStatus::OFF:
+                       case ChannelStatus::EMPTY:
+                               setDefaultMode(); break;
+                       case ChannelStatus::PLAY:
+                               setPlayMode(); break;
+                       case ChannelStatus::ENDING:
+                               setEndingMode(); break;
+                       default: break;
+               }
+
+               switch (c.recStatus) {
+                       case ChannelStatus::ENDING:
+                               setEndingMode(); break;
+                       default: break;
+               }
+       });
 }
 
 
@@ -55,10 +77,8 @@ void geChannelButton::setKey(const string& k)
 
 void geChannelButton::setKey(int k)
 {
-       if (k == 0)
-               m_key = "";
-       else 
-               m_key = static_cast<char>(k); // FIXME - What about unicode/utf-8?
+       m_key = k == 0 ? "" : std::string(1, k);
+       redraw();
 }
 
 
@@ -134,3 +154,5 @@ void geChannelButton::setEndingMode()
 {
        bgColor0 = G_COLOR_GREY_4;
 }
+
+}} // giada::v::
index 95f34339e03aa6e25984239de95f35f3e2b61be8..a49bb2a98e5a3255c92965770acc57c3e9d96511 100644 (file)
 #define GE_CHANNEL_BUTTON_H
 
 
-#include "../../basics/button.h"
+#include "gui/elems/basics/button.h"
 
 
+namespace giada {
+namespace m 
+{ 
+class Channel; 
+}
+namespace v
+{
 class geChannelButton : public geButton
 {
-private:
-
-       std::string m_key;
-
 public:
 
-       geChannelButton(int x, int y, int w, int h, const char* l=0);
+       geChannelButton(int x, int y, int w, int h, ID channelId);
+
+       virtual void refresh();
 
        void draw() override;
   
-       void setKey(const std::string& k);
        void setKey(int k);
        void setPlayMode();
        void setEndingMode();
        void setDefaultMode(const char* l=0);
        void setInputRecordMode();
        void setActionRecordMode();
+
+protected:
+
+       ID          m_channelId;
+       std::string m_key;
 };
+}} // giada::v::
 
 
 #endif
index 7153e695110e5f57facaa9cf0b0f1520bf01aee5..bfe17803c701e2e13addf4bd7abb300ad0d5337b 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/fl_draw.H>
-#include "../../../../utils/gui.h"
-#include "../../../../core/graphics.h"
-#include "../../../../core/sampleChannel.h"
-#include "../../../../core/const.h"
-#include "../../basics/boxtypes.h"
+#include "utils/gui.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/graphics.h"
+#include "core/const.h"
+#include "glue/channel.h"
+#include "gui/elems/basics/boxtypes.h"
 #include "channelMode.h"
 
 
-using namespace giada;
-
-
-geChannelMode::geChannelMode(int x, int y, int w, int h, m::SampleChannel *ch, 
-       const char *L)
-: Fl_Menu_Button(x, y, w, h, L), ch(ch)
+namespace giada {
+namespace v
+{
+geChannelMode::geChannelMode(int x, int y, int w, int h, ID channelId)
+: Fl_Menu_Button(x, y, w, h), 
+  m_channelId   (channelId)
 {
        box(G_CUSTOM_BORDER_BOX);
        textsize(G_GUI_FONT_SIZE_BASE);
@@ -65,7 +68,11 @@ geChannelMode::geChannelMode(int x, int y, int w, int h, m::SampleChannel *ch,
 void geChannelMode::draw() 
 {
        fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);    // border
-       switch (ch->mode) {
+
+       m::model::ChannelsLock l(m::model::channels);
+       const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, m_channelId));
+       
+       switch (ch.mode) {
                case ChannelMode::LOOP_BASIC:
                        fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1);
                        break;
@@ -97,18 +104,14 @@ void geChannelMode::draw()
 /* -------------------------------------------------------------------------- */
 
 
-void geChannelMode::cb_changeMode(Fl_Widget *v, void *p) { ((geChannelMode*)v)->__cb_changeMode((intptr_t)p); }
+void geChannelMode::cb_changeMode(Fl_Widget* v, void* p) { ((geChannelMode*)v)->cb_changeMode((intptr_t)p); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geChannelMode::__cb_changeMode(int mode)
+void geChannelMode::cb_changeMode(int mode)
 {
-       ch->mode = static_cast<ChannelMode>(mode);
-
-       /* What to do when the channel is playing and you change the mode? Nothing, 
-       since v0.5.3. Just refresh the action editor window, in case it's open. */
-
-       u::gui::refreshActionEditor();
+       c::channel::setSampleMode(m_channelId, static_cast<ChannelMode>(mode));
 }
+}} // giada::v::
index 03a2bbfb6dab6be24c80d1aee14bb4a7c68ed22a..90f95a31f8340fd2f52a356dcf096051b25d257d 100644 (file)
 #include <FL/Fl_Menu_Button.H>
 
 
+namespace giada {
+namespace m 
+{ 
+class SampleChannel; 
+}
+namespace v
+{
 class geChannelMode : public Fl_Menu_Button
 {
-private:
+public:
 
-       static void cb_changeMode  (Fl_Widget *v, void *p);
-       inline void __cb_changeMode(int mode);
+  geChannelMode(int x, int y, int w, int h, ID channelId);
 
-       giada::m::SampleChannel *ch;
+       void draw() override;
 
-public:
+private:
+
+    static void cb_changeMode(Fl_Widget* v, void* p);
+    void cb_changeMode(int mode);
 
-  geChannelMode(int x, int y, int w, int h, giada::m::SampleChannel *ch,
-    const char *l=0);
-       void draw();
+    ID m_channelId;
 };
+}} // giada::v::
 
 
 #endif
index 63653941569b50c8a21d9c7eb4087886ebcad0e4..052d3cc06c058d02ff7c28299d081cfb995ce275 100644 (file)
 
 
 #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 "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/mixer.h"
+#include "core/clock.h"
+#include "core/recorder.h"
+#include "core/recManager.h"
+#include "core/const.h"
 #include "channelStatus.h"
 
 
-using namespace giada;
-using namespace giada::m;
-
-
-geChannelStatus::geChannelStatus(int x, int y, int w, int h, SampleChannel *ch,
-  const char *L)
-  : Fl_Box(x, y, w, h, L), ch(ch) {}
+namespace giada {
+namespace v
+{
+geChannelStatus::geChannelStatus(int x, int y, int w, int h, ID channelId)
+: Fl_Box(x, y, w, h), channelId(channelId)
+{
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -48,40 +50,41 @@ geChannelStatus::geChannelStatus(int x, int y, int w, int h, SampleChannel *ch,
 
 void geChannelStatus::draw()
 {
-  fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);              // reset border
-  fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);     // reset background
-
-  if (ch == nullptr) 
-    return;
-
-  if (ch->status == ChannelStatus::WAIT    || 
-      ch->status == ChannelStatus::ENDING  ||
-      ch->recStatus == ChannelStatus::WAIT || 
-      ch->recStatus == ChannelStatus::ENDING)
-  {
-    fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
-  }
-  else
-  if (ch->status == ChannelStatus::PLAY)
-    fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
-  else
-    fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);  // status empty
-
-
-  if (mixer::recording && ch->armed)
-    fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED);     // take in progress
-  else
-  if (recorder::isActive())
-    fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE);    // action recording
-
-  /* Equation for the progress bar: 
-  ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
-
-  int pos = ch->getPosition();
-  if (pos == -1)
-    pos = 0;
-  else
-    pos = (pos * (w()-1)) / ((ch->getEnd() - ch->getBegin()));
-  fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
-
+       fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);              // reset border
+       fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);     // reset background
+
+       m::model::ChannelsLock l(m::model::channels);
+       const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, channelId));
+       
+       if (ch.playStatus == ChannelStatus::WAIT    || 
+           ch.playStatus == ChannelStatus::ENDING  ||
+           ch.recStatus == ChannelStatus::WAIT || 
+           ch.recStatus == ChannelStatus::ENDING)
+       {
+               fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
+       }
+       else
+       if (ch.playStatus == ChannelStatus::PLAY)
+               fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
+       else
+               fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);  // status empty
+
+
+       if (m::recManager::isRecordingInput() && ch.armed)
+               fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED);     // take in progress
+       else
+       if (m::recManager::isRecordingAction())
+               fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE);    // action recording
+
+       /* Equation for the progress bar: 
+       ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
+
+       int pos = ch.getPosition();
+       if (pos == -1)
+               pos = 0;
+       else
+               pos = (pos * (w()-1)) / ((ch.getEnd() - ch.getBegin()));
+       fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
 }
+
+}} // giada::v::
index 44d6ccce20ec995e31f00f030463ec73cd47cd4b..8225c5c0a519b2df8cd1f6729536fa71533b9e2c 100644 (file)
 #include <FL/Fl_Box.H>
 
 
+namespace giada {
+namespace m 
+{ 
+class SampleChannel; 
+}
+namespace v
+{
 class geChannelStatus : public Fl_Box
 {
 public:
-       geChannelStatus(int X, int Y, int W, int H, giada::m::SampleChannel *ch,
-    const char *L=0);
-       void draw();
-       giada::m::SampleChannel *ch;
+
+       geChannelStatus(int x, int y, int w, int h, ID channelId);
+
+       void draw() override;
+
+       ID channelId;
 };
+}} // giada::v::
 
 
 #endif
index f925885201910dbb729cce07e5989251c118341a..bf3962105335d5b7dcedf3127166561cb227680a 100644 (file)
 #include <cassert>
 #include <FL/fl_draw.H>
 #include <FL/Fl_Menu_Button.H>
-#include "../../../../core/sampleChannel.h"
-#include "../../../../core/midiChannel.h"
-#include "../../../../glue/channel.h"
-#include "../../../../utils/log.h"
-#include "../../../../utils/fs.h"
-#include "../../../../utils/string.h"
-#include "../../../dialogs/warnings.h"
-#include "../../../elems/basics/boxtypes.h"
-#include "../../../elems/basics/resizerBar.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/midiChannel.h"
+#include "glue/channel.h"
+#include "utils/log.h"
+#include "utils/fs.h"
+#include "utils/string.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/boxtypes.h"
 #include "keyboard.h"
 #include "sampleChannel.h"
 #include "midiChannel.h"
 #include "column.h"
 
 
-using std::vector;
-using std::string;
-using namespace giada;
-
-
-geColumn::geColumn(int X, int Y, int W, int H, int index, v::geKeyboard* parent)
-       : Fl_Group(X, Y, W, H), 
-               m_parent(parent), 
-               m_index (index)
+namespace giada {
+namespace v
 {
-       /* geColumn does a bit of a mess: we pass a pointer to its m_parent (geKeyboard) and
-       the geColumn itself deals with the creation of another widget, outside geColumn
-       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 nullptr: at this point (i.e the constructor)
-       geColumn is still detached from any parent. We use a custom geKeyboard *parent
-       instead. */
-
-       begin();
-       m_addChannelBtn = new geButton(x(), y(), w(), G_GUI_UNIT, "Add new channel");
-       end();
-
-       m_resizer = new geResizerBar(x()+w(), y(), G_GUI_OUTER_MARGIN * 2, h(), 
-               G_MIN_COLUMN_WIDTH, geResizerBar::HORIZONTAL);
-       m_parent->add(m_resizer);
-
-       m_addChannelBtn->callback(cb_addChannel, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geColumn::~geColumn()
-{
-       /* FIXME - this could actually cause a memory leak. m_resizer is
-       just removed, not deleted. But we cannot delete it right now. */
-
-       m_parent->remove(m_resizer);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-
-int geColumn::handle(int e)
+geColumn::geColumn(int X, int Y, int W, int H, ID id, geResizerBar* b)
+: Fl_Pack   (X, Y, W, H), 
+  id        (id),
+  resizerBar(b)
 {
-       switch (e) {
-               case FL_RELEASE: {
-                       if (Fl::event_button() == FL_RIGHT_MOUSE) {
-                               __cb_addChannel();
-                               return 1;
-                       }
-               }
-               case FL_DND_ENTER:              // return(1) for these events to 'accept' dnd
-               case FL_DND_DRAG:
-               case FL_DND_RELEASE: {
-                       return 1;
-               }
-               case FL_PASTE: {              // handle actual drop (paste) operation
-                       vector<string> paths = u::string::split(Fl::event_text(), "\n");
-                       bool fails = false;
-                       int result = 0;
-                       for (string& path : paths) {
-                               gu_log("[geColumn::handle] loading %s...\n", path.c_str());
-                               // TODO - c::channel::addAndLoad(...)
-                               m::SampleChannel* c = static_cast<m::SampleChannel*>(c::channel::addChannel(
-                                       m_index, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1));
-                               result = c::channel::loadChannel(c, gu_stripFileUrl(path));
-                               if (result != G_RES_OK) {
-                                       deleteChannel(c->guiChannel);
-                                       fails = true;
-                               }
-                       }
-                       if (fails) {
-                               if (paths.size() > 1)
-                                       gdAlert("Some files were not loaded successfully.");
-                               else
-                                       m_parent->printChannelMessage(result);
-                       }
-                       return 1;
-               }
-       }
+       end();
 
-       /* we return fl_Group::handle only if none of the cases above are fired. That
-       is because we don't want to propagate a dnd drop to all the sub widgets. */
+       type(Fl_Pack::VERTICAL);
+       spacing(G_GUI_INNER_MARGIN);
 
-       return Fl_Group::handle(e);
+       init();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::resize(int X, int Y, int W, int H)
+void geColumn::refresh()
 {
-       /* Resize all children, including "add channel" button. */
-
-       for (int i=0; i<children(); i++) {
-               Fl_Widget* wgCurr = child(i);
-               Fl_Widget* wgPrev = i == 0 ? nullptr : child(i - 1);
-               wgCurr->resize(X, (wgPrev == nullptr ? Y : wgPrev->y() + wgPrev->h() + G_GUI_INNER_MARGIN), 
-                       W, wgCurr->h());
+       for (int i=1; i<children(); i++) {  // Skip "add channel" button
+               geChannel* c = dynamic_cast<geChannel*>(child(i));
+               if (c != nullptr)
+                       c->refresh();
        }
-
-       /* Resize group itself. Must use internal functions, resize() would trigger
-       infinite recursion. */
-
-       x(X); y(Y); w(W); h(H);
-
-       /* Resize resizerBar. */
-
-       m_resizer->size(G_GUI_OUTER_MARGIN * 2, H);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::refreshChannels()
-{
-       for (int i=1; i<children(); i++)
-               static_cast<geChannel*>(child(i))->refresh();
-}
+void geColumn::cb_addChannel(Fl_Widget* v, void* p) { ((geColumn*)p)->cb_addChannel(); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::draw()
+geChannel* geColumn::addChannel(ID channelId, ChannelType t, int size)
 {
-       fl_color(G_COLOR_GREY_1_5);
-       fl_rectf(x(), y(), w(), h());
+       geChannel* gch = nullptr;
 
-       /* call draw and then redraw in order to avoid channel corruption when
-       scrolling horizontally */
+       if (t == ChannelType::SAMPLE)
+               gch = new geSampleChannel(0, 0, w(), size, channelId);
+       else
+               gch = new geMidiChannel(0, 0, w(), size, channelId);
 
-       for (int i=0; i<children(); i++) {
-               child(i)->draw();
-               child(i)->redraw();
-       }
+       add(gch);
+       gch->redraw();      // fix corruption
+       return gch;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::cb_addChannel(Fl_Widget* v, void* p) { ((geColumn*)p)->__cb_addChannel(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geColumn::repositionChannels()
+void geColumn::cb_addChannel()
 {
-       int totalH = 0;
-       for (int i=0; i<children(); i++)
-               totalH += child(i)->h() + G_GUI_INNER_MARGIN;
-       resize(x(), y(), w(), totalH + 66); // evil space for drag n drop
-}
+       u::log::print("[geColumn::cb_addChannel] id = %d\n", id);
 
+       Fl_Menu_Item menu[] = {
+               {"Add Sample channel"},
+               {"Add MIDI channel"},
+               {"Remove"},
+               {0}
+       };
 
-/* -------------------------------------------------------------------------- */
-
+       if (countChannels() > 0)
+               menu[2].deactivate();
 
-geChannel* geColumn::addChannel(m::Channel* ch, int size)
-{
-       geChannel* gch = nullptr;
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
 
-       /* All geChannels are added with y=0. That's not a problem, they will be 
-       repositioned later on during geColumn::resize(). */
+       const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
+       if (m == nullptr) return;
 
-       if (ch->type == ChannelType::SAMPLE)
-               gch = new geSampleChannel(x(), 0, w(), size, static_cast<m::SampleChannel*>(ch));
+       if (strcmp(m->label(), "Add Sample channel") == 0)
+               c::channel::addChannel(id, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1);
        else
-               gch = new geMidiChannel(x(), 0, w(), size, static_cast<m::MidiChannel*>(ch));
-
-       add(gch);
-
-       repositionChannels();
-       gch->redraw();    // fix corruption
-       m_parent->redraw(); // redraw Keyboard
-       return gch;
+       if (strcmp(m->label(), "Add MIDI channel") == 0)
+               c::channel::addChannel(id, ChannelType::MIDI, G_GUI_CHANNEL_H_1);
+       else
+               static_cast<geKeyboard*>(parent())->deleteColumn(id);
+               
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::deleteChannel(geChannel* gch)
+geChannel* geColumn::getChannel(ID chanID) const
 {
-       gch->hide();
-       remove(gch);
-       delete gch;
-
-       /** TODO
-        * reposition is useless when called by geColumn::clear(). Add a new
-        * parameter to skip the operation */
-
-       repositionChannels();
+       for (int i=1; i<children(); i++) { // Skip "add channel" button
+               geChannel* gch = static_cast<geChannel*>(child(i));
+               if (gch->channelId == chanID)
+                       return gch;
+       }
+       return nullptr;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::__cb_addChannel()
+void geColumn::init()
 {
-       gu_log("[geColumn::__cb_addChannel] m_index = %d\n", m_index);
+       clear();
 
-       Fl_Menu_Item rclick_menu[] = {
-               {"Sample channel"},
-               {"MIDI channel"},
-               {0}
-       };
-
-       Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50);
-       b->box(G_CUSTOM_BORDER_BOX);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
-
-       const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (!m) return;
+       m_addChannelBtn = new geButton(0, 0, w(), G_GUI_UNIT, "Edit column");
+       m_addChannelBtn->callback(cb_addChannel, (void*)this);
 
-       if (strcmp(m->label(), "Sample channel") == 0)
-               c::channel::addChannel(m_index, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1);
-       else
-               c::channel::addChannel(m_index, ChannelType::MIDI, G_GUI_CHANNEL_H_1);
+       add(m_addChannelBtn);
 }
 
 
-
 /* -------------------------------------------------------------------------- */
 
 
-void geColumn::clear(bool full)
+void geColumn::forEachChannel(std::function<void(geChannel* c)> f) const
 {
-       if (full)
-               Fl_Group::clear();
-       else {
-               while (children() >= 2) {  // skip "add new channel" btn
-                       int i = children()-1;
-                       deleteChannel(static_cast<geChannel*>(child(i)));
-               }
-       }
+       for (int i=1; i<children(); i++) // Skip "add channel" button
+               f(static_cast<geChannel*>(child(i)));
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-m::Channel* geColumn::getChannel(int i)
+int geColumn::countChannels() const
 {
-       return static_cast<geChannel*>(child(i + 1))->ch;  // Skip "add channel"
+       return children() - 1;
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geColumn::getIndex()       { return m_index; }
-void geColumn::setIndex(int i) { m_index = i; }
-bool geColumn::isEmpty()       { return children() == 1; }
-int geColumn::countChannels()  { return children() - 1; }
\ No newline at end of file
+}} // giada::v::
index 209bc9c26c09c84ea407ed584307d67dfc5c99bc..2ef5fd64bb7fa90c4dcee7dcff6326dffa1f7270 100644 (file)
 #define GE_COLUMN_H
 
 
-#include <FL/Fl_Group.H>
+#include <functional>
+#include <FL/Fl_Pack.H>
+#include "core/types.h"
 
 
 class geButton;
-class geChannel;
 class geResizerBar;
+
+
 namespace giada {
-namespace v 
+namespace v
 {
 class geKeyboard;
-}}
-
-
-class geColumn : public Fl_Group
+class geChannel;
+class geColumn : public Fl_Pack
 {
-private:
-
-       static void cb_addChannel  (Fl_Widget* v, void* p);
-       inline void __cb_addChannel();
-
-       geButton*     m_addChannelBtn;
-       geResizerBar* m_resizer;
-       giada::v::geKeyboard* m_parent;
-
-       int m_index;
-
 public:
 
-       geColumn(int x, int y, int w, int h, int index, giada::v::geKeyboard* parent);
-       ~geColumn();
+       geColumn(int x, int y, int w, int h, ID id, geResizerBar* b);
 
+       geChannel* getChannel(ID chanID) const;
+       
        /* addChannel
-       Adds a new channel in this column and set the internal pointer to channel 
-       to 'ch'. */
+       Adds a new channel in this column. */
 
-       geChannel* addChannel(giada::m::Channel* ch, int size);
+       geChannel* addChannel(ID channelId, ChannelType t, int size);
 
-       int handle(int e) override;
-       void draw() override;
-       void resize(int x, int y, int w, int h) override;
+       /* refreshChannels
+       Updates channels' graphical statues. Called on each GUI cycle. */
 
-       /* clear
-       Removes all channels from the column. If full==true, delete also the "add new 
-       channel" button. */
+       void refresh();
 
-       void clear(bool full=false);
+       void init(); 
 
-       /* deleteChannel
-       Removes the channel 'gch' from this column. */
+       void forEachChannel(std::function<void(geChannel* c)> f) const;
+       
+       ID id;
 
-       void deleteChannel(geChannel* gch);
+       geResizerBar* resizerBar;
 
-       void repositionChannels();
+private:
 
-       /* refreshChannels
-       Updates channels' graphical statues. Called on each GUI cycle. */
+       static void cb_addChannel(Fl_Widget* v, void* p);
+       void cb_addChannel();
 
-       void refreshChannels();
+       int countChannels() const;
 
-       giada::m::Channel* getChannel(int i);
-       int getIndex();
-       void setIndex(int i);
-       bool isEmpty();   
-  int countChannels();
+       geButton* m_addChannelBtn;
 };
+}} // giada::v::
 
 
 #endif
index 8ca0a028caaf8a7aa9a96f13502f491f84ac2ec2..112132f78b99c3dfebba31dcf768eda722cc80b1 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../../core/sampleChannel.h"
-#include "../../../../glue/transport.h"
-#include "../../../../glue/io.h"
-#include "../../../../utils/log.h"
-#include "../../../dialogs/warnings.h"
-#include "../../../dispatcher.h"
-#include "../../basics/boxtypes.h"
+#include <cassert>
+#include <FL/fl_draw.H>
+#include "core/model/model.h"
+#include "core/channels/sampleChannel.h"
+#include "glue/io.h"
+#include "glue/channel.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/vector.h"
+#include "utils/string.h"
+#include "gui/dispatcher.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/resizerBar.h"
 #include "column.h"
 #include "sampleChannel.h"
 #include "channelButton.h"
 namespace giada {
 namespace v
 {
-int geKeyboard::indexColumn = 0;
-
-
-/* -------------------------------------------------------------------------- */
-
-
 geKeyboard::geKeyboard(int X, int Y, int W, int H)
 : Fl_Scroll     (X, Y, W, H),
-       addColumnBtn(nullptr)
+  m_addColumnBtn(nullptr)
 {
+       end();
+
        color(G_COLOR_GREY_1);
        type(Fl_Scroll::BOTH_ALWAYS);
        scrollbar.color(G_COLOR_GREY_2);
@@ -62,10 +65,6 @@ geKeyboard::geKeyboard(int X, int Y, int W, int H)
        hscrollbar.labelcolor(G_COLOR_LIGHT_1);
        hscrollbar.slider(G_CUSTOM_BORDER_BOX);
 
-       addColumnBtn = new geButton(8, y(), 200, 20, "Add new column");
-       addColumnBtn->callback(cb_addColumn, (void*) this);
-       add(addColumnBtn);
-
        init();
 }
 
@@ -75,136 +74,106 @@ geKeyboard::geKeyboard(int X, int Y, int W, int H)
 
 void geKeyboard::init()
 {
-       /* add 6 empty columns as init layout */
-
-       __cb_addColumn();
-       __cb_addColumn();
-       __cb_addColumn();
-       __cb_addColumn();
-       __cb_addColumn();
-       __cb_addColumn();
-}
-
+       m_columnId = m::IdManager();
 
-/* -------------------------------------------------------------------------- */
+       deleteAllColumns();
 
+       /* Add 6 empty columns as initial layout. */
 
-void geKeyboard::freeChannel(geChannel* gch)
-{
-       gch->reset();
+       cb_addColumn();
+       cb_addColumn();
+       cb_addColumn();
+       cb_addColumn();
+       cb_addColumn();
+       cb_addColumn();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::deleteChannel(geChannel* gch)
+void geKeyboard::rebuild()
 {
-       for (unsigned i=0; i<columns.size(); i++) {
-               int k = columns.at(i)->find(gch);
-               if (k != columns.at(i)->children()) {
-                       columns.at(i)->deleteChannel(gch);
-                       return;
-               }
+       for (geColumn* c : m_columns)
+               c->init();
+       
+       m::model::ChannelsLock lock(m::model::channels);
+
+       for (const m::Channel* ch : m::model::channels) {
+               
+               if (ch->id == m::mixer::MASTER_OUT_CHANNEL_ID ||
+                       ch->id == m::mixer::MASTER_IN_CHANNEL_ID  ||
+                       ch->id == m::mixer::PREVIEW_CHANNEL_ID)
+                       continue;
+               
+               geColumn* column = getColumn(ch->columnId);
+               if (column == nullptr)
+                       column = cb_addColumn(G_DEFAULT_COLUMN_WIDTH, ch->columnId);
+               
+               column->addChannel(ch->id, ch->type, G_GUI_CHANNEL_H_1);
        }
-}
-
 
-/* -------------------------------------------------------------------------- */
-
-
-void geKeyboard::updateChannel(geChannel* gch)
-{
-       gch->update();
+       redraw();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::organizeColumns()
+void geKeyboard::deleteColumn(ID id)
 {
-       if (columns.size() == 0)
-               return;
-
-       /* Otherwise delete all empty columns. */
-
-       for (size_t i=columns.size(); i-- > 0;) {
-               if (columns.at(i)->isEmpty()) {
-                       Fl::delete_widget(columns.at(i));
-                       columns.erase(columns.begin() + i);
-               }
-       }
-
-       /* Zero columns? Just add the "add column" button. Compact column and avoid 
-       empty spaces otherwise. */
-
-       if (columns.size() == 0)
-               addColumnBtn->position(x() - xposition(), y());
-       else {
-               for (size_t i=0; i<columns.size(); i++) {
-                       int pos = i == 0 ? x() - xposition() : columns.at(i-1)->x() + columns.at(i-1)->w() + COLUMN_GAP;
-                       columns.at(i)->position(pos, y());
-               }
-               addColumnBtn->position(columns.back()->x() + columns.back()->w() + COLUMN_GAP, y());
-       }
+       size_t i = u::vector::indexOfIf(m_columns, [=](const geColumn* c) { return c->id == id; });
 
-       refreshColIndexes();
-       redraw();
-}
+       assert(i < m_columns.size());
 
+       /* Delete selected column. */
 
-/* -------------------------------------------------------------------------- */
+       Fl::delete_widget(m_columns.at(i)->resizerBar);
+       Fl::delete_widget(m_columns.at(i));
+       m_columns.erase(m_columns.begin() + i);
 
+       /* Reposition remaining columns and 'add column' button. */
 
-void geKeyboard::cb_addColumn(Fl_Widget* v, void* p)
-{
-       ((geKeyboard*)p)->__cb_addColumn(G_DEFAULT_COLUMN_WIDTH);
+       int px = G_GUI_OUTER_MARGIN;
+       for (geColumn* c : m_columns) {
+               c->position(px, c->y());
+               c->resizerBar->position(c->x() + c->w(), c->resizerBar->y());
+               px = c->resizerBar->x() + c->resizerBar->w();
+       }
+       m_addColumnBtn->position(px, y());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-geChannel* geKeyboard::addChannel(int colIndex, m::Channel* ch, int size, bool build)
+void geKeyboard::deleteAllColumns()
 {
-       geColumn* col = getColumnByIndex(colIndex);
-
-       /* no column with index 'colIndex' found? Just create it and set its index
-       to 'colIndex'. */
-
-       if (!col) {
-               __cb_addColumn();
-               col = columns.back();
-               col->setIndex(colIndex);
-               gu_log("[geKeyboard::addChannel] created new column with index=%d\n", colIndex);
-       }
+       Fl_Scroll::clear();
+       m_columns.clear();
 
-       gu_log("[geKeyboard::addChannel] add to column with index=%d, size=%d\n", 
-               col->getIndex(), size);
-       return col->addChannel(ch, size);
+       m_addColumnBtn = new geButton(8, y(), 200, 20, "Add new column");
+       m_addColumnBtn->callback(cb_addColumn, (void*) this);
+       add(m_addColumnBtn);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::refreshColumns()
+void geKeyboard::cb_addColumn(Fl_Widget* v, void* p)
 {
-       for (unsigned i=0; i<columns.size(); i++)
-               columns.at(i)->refreshChannels();
+       ((geKeyboard*)p)->cb_addColumn(G_DEFAULT_COLUMN_WIDTH);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-geColumn* geKeyboard::getColumnByIndex(int index)
+void geKeyboard::refresh()
 {
-       for (unsigned i=0; i<columns.size(); i++)
-               if (columns.at(i)->getIndex() == index)
-                       return columns.at(i);
-       return nullptr;
+       for (geColumn* c : m_columns)
+               c->refresh();
 }
 
 
@@ -224,6 +193,17 @@ int geKeyboard::handle(int e)
                        dispatcher::dispatchKey(e);
                        return 1;
                }
+               case FL_DND_ENTER:          // return(1) for these events to 'accept' dnd
+               case FL_DND_DRAG:
+               case FL_DND_RELEASE: {
+                       return 1;
+               }
+               case FL_PASTE: {            // handle actual drop (paste) operation
+                       const geColumn* c = getColumnAtCursor(Fl::event_x());
+                       if (c != nullptr)
+                               c::channel::addAndLoadChannels(c->id, getDroppedFilePaths());
+                       return 1;
+               }
        }
        return Fl_Group::handle(e);     // Assume the buttons won't handle the Keyboard events
 }
@@ -232,118 +212,134 @@ int geKeyboard::handle(int e)
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::clear()
+void geKeyboard::draw()
 {
-       for (unsigned i=0; i<columns.size(); i++)
-               Fl::delete_widget(columns.at(i));
-       columns.clear();
-       indexColumn = 0;     // new columns will start from index=0
-       addColumnBtn->position(8, y());
-}
+       Fl_Scroll::draw();
 
+       /* Paint columns background. Use a clip to draw only what's visible. */
 
-/* -------------------------------------------------------------------------- */
+       fl_color(G_COLOR_GREY_1_5);
 
+       fl_push_clip(
+               x(), 
+               y(), 
+               w() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2), 
+               h() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2));
 
-void geKeyboard::setChannelWithActions(geSampleChannel* gch)
-{
-       if (gch->ch->hasActions)
-               gch->showActionButton();
-       else
-               gch->hideActionButton();
+       for (const geColumn* c : m_columns) {
+               fl_rectf(c->x(), c->y() + c->h(), c->w(), h() + yposition());
+               c->resizerBar->size(c->resizerBar->x(), h());
+       }
+
+       fl_pop_clip();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::printChannelMessage(int res)
+geColumn* geKeyboard::cb_addColumn(int width, ID id)
 {
-       if      (res == G_RES_ERR_WRONG_DATA)
-               gdAlert("Multichannel samples not supported.");
-       else if (res == G_RES_ERR_IO)
-               gdAlert("Unable to read this sample.");
-       else if (res == G_RES_ERR_PATH_TOO_LONG)
-               gdAlert("File path too long.");
-       else if (res == G_RES_ERR_NO_DATA)
-               gdAlert("No file specified.");
-       else
-               gdAlert("Unknown error.");
-}
+       int colx = x() - xposition();  // Mind the x-scroll offset with xposition()
 
+       /* If this is not the first column... */
 
-/* -------------------------------------------------------------------------- */
+       if (m_columns.size() > 0)
+               colx = m_columns.back()->x() + m_columns.back()->w() + COLUMN_GAP;
 
+       /* Generate new index. If not passed in. */
 
-void geKeyboard::__cb_addColumn(int width)
-{
-       int colx;
-       int colxw;
-       if (columns.size() == 0) {
-               colx  = x() - xposition();  // mind the offset with xposition()
-               colxw = colx + width;
-       }
-       else {
-               geColumn* prev = columns.back();
-               colx  = prev->x()+prev->w() + COLUMN_GAP;
-               colxw = colx + width;
-       }
+       m_columnId.set(id);
 
-       /* add geColumn to geKeyboard and to columns vector */
+       /* Add a new column + a new resizer bar. */
 
-       geColumn* gc = new geColumn(colx, y(), width, 2000, indexColumn, this);
-  add(gc);
-       columns.push_back(gc);
-       indexColumn++;
+       geResizerBar* bar    = new geResizerBar(colx + width, y(), COLUMN_GAP, h(), G_MIN_COLUMN_WIDTH, geResizerBar::HORIZONTAL);
+       geColumn*     column = new geColumn(colx, y(), width, h(), m_columnId.get(id), bar);
+       add(column);
+       add(bar);
+       m_columns.push_back(column);
 
-       /* move addColumn button */
-
-       addColumnBtn->position(colxw + COLUMN_GAP, y());
-       redraw();
+       /* And then shift the "add column" button on the rightmost edge. */
 
-       gu_log("[geKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n",
-               gc->getIndex(), width, columns.size(), addColumnBtn->x());
+       m_addColumnBtn->position(colx + width + COLUMN_GAP, y());
 
-       /* recompute col indexes */
+       redraw();
 
-       refreshColIndexes();
+       return column;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::addColumn(int width)
+void geKeyboard::addColumn(int width, ID id)
 {
-       __cb_addColumn(width);
+       cb_addColumn(width, id);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::refreshColIndexes()
+void geKeyboard::forEachChannel(std::function<void(geChannel* c)> f) const
+{
+       for (geColumn* column : m_columns) 
+               column->forEachChannel(f);
+}
+
+
+void geKeyboard::forEachColumn(std::function<void(const geColumn& c)> f) const
 {
-       for (unsigned i=0; i<columns.size(); i++)
-               columns.at(i)->setIndex(i);
+       for (geColumn* column : m_columns) 
+               f(*column);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geKeyboard::getColumnWidth(int i)
+geColumn* geKeyboard::getColumn(ID id)
+{
+       for (geColumn* c : m_columns) 
+               if (c->id == id)
+                       return c;
+       return nullptr;
+}
+
+
+geColumn* geKeyboard::getColumnAtCursor(Pixel px)
 {
-  return getColumnByIndex(i)->w();
+       px += xposition();
+       for (geColumn* c : m_columns)
+               if (px > c->x() && px <= c->x() + c->w())
+                       return c;
+       return nullptr;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-geColumn* geKeyboard::getColumn(int i)
+geChannel* geKeyboard::getChannel(ID channelId)
 {
-  return columns.at(i);
+       for (geColumn* column : m_columns) {
+               geChannel* c = column->getChannel(channelId);
+               if (c != nullptr) 
+                       return c;
+       }
+       assert(false);
+       return nullptr;
 }
 
-}} // giada::v::
\ No newline at end of file
+
+/* -------------------------------------------------------------------------- */
+
+
+std::vector<std::string> geKeyboard::getDroppedFilePaths() const
+{                      
+       std::vector<std::string> paths = u::string::split(Fl::event_text(), "\n");
+               for (std::string& p : paths)
+                       p = u::fs::stripFileUrl(p);
+       return paths;
+}
+}} // giada::v::
index daac4c8352ba282c734d71a97bc5555e301d5318..5c2e6be1dd15261e03d9906f4a02c77482c180a1 100644 (file)
 
 #include <vector>
 #include <FL/Fl_Scroll.H>
-#include "../../../../core/const.h"
-#include "../../../../core/channel.h"
+#include "core/channels/channel.h"
+#include "core/idManager.h"
+#include "core/const.h"
 
 
 class geButton;
 class geColumn;
-class geChannel;
-class geSampleChannel;
+class geResizerBar;
 
 
 namespace giada {
 namespace v
 {
+class geChannel;
+class geSampleChannel;
+
 class geKeyboard : public Fl_Scroll
 {
-private:
-
-       static const int COLUMN_GAP = 16;
-
-       /* refreshColIndexes
-        * Recompute all column indexes in order to avoid any gaps between them.
-        * Indexes must always be contiguous! */
-
-       void refreshColIndexes();
-
-       static void cb_addColumn  (Fl_Widget* v, void* p);
-       inline void __cb_addColumn(int width=G_DEFAULT_COLUMN_WIDTH);
-
-       /* indexColumn
-        * the last index used for column. */
-
-       static int indexColumn;
-
-       geButton* addColumnBtn;
-
-       /* columns
-        * a vector of columns which in turn contain channels. */
-
-       std::vector<geColumn*> columns;
-
 public:
 
        geKeyboard(int X, int Y, int W, int H);
 
-       int handle(int e);
+       int handle(int e) override;
+       void draw() override;
 
-       /* init
-        * build the initial setup of empty channels. */
+       /* rebuild
+       Rebuilds this widget from scratch. Used when the model has changed. */
 
-       void init();
+       void rebuild();
 
-       /* addChannel
-       Adds a new channel to geChannels. Used by callbacks and during patch loading. 
-       Requires Channel (and not geChannel). If build is set to true, also generate 
-       the corresponding column if column (index) does not exist yet. */
+       /* refresh
+       Refreshes each column's channel, called on each GUI cycle. */
 
-       geChannel* addChannel(int column, giada::m::Channel* ch, int size, bool build=false);
+       void refresh();
 
        /* addColumn
-        * add a new column to the top of the stack. */
-
-       void addColumn(int width=380);
-
-       /* deleteChannel
-        * delete a channel from geChannels<> where geChannel->ch == ch and remove
-        * it from the stack. */
-
-       void deleteChannel(geChannel* gch);
+       Adds a new column on the top of the stack. */
 
-       /* freeChannel
-        * free a channel from geChannels<> where geChannel->ch == ch. No channels
-        * are deleted */
+       void addColumn(int width=G_DEFAULT_COLUMN_WIDTH, ID id=0);
 
-       void freeChannel(geChannel* gch);
+       /* deleteColumn
+       Deletes column by id. */
+       
+       void deleteColumn(ID id);
 
-       /* updateChannel
-        * wrapper function to call gch->update(). */
+       /* deleteAllColumns
+       Deletes all columns from the stack. */
 
-       void updateChannel(geChannel* gch);
+       void deleteAllColumns();
 
-       /* organizeColumns
-        * reorganize columns layout by removing empty gaps. */
+       /* getChannel
+       Given a channel ID returns the UI channel it belongs to. */
 
-       void organizeColumns();
+       geChannel* getChannel(ID channelId);
 
-       /* refreshColumns
-        * refresh each column's channel, called on each GUI cycle. */
-
-       void refreshColumns();
+       /* init
+       Builds the default setup of empty columns. */
 
-       /* getColumnByIndex
-        * return the column with index 'index', or nullptr if not found. */
+       void init();
 
-       geColumn* getColumnByIndex(int index);
+       void forEachChannel(std::function<void(geChannel* c)> f) const;
+       void forEachColumn(std::function<void(const geColumn& c)> f) const;
 
-       /* getColumn
-        * return the column with from columns->at(i). */
-
-       geColumn* getColumn(int i);
+private:
 
-       /* clear
-        * delete all channels and groups. */
+       static const int COLUMN_GAP = 20;
 
-       void clear();
+       static void cb_addColumn(Fl_Widget* v, void* p);
+       geColumn* cb_addColumn(int width=G_DEFAULT_COLUMN_WIDTH, ID id=0);
 
-       /* setChannelWithActions
-        * add 'R' button if channel has actions, and set recorder to active. */
+       /* getDroppedFilePaths
+       Returns a vector of audio file paths after a drag-n-drop from desktop
+       event. */
 
-       void setChannelWithActions(geSampleChannel* gch);
+       std::vector<std::string> getDroppedFilePaths() const;
 
-       /* printChannelMessage
-        * given any output by glue_loadChannel, print the message on screen
-        * on a gdAlert subwindow. */
+       /* getColumn
+       Returns the column given the ID. */
 
-       void printChannelMessage(int res);
+       geColumn* getColumn(ID id);
 
-       /* getTotalColumns */
+       /* getColumnAtCursor
+       Returns the column below the cursor. */
 
-       unsigned getTotalColumns() { return columns.size(); }
+       geColumn* getColumnAtCursor(Pixel x);
 
-       /* getColumnWidth
-        * return the width in pixel of i-th column. Warning: 'i' is the i-th column
-        * in the column array, NOT the index. */
+       m::IdManager m_columnId;
+       std::vector<geColumn*> m_columns;
 
-       int getColumnWidth(int i);
+       geButton* m_addColumnBtn;
 };
 }} // giada::v::
 
index 862b1727f084aa9a97e469b60d0c630068bf41c2..e8c2f55fe6863ead7876f97202bcfc06d4635370 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl_Menu_Button.H>
-#include "../../../../core/const.h"
-#include "../../../../core/graphics.h"
-#include "../../../../core/midiChannel.h"
-#include "../../../../core/recorder.h"
-#include "../../../../utils/gui.h"
-#include "../../../../utils/string.h"
-#include "../../../../glue/channel.h"
-#include "../../../../glue/io.h"
-#include "../../../../glue/recorder.h"
-#include "../../../dispatcher.h"
-#include "../../../dialogs/mainWindow.h"
-#include "../../../dialogs/channelNameInput.h"
-#include "../../../dialogs/warnings.h"
-#include "../../../dialogs/keyGrabber.h"
-#include "../../../dialogs/pluginList.h"
-#include "../../../dialogs/actionEditor/midiActionEditor.h"
-#include "../../../dialogs/midiIO/midiInputChannel.h"
-#include "../../../dialogs/midiIO/midiOutputMidiCh.h"
-#include "../../basics/boxtypes.h"
-#include "../../basics/button.h"
-#include "../../basics/statusButton.h"
-#include "../../basics/dial.h"
+#include "core/const.h"
+#include "core/graphics.h"
+#include "core/channels/midiChannel.h"
+#include "core/model/model.h"
+#include "core/recorder.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "glue/channel.h"
+#include "glue/io.h"
+#include "glue/recorder.h"
+#include "gui/dispatcher.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/channelNameInput.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/keyGrabber.h"
+#include "gui/dialogs/pluginList.h"
+#include "gui/dialogs/actionEditor/midiActionEditor.h"
+#include "gui/dialogs/midiIO/midiInputChannel.h"
+#include "gui/dialogs/midiIO/midiOutputMidiCh.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/basics/dial.h"
 #include "column.h"
 #include "midiChannelButton.h"
 #include "midiChannel.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using namespace giada;
-using std::string;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
+namespace giada {
+namespace v
+{
 namespace
 {
 enum class Menu
@@ -71,12 +72,12 @@ enum class Menu
        SETUP_KEYBOARD_INPUT,
        SETUP_MIDI_INPUT,
        SETUP_MIDI_OUTPUT,
-       RESIZE,
+       /*RESIZE,
        RESIZE_H1,
        RESIZE_H2,
        RESIZE_H3,
        RESIZE_H4,
-       __END_RESIZE_SUBMENU__,
+       __END_RESIZE_SUBMENU__,*/
        RENAME_CHANNEL,
        CLONE_CHANNEL,
        DELETE_CHANNEL
@@ -88,10 +89,7 @@ enum class Menu
 
 void menuCallback(Fl_Widget* w, void* v)
 {
-       using namespace giada;
-
-       geMidiChannel*  gch = static_cast<geMidiChannel*>(w);
-       m::MidiChannel* ch  = static_cast<m::MidiChannel*>(gch->ch);
+       geMidiChannel* gch = static_cast<geMidiChannel*>(w);
 
        Menu selectedItem = (Menu) (intptr_t) v;
 
@@ -99,91 +97,88 @@ void menuCallback(Fl_Widget* w, void* v)
        {
                case Menu::CLEAR_ACTIONS:
                case Menu::__END_CLEAR_ACTION_SUBMENU__:
-               case Menu::RESIZE:
-               case Menu::__END_RESIZE_SUBMENU__:
+               /*case Menu::RESIZE:
+               case Menu::__END_RESIZE_SUBMENU__:*/
                        break;
                case Menu::EDIT_ACTIONS:
-                       u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(ch), WID_ACTION_EDITOR);
+                       u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(gch->channelId), WID_ACTION_EDITOR);
                        break;
                case Menu::CLEAR_ACTIONS_ALL:
-                       c::recorder::clearAllActions(gch);
+                       c::recorder::clearAllActions(gch->channelId);
                        break;
                case Menu::SETUP_KEYBOARD_INPUT:
-                       u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0);
+                       u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(gch->channelId), WID_KEY_GRABBER);
                        break;
                case Menu::SETUP_MIDI_INPUT:
-                       u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+                       u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->channelId), WID_MIDI_INPUT);
                        break;
                case Menu::SETUP_MIDI_OUTPUT:
-                       u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(ch), 0);
+                       u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(gch->channelId), WID_MIDI_OUTPUT);
                        break;
-               case Menu::RESIZE_H1:
+               /*case Menu::RESIZE_H1:
                        gch->changeSize(G_GUI_CHANNEL_H_1);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;          
                case Menu::RESIZE_H2:
                        gch->changeSize(G_GUI_CHANNEL_H_2);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;          
                case Menu::RESIZE_H3:
                        gch->changeSize(G_GUI_CHANNEL_H_3);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;          
                case Menu::RESIZE_H4:
                        gch->changeSize(G_GUI_CHANNEL_H_4);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
-                       break;
+                       break;*/
                case Menu::CLONE_CHANNEL:
-                       c::channel::cloneChannel(gch->ch);
+                       c::channel::cloneChannel(gch->channelId);
                        break;          
                case Menu::RENAME_CHANNEL:
-                       u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->ch), WID_SAMPLE_NAME);
+                       u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->channelId), WID_SAMPLE_NAME);
                        break;
                case Menu::DELETE_CHANNEL:
-                       c::channel::deleteChannel(gch->ch);
+                       c::channel::deleteChannel(gch->channelId);
                        break;
        }
 }
-
-}; // {namespace}
+} // {anonymous}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-geMidiChannel::geMidiChannel(int X, int Y, int W, int H, m::MidiChannel* ch)
-       : geChannel(X, Y, W, H, ch)
+geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
+       : geChannel(X, Y, W, H, channelId)
 {
-       begin();
-
 #if defined(WITH_VST)
-       int delta = 144; // (6 widgets * G_GUI_UNIT) + (6 paddings * 4)
+       const int delta = 144; // (6 widgets * G_GUI_UNIT) + (6 paddings * 4)
 #else
-       int delta = 120; // (5 widgets * G_GUI_UNIT) + (5 paddings * 4)
+       const int delta = 120; // (5 widgets * G_GUI_UNIT) + (5 paddings * 4)
 #endif
 
-       button     = new geButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, "", channelStop_xpm, channelPlay_xpm);
-       arm        = new geButton(button->x()+button->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
-       mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, H, "-- MIDI --");
-       mute       = new geButton(mainButton->x()+mainButton->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", muteOff_xpm, muteOn_xpm);
-       solo       = new geButton(mute->x()+mute->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", soloOff_xpm, soloOn_xpm);
+       playButton = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
+       arm        = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
+       mainButton = new geMidiChannelButton(0, 0, w() - delta, H, channelId);
+       mute       = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
+       solo       = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
 #if defined(WITH_VST)
-       fx         = new geStatusButton(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
-       vol        = new geDial(fx->x()+fx->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT);
-#else
-       vol        = new geDial(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT);
+       fx         = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
 #endif
+       vol        = new geDial(0, 0, G_GUI_UNIT, G_GUI_UNIT);
 
        end();
 
        resizable(mainButton);
 
-       update();
+       m::model::ChannelsLock l(m::model::channels);
+       const m::Channel& ch = m::model::get(m::model::channels, channelId);
+
+#ifdef WITH_VST
+       fx->setStatus(ch.pluginIds.size() > 0);
+#endif
 
-       button->callback(cb_button, (void*)this);
-       button->when(FL_WHEN_CHANGED);   // do callback on keypress && on keyrelease
+       playButton->callback(cb_playButton, (void*)this);
+       playButton->when(FL_WHEN_CHANGED);   // On keypress && on keyrelease
 
        arm->type(FL_TOGGLE_BUTTON);
+       arm->value(ch.armed);
        arm->callback(cb_arm, (void*)this);
 
 #ifdef WITH_VST
@@ -196,12 +191,12 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, m::MidiChannel* ch)
        solo->type(FL_TOGGLE_BUTTON);
        solo->callback(cb_solo, (void*)this);
 
+       mainButton->setKey(ch.key);
        mainButton->callback(cb_openMenu, (void*)this);
 
+       vol->value(ch.volume);
        vol->callback(cb_changeVol, (void*)this);
 
-       ch->guiChannel = this;
-
        changeSize(H);  // Update size dynamically
 }
 
@@ -209,16 +204,16 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, m::MidiChannel* ch)
 /* -------------------------------------------------------------------------- */
 
 
-void geMidiChannel::cb_button  (Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb_button(); }
+void geMidiChannel::cb_playButton(Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb_playButton(); }
 void geMidiChannel::cb_openMenu(Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb_openMenu(); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geMidiChannel::cb_button()
+void geMidiChannel::cb_playButton()
 {
-       v::dispatcher::dispatchTouch(ch, button->value());
+       v::dispatcher::dispatchTouch(this, playButton->value());
 }
 
 
@@ -235,12 +230,12 @@ void geMidiChannel::cb_openMenu()
                {"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},
-               {"Resize",    0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU},
+               /*{"Resize",    0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU},
                        {"Normal",  0, menuCallback, (void*) Menu::RESIZE_H1},
                        {"Medium",  0, menuCallback, (void*) Menu::RESIZE_H2},
                        {"Large",   0, menuCallback, (void*) Menu::RESIZE_H3},
                        {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4},
-                       {0},
+                       {0},*/
                {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL},
                {"Clone",  0, menuCallback, (void*) Menu::CLONE_CHANNEL},
                {"Delete", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
@@ -249,17 +244,20 @@ void geMidiChannel::cb_openMenu()
 
        /* No 'clear actions' if there are no actions. */
 
-       if (!ch->hasActions)
-               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);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
-
-       const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (m)
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               if (!c.hasActions)
+                       rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate();
+       });
+
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
+
+       const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
+       if (m != nullptr)
                m->do_callback(this, m->user_data());
        return;
 }
@@ -268,61 +266,6 @@ void geMidiChannel::cb_openMenu()
 /* -------------------------------------------------------------------------- */
 
 
-void geMidiChannel::refresh()
-{
-       setColorsByStatus();
-       if (m::recorder::isActive() && ch->armed)
-               mainButton->setActionRecordMode();
-       mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiChannel::reset()
-{
-       mainButton->setDefaultMode("-- MIDI --");
-       mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiChannel::update()
-{
-       const m::MidiChannel* mch = static_cast<const m::MidiChannel*>(ch);
-
-       string label; 
-       if (mch->name.empty())
-               label = "-- MIDI --";
-       else
-               label = mch->name.c_str();
-
-       if (mch->midiOut) 
-               label += " (ch " + u::string::iToString(mch->midiOutChan + 1) + " out)";
-
-       mainButton->label(label.c_str());
-
-       vol->value(mch->volume);
-       mute->value(mch->mute);
-       solo->value(mch->solo);
-
-       mainButton->setKey(mch->key);
-
-       arm->value(mch->armed);
-
-#ifdef WITH_VST
-       fx->status = mch->plugins.size() > 0;
-       fx->redraw();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void geMidiChannel::resize(int X, int Y, int W, int H)
 {
        geChannel::resize(X, Y, W, H);
@@ -340,4 +283,6 @@ void geMidiChannel::resize(int X, int Y, int W, int H)
 #endif
 
        packWidgets();
-}
\ No newline at end of file
+}
+
+}} // giada::v::
index e917506e27cc923cb3ddd4bf277a6ec587586657..7004c909cb4b7413dc3c8aba7454696c87436da2 100644 (file)
 #define GE_MIDI_CHANNEL_H
 
 
-#include "../../../../core/midiChannel.h"
 #include "channel.h"
 #include "channelButton.h"
 
 
+namespace giada {
+namespace m
+{
+class MidiChannel;
+}
+namespace v
+{
 class geMidiChannel : public geChannel
 {
-private:
-
-       static void cb_button(Fl_Widget* v, void* p);
-       static void cb_openMenu(Fl_Widget* v, void* p);
-       void cb_button();
-       void cb_openMenu();
-
 public:
 
-       geMidiChannel(int x, int y, int w, int h, giada::m::MidiChannel* ch);
+    geMidiChannel(int x, int y, int w, int h, ID channelId);
 
-       void resize(int x, int y, int w, int h) override;
+    void resize(int x, int y, int w, int h) override;
 
-       void reset() override;
-       void update() override;
-       void refresh() override;
+private:
 
-       int keyPress(int event);  // TODO - move to base class
+       static void cb_playButton(Fl_Widget* v, void* p);
+       static void cb_openMenu(Fl_Widget* v, void* p);
+       void cb_playButton();
+       void cb_openMenu();
 };
+}} // giada::v::
 
 
 #endif
index 4308d5932bce1baed902572edade5965796e87c4..d5caf60571f95606c09903e5d74c1ed197082937 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include "utils/string.h"
+#include "core/channels/midiChannel.h"
+#include "core/model/model.h"
+#include "core/recManager.h"
 #include "midiChannelButton.h"
 
 
-geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const char* l)
-       : geChannelButton(x, y, w, h, l)
+namespace giada {
+namespace v
 {
+geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, ID channelId)
+: geChannelButton(x, y, w, h, channelId)
+{
+    std::string l; 
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               const m::MidiChannel& mc = static_cast<m::MidiChannel&>(c);
+               if (mc.name.empty())
+                       l = "-- MIDI --";
+               else
+                       l = mc.name.c_str();
+
+               if (mc.midiOut) 
+                       l += " (ch " + u::string::iToString(mc.midiOutChan + 1) + " out)";
+       });
+
+    label(l.c_str());
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geMidiChannelButton::handle(int e)
+void geMidiChannelButton::refresh()
 {
-       // Currently MIDI drag-n-drop does nothing.
-       return geButton::handle(e);
+       geChannelButton::refresh();
+
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               if (m::recManager::isRecordingAction() && c.armed)
+                       setActionRecordMode();
+       });
+       
+       redraw();
 }
+}} // giada::v::
index 225aa327026bb1b51beca9208f0e9d503525b0d0..2600720e33d2d96d0774f846474af576a2c47b1e 100644 (file)
 #include "channelButton.h"
 
 
+namespace giada {
+namespace m 
+{ 
+class MidiChannel; 
+}
+namespace v
+{
 class geMidiChannelButton : public geChannelButton
 {
 public:
-       geMidiChannelButton(int x, int y, int w, int h, const char* l=0);
-       int handle(int e);
+
+       geMidiChannelButton(int x, int y, int w, int h, ID channelId);
+       
+       void refresh() override;
 };
+}} // giada::v::
 
 
-#endif
\ No newline at end of file
+#endif
index 2bb03a02e15ae71807bf0b123f25a1505234fff1..3f4404001e4e4c143fa5cb9e30d1333ae06dab34 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../../core/mixer.h"
-#include "../../../../core/conf.h"
-#include "../../../../core/clock.h"
-#include "../../../../core/graphics.h"
-#include "../../../../core/wave.h"
-#include "../../../../core/recorder.h"
-#include "../../../../core/sampleChannel.h"
-#include "../../../../glue/io.h"
-#include "../../../../glue/channel.h"
-#include "../../../../glue/recorder.h"
-#include "../../../../glue/storage.h"
-#include "../../../../utils/gui.h"
-#include "../../../dispatcher.h"
-#include "../../../dialogs/mainWindow.h"
-#include "../../../dialogs/keyGrabber.h"
-#include "../../../dialogs/sampleEditor.h"
-#include "../../../dialogs/channelNameInput.h"
-#include "../../../dialogs/warnings.h"
-#include "../../../dialogs/actionEditor/sampleActionEditor.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/button.h"
-#include "../../basics/statusButton.h"
-#include "../../basics/dial.h"
+#include <cassert>
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/mixer.h"
+#include "core/conf.h"
+#include "core/clock.h"
+#include "core/graphics.h"
+#include "core/wave.h"
+#include "core/recorder.h"
+#include "core/recManager.h"
+#include "glue/io.h"
+#include "glue/channel.h"
+#include "glue/recorder.h"
+#include "glue/storage.h"
+#include "utils/gui.h"
+#include "gui/dispatcher.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/keyGrabber.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/channelNameInput.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/actionEditor/sampleActionEditor.h"
+#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/browser/browserLoad.h"
+#include "gui/dialogs/midiIO/midiOutputSampleCh.h"
+#include "gui/dialogs/midiIO/midiInputChannel.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/basics/dial.h"
 #include "channelStatus.h"
 #include "channelMode.h"
 #include "sampleChannelButton.h"
 #include "sampleChannel.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using namespace giada;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
+namespace giada {
+namespace v
+{
 namespace
 {
 enum class Menu
@@ -83,12 +86,12 @@ enum class Menu
        CLEAR_ACTIONS_VOLUME,
        CLEAR_ACTIONS_START_STOP,
        __END_CLEAR_ACTIONS_SUBMENU__,
-       RESIZE,
+       /*RESIZE,
        RESIZE_H1,
        RESIZE_H2,
        RESIZE_H3,
        RESIZE_H4,
-       __END_RESIZE_SUBMENU__,
+       __END_RESIZE_SUBMENU__,*/
        RENAME_CHANNEL,
        CLONE_CHANNEL,
        FREE_CHANNEL,
@@ -101,109 +104,113 @@ enum class Menu
 
 void menuCallback(Fl_Widget* w, void* v)
 {
-       using namespace giada;
+       geSampleChannel* gch = static_cast<geSampleChannel*>(w);
 
-       geSampleChannel*  gch = static_cast<geSampleChannel*>(w);
-       m::SampleChannel* ch  = static_cast<m::SampleChannel*>(gch->ch);
+       ID    waveId;
+       bool inputMonitor;
+       m::model::onGet(m::model::channels, gch->channelId, [&](m::Channel& c)
+       {
+               waveId       = static_cast<m::SampleChannel&>(c).waveId;
+               inputMonitor = static_cast<m::SampleChannel&>(c).inputMonitor;
+       });
 
        Menu selectedItem = (Menu) (intptr_t) v;
 
        switch (selectedItem) {
                case Menu::INPUT_MONITOR: {
-                       c::channel::toggleInputMonitor(gch->ch);
+                       c::channel::setInputMonitor(gch->channelId, !inputMonitor);
                        break;
                }
                case Menu::LOAD_SAMPLE: {
-                       gdWindow *w = new gdBrowserLoad(m::conf::browserX, m::conf::browserY,
-                               m::conf::browserW, m::conf::browserH, "Browse sample",
-                               m::conf::samplePath.c_str(), c::storage::loadSample, gch->ch);
+                       gdWindow* w = new gdBrowserLoad("Browse sample", 
+                               m::conf::samplePath.c_str(), c::storage::loadSample, gch->channelId);
                        u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
                        break;
                }
                case Menu::EXPORT_SAMPLE: {
-                       gdWindow *w = new gdBrowserSave(m::conf::browserX, m::conf::browserY,
-                               m::conf::browserW, m::conf::browserH, "Save sample",
-                               m::conf::samplePath.c_str(), "", c::storage::saveSample, gch->ch);
+                       gdWindow* w = new gdBrowserSave("Save sample", 
+                               m::conf::samplePath.c_str(), "", c::storage::saveSample, gch->channelId);
                        u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
                        break;
                }
                case Menu::SETUP_KEYBOARD_INPUT: {
-                       new gdKeyGrabber(gch->ch); // FIXME - use gu_openSubWindow
+                       u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(gch->channelId), 
+                               WID_KEY_GRABBER);
                        break;
                }
                case Menu::SETUP_MIDI_INPUT: {
-                       u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+                       u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->channelId), 
+                               WID_MIDI_INPUT);
                        break;
                }
                case Menu::SETUP_MIDI_OUTPUT: {
-                       u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(ch), 0);
+                       u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(gch->channelId), 
+                               WID_MIDI_OUTPUT);
                        break;
                }
                case Menu::EDIT_SAMPLE: {
-                       u::gui::openSubWindow(G_MainWin, new gdSampleEditor(ch), WID_SAMPLE_EDITOR);
+                       u::gui::openSubWindow(G_MainWin, new gdSampleEditor(gch->channelId, waveId), 
+                               WID_SAMPLE_EDITOR);
                        break;
                }
                case Menu::EDIT_ACTIONS: {
-                       u::gui::openSubWindow(G_MainWin, new v::gdSampleActionEditor(ch), WID_ACTION_EDITOR);
+                       u::gui::openSubWindow(G_MainWin, new gdSampleActionEditor(gch->channelId), 
+                               WID_ACTION_EDITOR);
                        break;
                }
                case Menu::CLEAR_ACTIONS:
-               case Menu::RESIZE:
                case Menu::__END_CLEAR_ACTIONS_SUBMENU__:
-               case Menu::__END_RESIZE_SUBMENU__:
+               //case Menu::RESIZE:
+               //case Menu::__END_RESIZE_SUBMENU__:
                        break;
                case Menu::CLEAR_ACTIONS_ALL: {
-                       c::recorder::clearAllActions(gch);
+                       c::recorder::clearAllActions(gch->channelId);
                        break;
                }
                case Menu::CLEAR_ACTIONS_VOLUME: {
-                       c::recorder::clearVolumeActions(gch);
+                       c::recorder::clearVolumeActions(gch->channelId);
                        break;
                }
                case Menu::CLEAR_ACTIONS_START_STOP: {
-                       c::recorder::clearStartStopActions(gch);
+                       c::recorder::clearStartStopActions(gch->channelId);
                        break;
                }
-               case Menu::RESIZE_H1: {
+               /*case Menu::RESIZE_H1: {
                        gch->changeSize(G_GUI_CHANNEL_H_1);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;
                }               
                case Menu::RESIZE_H2: {
                        gch->changeSize(G_GUI_CHANNEL_H_2);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;
                }               
                case Menu::RESIZE_H3: {
                        gch->changeSize(G_GUI_CHANNEL_H_3);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;
                }               
                case Menu::RESIZE_H4: {
                        gch->changeSize(G_GUI_CHANNEL_H_4);
-                       static_cast<geColumn*>(gch->parent())->repositionChannels();
                        break;
-               }
+               }*/
                case Menu::CLONE_CHANNEL: {
-                       c::channel::cloneChannel(gch->ch);
+                       c::channel::cloneChannel(gch->channelId);
                        break;
                }
                case Menu::RENAME_CHANNEL: {
-                       u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->ch), WID_SAMPLE_NAME);
+                       u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->channelId), 
+                               WID_SAMPLE_NAME);
                        break;
                }
                case Menu::FREE_CHANNEL: {
-                       c::channel::freeChannel(gch->ch);
+                       c::channel::freeChannel(gch->channelId);
                        break;
                }
                case Menu::DELETE_CHANNEL: {
-                       c::channel::deleteChannel(gch->ch);
+                       c::channel::deleteChannel(gch->channelId);
                        break;
                }
        }
 }
-
-}; // {namespace}
+} // {anonymous}
 
 
 /* -------------------------------------------------------------------------- */
@@ -211,36 +218,41 @@ void menuCallback(Fl_Widget* w, void* v)
 /* -------------------------------------------------------------------------- */
 
 
-geSampleChannel::geSampleChannel(int X, int Y, int W, int H, m::SampleChannel* ch)
-       : geChannel(X, Y, W, H, ch)
+geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
+       : geChannel(X, Y, W, H, channelId)
 {
-       begin();
-
-       button      = new geButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, "", channelStop_xpm, channelPlay_xpm);
-       arm         = new geButton(button->x()+button->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
-       status      = new geChannelStatus(arm->x()+arm->w()+4, y(), G_GUI_UNIT, H, ch);
-       mainButton  = new geSampleChannelButton(status->x()+status->w()+4, y(), G_GUI_UNIT, H, "-- no sample --");
-       readActions = new geButton(mainButton->x()+mainButton->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", readActionOff_xpm, readActionOn_xpm);
-       modeBox     = new geChannelMode(readActions->x()+readActions->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, ch);
-       mute        = new geButton(modeBox->x()+modeBox->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", muteOff_xpm, muteOn_xpm);
-       solo        = new geButton(mute->x()+mute->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", soloOff_xpm, soloOn_xpm);
+       playButton  = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
+       arm         = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
+       status      = new geChannelStatus(0, 0, G_GUI_UNIT, H, channelId);
+       mainButton  = new geSampleChannelButton(0, 0, G_GUI_UNIT, H, channelId);
+       readActions = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, readActionOff_xpm, readActionOn_xpm);
+       modeBox     = new geChannelMode(0, 0, G_GUI_UNIT, G_GUI_UNIT, channelId);
+       mute        = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
+       solo        = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
 #ifdef WITH_VST
-       fx          = new geStatusButton(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
-       vol         = new geDial(fx->x()+fx->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT);
-#else
-       vol         = new geDial(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT);
+       fx          = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
 #endif
+       vol         = new geDial(0, 0, G_GUI_UNIT, G_GUI_UNIT);
 
        end();
 
        resizable(mainButton);
 
-       update();
+       m::model::ChannelsLock l(m::model::channels);
+       const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, channelId));
+
+       modeBox->value(static_cast<int>(ch.mode));
+       modeBox->redraw();
 
-       button->callback(cb_button, (void*)this);
-       button->when(FL_WHEN_CHANGED);   // do callback on keypress && on keyrelease
+#ifdef WITH_VST
+       fx->setStatus(ch.pluginIds.size() > 0);
+#endif
+
+       playButton->callback(cb_playButton, (void*)this);
+       playButton->when(FL_WHEN_CHANGED);   // On keypress && on keyrelease
 
        arm->type(FL_TOGGLE_BUTTON);
+       arm->value(ch.armed);
        arm->callback(cb_arm, (void*)this);
 
 #ifdef WITH_VST
@@ -253,16 +265,17 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, m::SampleChannel* c
        solo->type(FL_TOGGLE_BUTTON);
        solo->callback(cb_solo, (void*)this);
 
+       mainButton->setKey(ch.key);
        mainButton->callback(cb_openMenu, (void*)this);
 
-       readActions->type(FL_TOGGLE_BUTTON);
-       readActions->value(ch->readActions);
+       //readActions->type(FL_TOGGLE_BUTTON);
+       //readActions->value(ch.readActions);
+       readActions->setStatus(ch.readActions);
        readActions->callback(cb_readActions, (void*)this);
 
+       vol->value(ch.volume);
        vol->callback(cb_changeVol, (void*)this);
 
-       ch->guiChannel = this;
-
        changeSize(H);  // Update size dynamically
 }
 
@@ -270,7 +283,7 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, m::SampleChannel* c
 /* -------------------------------------------------------------------------- */
 
 
-void geSampleChannel::cb_button     (Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_button(); }
+void geSampleChannel::cb_playButton (Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_playButton(); }
 void geSampleChannel::cb_openMenu   (Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_openMenu(); }
 void geSampleChannel::cb_readActions(Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_readActions(); }
 
@@ -278,9 +291,9 @@ void geSampleChannel::cb_readActions(Fl_Widget* v, void* p) { ((geSampleChannel*
 /* -------------------------------------------------------------------------- */
 
 
-void geSampleChannel::cb_button()
+void geSampleChannel::cb_playButton()
 {
-       v::dispatcher::dispatchTouch(ch, button->value());
+       v::dispatcher::dispatchTouch(this, playButton->value());
 }
 
 
@@ -289,17 +302,28 @@ void geSampleChannel::cb_button()
 
 void geSampleChannel::cb_openMenu()
 {
-       using namespace giada;
+       bool inputMonitor;
+       bool isEmptyOrMissing;
+       bool hasActions;
+       bool isAnyLoopMode;
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               inputMonitor     = sc.inputMonitor;
+               isEmptyOrMissing = sc.playStatus == ChannelStatus::EMPTY || sc.playStatus == ChannelStatus::MISSING;
+               hasActions       = sc.hasActions;
+               isAnyLoopMode    = sc.isAnyLoopMode();
+       });
 
        /* If you're recording (input or actions) no menu is allowed; you can't do
        anything, especially deallocate the channel */
 
-       if (m::mixer::recording || m::recorder::isActive())
+       if (m::recManager::isRecording())
                return;
 
        Fl_Menu_Item rclick_menu[] = {
                {"Input monitor",            0, menuCallback, (void*) Menu::INPUT_MONITOR,
-                       FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast<m::SampleChannel*>(ch)->inputMonitor ? FL_MENU_VALUE : 0)},
+                       FL_MENU_TOGGLE | FL_MENU_DIVIDER | (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},
@@ -312,12 +336,12 @@ void geSampleChannel::cb_openMenu()
                        {"Volume",     0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME},
                        {"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP},
                        {0},
-               {"Resize",    0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU},
+/*             {"Resize",    0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU},
                        {"Normal",  0, menuCallback, (void*) Menu::RESIZE_H1},
                        {"Medium",  0, menuCallback, (void*) Menu::RESIZE_H2},
                        {"Large",   0, menuCallback, (void*) Menu::RESIZE_H3},
                        {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4},
-                       {0},
+                       {0},*/
                {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL},
                {"Clone",  0, menuCallback, (void*) Menu::CLONE_CHANNEL},
                {"Free",   0, menuCallback, (void*) Menu::FREE_CHANNEL},
@@ -325,33 +349,31 @@ void geSampleChannel::cb_openMenu()
                {0}
        };
 
-       if (ch->status == ChannelStatus::EMPTY || 
-                 ch->status == ChannelStatus::MISSING) 
-       {
+       if (isEmptyOrMissing) {
                rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate();
                rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate();
                rclick_menu[(int) Menu::FREE_CHANNEL].deactivate();
                rclick_menu[(int) Menu::RENAME_CHANNEL].deactivate();
        }
 
-       if (!ch->hasActions)
+       if (!hasActions)
                rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate();
 
 
        /* No 'clear start/stop actions' for those channels in loop mode: they cannot
        have start/stop actions. */
 
-       if (static_cast<m::SampleChannel*>(ch)->isAnyLoopMode())
+       if (isAnyLoopMode)
                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);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
 
-       const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (m)
+       const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
+       if (m != nullptr)
                m->do_callback(this, m->user_data());
        return;
 }
@@ -362,8 +384,7 @@ void geSampleChannel::cb_openMenu()
 
 void geSampleChannel::cb_readActions()
 {
-       using namespace giada::c::channel;
-       toggleReadingActions(static_cast<m::SampleChannel*>(ch));
+       c::channel::toggleReadingActions(channelId);
 }
 
 
@@ -372,106 +393,21 @@ void geSampleChannel::cb_readActions()
 
 void geSampleChannel::refresh()
 {
-       using namespace giada;
-       
-       if (!mainButton->visible()) // mainButton invisible? status too (see below)
-               return;
-
-       setColorsByStatus();
-
-       if (static_cast<m::SampleChannel*>(ch)->wave != nullptr) {
-               if (m::mixer::recording && ch->armed)
-                       mainButton->setInputRecordMode();
-               if (m::recorder::isActive())
-                       mainButton->setActionRecordMode();
-               status->redraw(); // status invisible? sampleButton too (see below)
-       }
-       mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geSampleChannel::reset()
-{
-       hideActionButton();
-       mainButton->setDefaultMode("-- no sample --");
-       mainButton->redraw();
-       status->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geSampleChannel::update()
-{
-       const m::SampleChannel* sch = static_cast<const m::SampleChannel*>(ch);
-
-       switch (sch->status) {
-               case ChannelStatus::EMPTY:
-                       mainButton->label("-- no sample --");
-                       break;
-               case ChannelStatus::MISSING:
-               case ChannelStatus::WRONG:
-                       mainButton->label("* file not found! *");
-                       break;
-               default:
-                       if (sch->name.empty())
-                               mainButton->label(sch->wave->getBasename(false).c_str());
-                       else
-                               mainButton->label(sch->name.c_str());
-                       break;
-       }
-
-       /* Update channels. If you load a patch with recorded actions, the 'R' button 
-       must be shown. Moreover if the actions are active, the 'R' button must be 
-       activated accordingly. */
-
-       if (sch->hasActions)
-               showActionButton();
-       else
-               hideActionButton();
-
-       modeBox->value(static_cast<int>(sch->mode));
-       modeBox->redraw();
-
-       vol->value(sch->volume);
-       mute->value(sch->mute);
-       solo->value(sch->solo);
-
-       mainButton->setKey(sch->key);
-
-       arm->value(sch->armed);
-
-#ifdef WITH_VST
-       fx->status = sch->plugins.size() > 0;
-       fx->redraw();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geSampleChannel::showActionButton()
-{
-       readActions->value(static_cast<m::SampleChannel*>(ch)->readActions);
-       readActions->show();
-       packWidgets();
-       redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
+       geChannel::refresh();
 
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               if (c.hasData()) 
+                       status->redraw();
 
-void geSampleChannel::hideActionButton()
-{
-       readActions->hide();
-       packWidgets();
-       redraw();
+               if (c.hasActions) {
+                       readActions->show();
+                       readActions->setStatus(c.readActions);
+                       readActions->redraw();
+               }
+               else 
+                       readActions->hide();
+       });
 }
 
 
@@ -488,7 +424,10 @@ void geSampleChannel::resize(int X, int Y, int W, int H)
 #ifdef WITH_VST
        fx->hide();
 #endif
-
+       
+       m::model::ChannelsLock l(m::model::channels);
+       const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, channelId));
+       
        if (w() > BREAK_ARM)
                arm->show();
 #ifdef WITH_VST
@@ -497,13 +436,16 @@ void geSampleChannel::resize(int X, int Y, int W, int H)
 #endif
        if (w() > BREAK_MODE_BOX)
                modeBox->show();
-       if (w() > BREAK_READ_ACTIONS && ch->hasActions)
+       if (w() > BREAK_READ_ACTIONS && ch.hasActions)
                readActions->show();
 
        packWidgets();
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+
 void geSampleChannel::changeSize(int H)
 {
        geChannel::changeSize(H);
@@ -513,4 +455,6 @@ void geSampleChannel::changeSize(int H)
        status->resize(x(), Y, w(), G_GUI_UNIT);
        modeBox->resize(x(), Y, w(), G_GUI_UNIT);
        readActions->resize(x(), Y, w(), G_GUI_UNIT);
-}
\ No newline at end of file
+}
+
+}} // giada::v::
index 24e02a63b3a7361266610a5f63004f60912cc32e..86923f53a6c8921e4c5c8082f2c04a71d0069c19 100644 (file)
 #define GE_SAMPLE_CHANNEL_H
 
 
-#include "../../../../core/sampleChannel.h"
 #include "channel.h"
 
 
-class geChannelMode;
-class geButton;
+class geStatusButton;
 
 
-class geSampleChannel : public geChannel
+namespace giada {
+namespace m 
+{ 
+class SampleChannel; 
+}
+namespace v
 {
-private:
-
-       static void cb_button(Fl_Widget* v, void* p);
-       static void cb_openMenu(Fl_Widget* v, void* p);
-       static void cb_readActions(Fl_Widget* v, void* p);
-       void cb_button();
-       void cb_openMenu();
-       void cb_readActions();
+class geChannelMode;
 
+class geSampleChannel : public geChannel
+{
 public:
 
-       geSampleChannel(int x, int y, int w, int h, giada::m::SampleChannel* ch);
+       geSampleChannel(int x, int y, int w, int h, ID channelId);
 
        void resize(int x, int y, int w, int h) override;
 
-       void reset() override;
-       void update() override;
        void refresh() override;
        void changeSize(int h) override;
 
-       /* show/hideActionButton
-       Adds or removes 'R' button when actions are available. */
+       geChannelMode* modeBox;
+       geStatusButton*      readActions;
 
-       void showActionButton();
-       void hideActionButton();
+private:
 
-       geChannelMode* modeBox;
-       geButton*      readActions;
+       static void cb_playButton(Fl_Widget* v, void* p);
+       static void cb_openMenu(Fl_Widget* v, void* p);
+       static void cb_readActions(Fl_Widget* v, void* p);
+       void cb_playButton();
+       void cb_openMenu();
+       void cb_readActions();
 };
+}} // giada::v::
 
 
 #endif
index 25b35bc8c0a7984ac70f82286d2347565a5478f8..93443ec549f9a04cc25d5e25e11961c82e0dddc6 100644 (file)
 
 
 #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/mainWindow.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/model/model.h"
+#include "core/wave.h"
+#include "core/mixer.h"
+#include "core/recorder.h"
+#include "core/recManager.h"
+#include "utils/string.h"
+#include "utils/fs.h"
+#include "glue/channel.h"
+#include "gui/dialogs/mainWindow.h"
 #include "sampleChannel.h"
 #include "keyboard.h"
 #include "sampleChannelButton.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
-using namespace giada;
+namespace giada {
+namespace v
+{
+geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, ID channelId)
+: geChannelButton(x, y, w, h, channelId)
+{
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
+               
+               switch (sc.playStatus) {
+                       case ChannelStatus::EMPTY:
+                               label("-- no sample --");
+                               break;
+                       case ChannelStatus::MISSING:
+                       case ChannelStatus::WRONG:
+                               label("* file not found! *");
+                               break;
+                       default:
+                               if (sc.name.empty()) {
+                                       m::model::onGet(m::model::waves, sc.waveId, [&](m::Wave& w)
+                                       {
+                                               label(w.getBasename(false).c_str());
+                                       });
+                               }
+                               else
+                                       label(sc.name.c_str());
+                               break;
+               }
+       });
+}
 
 
-geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h,
-       const char* l)
-       : geChannelButton(x, y, w, h, l)
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannelButton::refresh()
 {
+       geChannelButton::refresh();
+       
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               if (m::recManager::isRecordingInput() && c.armed)
+                       setInputRecordMode();
+               else
+               if (m::recManager::isRecordingAction() && c.hasData())
+                       setActionRecordMode();
+       });
+
+       redraw();
 }
 
 
@@ -64,14 +112,12 @@ int geSampleChannelButton::handle(int e)
                        break;
                }
                case FL_PASTE: {
-                       geSampleChannel*  gch = static_cast<geSampleChannel*>(parent());
-                       m::SampleChannel* ch  = static_cast<m::SampleChannel*>(gch->ch);
-                       int result = c::channel::loadChannel(ch, u::string::trim(gu_stripFileUrl(Fl::event_text())));
-                       if (result != G_RES_OK)
-                               G_MainWin->keyboard->printChannelMessage(result);
+                       c::channel::loadChannel(m_channelId, u::string::trim(u::fs::stripFileUrl(Fl::event_text())));
                        ret = 1;
                        break;
                }
        }
        return ret;
 }
+
+}} // giada::v::
index 213581aedf6d4b30e12d7d4fc4d1bd85e3a463ea..7d05b284fccf6bdfb12e9da5d14ea3fd75d1f8fa 100644 (file)
 #include "channelButton.h"
 
 
+namespace giada {
+namespace m 
+{ 
+class SampleChannel; 
+}
+namespace v
+{
 class geSampleChannelButton : public geChannelButton
 {
 public:
 
-       geSampleChannelButton(int x, int y, int w, int h, const char* l=0);
-       int handle(int e);
+       geSampleChannelButton(int x, int y, int w, int h, ID channelId);
+       
+    int handle(int e) override;
+
+    void refresh() override;
 };
+}} // giada::v::
 
 
 #endif
index 9affcef4f010b654fda2dfde087440c5220a2689..a3003e8fcb572d54164c23b8ef14415c1ca501a7 100644 (file)
@@ -2,7 +2,7 @@
 *
 * Giada - Your Hardcore Loopmachine
 *
-* -----------------------------------------------------------------------------
+* ------------------------------------------------------------------------------
 *
 * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual
 *
 * along with Giada - Your Hardcore Loopmachine. If not, see
 * <http://www.gnu.org/licenses/>.
 *
-* -------------------------------------------------------------------------- */
-
-
-#include "../../../core/const.h"
-#include "../../../core/graphics.h"
-#include "../../../core/mixer.h"
-#include "../../../core/pluginHost.h"
-#include "../../../glue/main.h"
-#include "../../../utils/gui.h"
-#include "../../elems/soundMeter.h"
-#include "../../elems/basics/statusButton.h"
-#include "../../elems/basics/dial.h"
-#include "../../dialogs/mainWindow.h"
-#include "../../dialogs/pluginList.h"
+* --------------------------------------------------------------------------- */
+
+
+#include "core/const.h"
+#include "core/model/model.h"
+#include "core/graphics.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/pluginHost.h"
+#include "glue/main.h"
+#include "utils/gui.h"
+#include "gui/elems/soundMeter.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/pluginList.h"
 #include "mainIO.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
 namespace v
 {
 geMainIO::geMainIO(int x, int y)
-       : Fl_Group(x, y, 396, 20)
+: Fl_Pack(x, y, 396, 20)
 {
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+
        begin();
 
 #if defined(WITH_VST)
-       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);
+
+       masterFxIn  = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
+       inVol       = new geDial        (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+       inMeter     = new geSoundMeter  (0, 0, 140, G_GUI_UNIT);
+       inToOut     = new geButton      (0, 0, 12, G_GUI_UNIT, "");
+       outMeter    = new geSoundMeter  (0, 0, 140, G_GUI_UNIT);
+       outVol      = new geDial        (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+       masterFxOut = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
+
 #else
-       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);
+
+       inVol       = new geDial      (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+       inMeter     = new geSoundMeter(0, 0, 140, G_GUI_UNIT);
+       outMeter    = new geSoundMeter(0, 0, 140, G_GUI_UNIT);
+       outVol      = new geDial      (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+
 #endif
 
        end();
@@ -70,15 +79,18 @@ geMainIO::geMainIO(int x, int y)
        resizable(nullptr);   // don't resize any widget
 
        outVol->callback(cb_outVol, (void*)this);
-       outVol->value(m::mixer::outVol.load());
        inVol->callback(cb_inVol, (void*)this);
-       inVol->value(m::mixer::inVol.load());
+
+       outVol->value(m::mh::getOutVol());
+       inVol->value(m::mh::getInVol());
 
 #ifdef WITH_VST
+
        masterFxOut->callback(cb_masterFxOut, (void*)this);
        masterFxIn->callback(cb_masterFxIn, (void*)this);
        inToOut->callback(cb_inToOut, (void*)this);
        inToOut->type(FL_TOGGLE_BUTTON);
+
 #endif
 }
 
@@ -86,12 +98,12 @@ geMainIO::geMainIO(int x, int y)
 /* -------------------------------------------------------------------------- */
 
 
-void geMainIO::cb_outVol     (Fl_Widget *v, void *p)   { ((geMainIO*)p)->cb_outVol(); }
-void geMainIO::cb_inVol      (Fl_Widget *v, void *p)   { ((geMainIO*)p)->cb_inVol(); }
+void geMainIO::cb_outVol     (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_outVol(); }
+void geMainIO::cb_inVol      (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inVol(); }
 #ifdef WITH_VST
-void geMainIO::cb_masterFxOut(Fl_Widget *v, void *p)    { ((geMainIO*)p)->cb_masterFxOut(); }
-void geMainIO::cb_masterFxIn (Fl_Widget *v, void *p)    { ((geMainIO*)p)->cb_masterFxIn(); }
-void geMainIO::cb_inToOut    (Fl_Widget *v, void *p)    { ((geMainIO*)p)->cb_inToOut(); }
+void geMainIO::cb_masterFxOut(Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxOut(); }
+void geMainIO::cb_masterFxIn (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxIn(); }
+void geMainIO::cb_inToOut    (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOut(); }
 #endif
 
 
@@ -117,20 +129,24 @@ void geMainIO::cb_inVol()
 
 
 #ifdef WITH_VST
+
 void geMainIO::cb_masterFxOut()
 {
-       u::gui::openSubWindow(G_MainWin, new gdPluginList(m::pluginHost::StackType::MASTER_OUT), WID_FX_LIST);
+       u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_OUT_CHANNEL_ID), WID_FX_LIST);
 }
 
+
 void geMainIO::cb_masterFxIn()
 {
-       u::gui::openSubWindow(G_MainWin, new gdPluginList(m::pluginHost::StackType::MASTER_IN), WID_FX_LIST);
+       u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_IN_CHANNEL_ID), WID_FX_LIST);
 }
 
+
 void geMainIO::cb_inToOut()
 {
-       m::mixer::inToOut = inToOut->value();
+       m::mh::setInToOut(inToOut->value());
 }
+
 #endif
 
 
@@ -156,15 +172,13 @@ void geMainIO::setInVol(float v)
 
 void geMainIO::setMasterFxOutFull(bool v)
 {
-  masterFxOut->status = v;
-  masterFxOut->redraw();
+  masterFxOut->setStatus(v);
 }
 
 
 void geMainIO::setMasterFxInFull(bool v)
 {
-  masterFxIn->status = v;
-  masterFxIn->redraw();
+  masterFxIn->setStatus(v);
 }
 
 #endif
@@ -181,4 +195,26 @@ void geMainIO::refresh()
        inMeter->redraw();
 }
 
-}} // giada::v::
\ No newline at end of file
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMainIO::rebuild()
+{
+       m::model::onGet(m::model::channels, m::mixer::MASTER_OUT_CHANNEL_ID, [&](m::Channel& c)
+       {
+               outVol->value(c.volume);
+#ifdef WITH_VST
+               masterFxOut->setStatus(c.pluginIds.size() > 0);
+#endif
+       });
+
+       m::model::onGet(m::model::channels, m::mixer::MASTER_IN_CHANNEL_ID, [&](m::Channel& c)
+       {
+               inVol->value(c.volume);
+#ifdef WITH_VST
+               masterFxIn->setStatus(c.pluginIds.size() > 0);
+#endif
+       });
+}
+}} // giada::v::
index cec4ad726012e21f4f57d32b8c405c7b9495777f..5194936bdcf33f535966b408baf70367d9b6384c 100644 (file)
@@ -29,7 +29,8 @@
 #define GE_MAIN_IO_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
+
 
 class geSoundMeter;
 class geDial;
@@ -38,23 +39,28 @@ class geStatusButton;
 class geButton;
 #endif
 
+
 namespace giada {
 namespace v
 {
-class geMainIO : public Fl_Group
+class geMainIO : public Fl_Pack
 {
-private:
+public:
 
-       geSoundMeter* outMeter;
-       geSoundMeter* inMeter;
-       geDial*       outVol;
-       geDial*       inVol;
+       geMainIO(int x, int y);
+
+       void refresh();
+       void rebuild();
+
+       void setOutVol(float v);
+       void setInVol (float v);
 #ifdef WITH_VST
-  geStatusButton* masterFxOut;
-  geStatusButton* masterFxIn;
-  geButton*       inToOut;
+       void setMasterFxOutFull(bool v);
+       void setMasterFxInFull(bool v);
 #endif
 
+private:
+
        static void cb_outVol     (Fl_Widget* v, void* p);
        static void cb_inVol      (Fl_Widget* v, void* p);
 #ifdef WITH_VST
@@ -62,26 +68,22 @@ private:
        static void cb_masterFxIn (Fl_Widget* v, void* p);
        static void cb_inToOut    (Fl_Widget* v, void* p);
 #endif
-
-       void cb_outVol     ();
-       void cb_inVol      ();
+       void cb_outVol();
+       void cb_inVol();
 #ifdef WITH_VST
        void cb_masterFxOut();
-       void cb_masterFxIn ();
-       void cb_inToOut    ();
+       void cb_masterFxIn();
+       void cb_inToOut();
 #endif
 
-public:
-
-       geMainIO(int x, int y);
-
-       void refresh();
-
-       void setOutVol(float v);
-       void setInVol (float v);
+       geSoundMeter* outMeter;
+       geSoundMeter* inMeter;
+       geDial*       outVol;
+       geDial*       inVol;
 #ifdef WITH_VST
-       void setMasterFxOutFull(bool v);
-       void setMasterFxInFull(bool v);
+  geStatusButton* masterFxOut;
+  geStatusButton* masterFxIn;
+  geButton*       inToOut;
 #endif
 };
 }} // giada::v::
index 0416ca4f3ab878c8a9129179cc61c62a690be55f..579ffd88d1a13ba38ef15c9414c5dcbce2c71298 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #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 "../../../core/sampleChannel.h"
-#include "../../../utils/gui.h"
-#include "../../../glue/storage.h"
-#include "../../../glue/main.h"
-#include "../../elems/basics/boxtypes.h"
-#include "../../elems/basics/button.h"
-#include "../../dialogs/mainWindow.h"
-#include "../../dialogs/about.h"
-#include "../../dialogs/config.h"
-#include "../../dialogs/warnings.h"
-#include "../../dialogs/browser/browserLoad.h"
-#include "../../dialogs/browser/browserSave.h"
-#include "../../dialogs/midiIO/midiInputMaster.h"
+#include "core/channels/sampleChannel.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/conf.h"
+#include "core/patch.h"
+#include "utils/gui.h"
+#include "glue/storage.h"
+#include "glue/main.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/about.h"
+#include "gui/dialogs/config.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/browser/browserLoad.h"
+#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/midiIO/midiInputMaster.h"
 #include "keyboard/keyboard.h"
 #include "mainMenu.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
 namespace v
 {
 geMainMenu::geMainMenu(int x, int y)
-       : Fl_Group(x, y, 300, 20)
+: Fl_Pack(x, y, 300, 20)
 {
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+
        begin();
 
-       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");
+       file   = new geButton(0, 0, 70, 21, "file");
+       edit   = new geButton(0, 0, 70, 21, "edit");
+       config = new geButton(0, 0, 70, 21, "config");
+       about  = new geButton(0, 0, 70, 21, "about");
 
        end();
 
        resizable(nullptr);   // don't resize any widget
 
-       about->callback(cb_about, (void*)this);
        file->callback(cb_file, (void*)this);
        edit->callback(cb_edit, (void*)this);
-       config->callback(cb_config, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMainMenu::cb_about (Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_about(); }
-void geMainMenu::cb_config(Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_config(); }
-void geMainMenu::cb_file  (Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_file(); }
-void geMainMenu::cb_edit  (Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_edit(); }
-
 
-/* -------------------------------------------------------------------------- */
-
-
-void geMainMenu::cb_about()
-{
-       u::gui::openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT);
+       about->callback([](Fl_Widget* w, void* v) { 
+               u::gui::openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT);
+       });
+       config->callback([](Fl_Widget* w, void* v) { 
+               u::gui::openSubWindow(G_MainWin, new gdConfig(400, 370), WID_CONFIG);
+       });
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geMainMenu::cb_config()
-{
-       u::gui::openSubWindow(G_MainWin, new gdConfig(400, 370), WID_CONFIG);
-}
+void geMainMenu::cb_file(Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_file(); }
+void geMainMenu::cb_edit(Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_edit(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -116,23 +106,26 @@ void geMainMenu::cb_file()
                {"Open patch or project..."},
                {"Save patch..."},
                {"Save project..."},
+               {"Close project"},
+#ifndef NDEBUG
+               {"Debug stats"},
+#endif
                {"Quit Giada"},
                {0}
        };
 
-       Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50);
-       b->box(G_CUSTOM_BORDER_BOX);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
 
-       const Fl_Menu_Item* m = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
+       const Fl_Menu_Item* m = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, &b);
        if (!m) return;
 
        if (strcmp(m->label(), "Open patch or project...") == 0) {
-               gdWindow* childWin = new gdBrowserLoad(conf::browserX, conf::browserY,
-                               conf::browserW, conf::browserH, "Load patch or project",
-                               conf::patchPath, c::storage::loadPatch, nullptr);
+               gdWindow* childWin = new gdBrowserLoad("Load patch or project", 
+                       conf::patchPath, c::storage::loadPatch, 0);
                u::gui::openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
@@ -140,19 +133,27 @@ void geMainMenu::cb_file()
                if (mh::hasLogicalSamples() || mh::hasEditedSamples())
                        if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples."))
                                return;
-               gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
-                               conf::browserW, conf::browserH, "Save patch",
-                               conf::patchPath, patch::name, c::storage::savePatch, nullptr);
+               gdWindow* childWin = new gdBrowserSave("Save patch", conf::patchPath, 
+                       patch::name, c::storage::savePatch, 0);
                u::gui::openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
        if (strcmp(m->label(), "Save project...") == 0) {
-               gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
-                               conf::browserW, conf::browserH, "Save project",
-                               conf::patchPath, patch::name, c::storage::saveProject, nullptr);
+               gdWindow* childWin = new gdBrowserSave("Save project", conf::patchPath, 
+                       patch::name, c::storage::saveProject, 0);
                u::gui::openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
                return;
        }
+       if (strcmp(m->label(), "Close project") == 0) {
+               c::main::resetToInitState(/*createColumns=*/true);
+               return;
+       }
+#ifndef NDEBUG
+       if (strcmp(m->label(), "Debug stats") == 0) {
+               m::model::debug();
+               return;
+       }
+#endif
        if (strcmp(m->label(), "Quit Giada") == 0) {
                G_MainWin->do_callback();
                return;
@@ -168,66 +169,33 @@ void geMainMenu::cb_edit()
        Fl_Menu_Item menu[] = {
                {"Clear all samples"},
                {"Clear all actions"},
-               {"Remove empty columns"},
-               {"Reset to init state"},
                {"Setup global MIDI input..."},
                {0}
        };
 
-       /* clear all actions disabled if no recs, clear all samples disabled
-        * if no samples. */
-
+       menu[0].deactivate();
        menu[1].deactivate();
 
-       for (const m::Channel* ch : m::mixer::channels)
-               if (ch->hasActions) {
-                       menu[1].activate();
-                       break;
-               }
-
-       for (const m::Channel* ch : m::mixer::channels)
-               if (ch->hasData()) {
-                       menu[0].activate();
-                       break;
-               }
-
-       Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50);
-       b->box(G_CUSTOM_BORDER_BOX);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
-
-       const Fl_Menu_Item* m = menu->popup(Fl::event_x(),      Fl::event_y(), 0, 0, b);
+       if (m::mh::hasAudioData()) menu[0].activate();
+       if (m::mh::hasActions())   menu[1].activate();
+
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
+
+       const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
        if (!m) return;
 
-       if (strcmp(m->label(), "Clear all samples") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all samples: are you sure?"))
-                       return;
-               G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+       if (strcmp(m->label(), "Clear all samples") == 0) 
                c::main::clearAllSamples();
-               return;
-       }
-       if (strcmp(m->label(), "Clear all actions") == 0) {
-               if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
-                       return;
-               G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+       else
+       if (strcmp(m->label(), "Clear all actions") == 0) 
                c::main::clearAllActions();
-               return;
-       }
-       if (strcmp(m->label(), "Reset to init state") == 0) {
-               if (!gdConfirmWin("Warning", "Reset to init state: are you sure?"))
-                       return;
-               c::main::resetToInitState();
-               return;
-       }
-       if (strcmp(m->label(), "Remove empty columns") == 0) {
-               G_MainWin->keyboard->organizeColumns();
-               return;
-       }
-       if (strcmp(m->label(), "Setup global MIDI input...") == 0) {
-               u::gui::openSubWindow(G_MainWin, new gdMidiInputMaster(), 0);
-               return;
-       }
+       else
+       if (strcmp(m->label(), "Setup global MIDI input...") == 0) 
+               u::gui::openSubWindow(G_MainWin, new gdMidiInputMaster(), WID_MIDI_INPUT);
 }
 
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 57e2b437f35a62fc17454d15bfa44d8fc0bfaa9e..60944b899b0b9b1c7e26354e40ede9b550284eab 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_MAIN_MENU_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geButton;
@@ -38,27 +38,23 @@ class geButton;
 namespace giada {
 namespace v
 {
-class geMainMenu : public Fl_Group
+class geMainMenu : public Fl_Pack
 {
-private:
+public:
 
-  geButton* file;
-  geButton* edit;
-  geButton* config;
-  geButton* about;
+       geMainMenu(int x, int y);
 
-       static void cb_about (Fl_Widget* v, void* p);
-       static void cb_config(Fl_Widget* v, void* p);
-       static void cb_file  (Fl_Widget* v, void* p);
-       static void cb_edit  (Fl_Widget* v, void* p);
-       void cb_about ();
-       void cb_config();
-       void cb_file  ();
-       void cb_edit  ();
+private:
 
-public:
+       static void cb_file(Fl_Widget* v, void* p);
+       static void cb_edit(Fl_Widget* v, void* p);
+       void cb_file();
+       void cb_edit();
 
-       geMainMenu(int x, int y);
+       geButton* file;
+       geButton* edit;
+       geButton* config;
+       geButton* about;
 };
 }} // giada::v::
 
index aa07f937ed729e552e2a8d36a2ef78c563ff3cf8..81c1314457b4e1a48cf2ca3dd54c8212dea77c65 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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 "../../../utils/string.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/choice.h"
-#include "../../dialogs/mainWindow.h"
-#include "../../dialogs/bpmInput.h"
-#include "../../dialogs/beatsInput.h"
+#include "core/const.h"
+#include "core/model/model.h"
+#include "core/kernelAudio.h"
+#include "core/mixer.h"
+#include "core/recManager.h"
+#include "core/graphics.h"
+#include "core/clock.h"
+#include "glue/main.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/bpmInput.h"
+#include "gui/dialogs/beatsInput.h"
 #include "mainTimer.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using std::string;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -56,7 +56,7 @@ geMainTimer::geMainTimer(int x, int y)
 
        quantizer  = new geChoice(x, y, 50, 20, "", false);
        bpm        = new geButton(quantizer->x()+quantizer->w()+4,  y, 50, 20);
-       meter      = new geButton(bpm->x()+bpm->w()+8,  y, 50, 20, "4/1");
+       meter      = new geButton(bpm->x()+bpm->w()+8,  y, 50, 20);
        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);
 
@@ -81,6 +81,15 @@ geMainTimer::geMainTimer(int x, int y)
        quantizer->add("1\\/6", 0, cb_quantizer, (void*)this);
        quantizer->add("1\\/8", 0, cb_quantizer, (void*)this);
        quantizer->value(0); //  "off" by default
+
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+       
+       /* Can't change bpm from within Giada when using JACK. */
+
+       if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
+               bpm->deactivate();
+
+#endif
 }
 
 
@@ -142,6 +151,47 @@ void geMainTimer::cb_divider()
 /* -------------------------------------------------------------------------- */
 
 
+void geMainTimer::refresh()
+{
+       if (m::recManager::isRecordingInput()) {
+               bpm->deactivate();
+               meter->deactivate();
+               multiplier->deactivate();
+               divider->deactivate();
+       }
+       else {
+               /* Don't reactivate bpm when using JACK. It must stay disabled. */
+
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+               if (m::kernelAudio::getAPI() != G_SYS_API_JACK)
+                       bpm->activate();
+#else
+               bpm->activate();
+#endif
+               meter->activate();      
+               multiplier->activate();
+               divider->activate();    
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMainTimer::rebuild()
+{
+       m::model::onGet(m::model::clock, [&](m::model::Clock& c)
+       {
+               setBpm(c.bpm);
+               setMeter(c.beats, c.bars);
+               setQuantizer(c.quantize);
+       });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void geMainTimer::setBpm(const char* v)
 {
        bpm->copy_label(v);
@@ -188,8 +238,8 @@ void geMainTimer::setQuantizer(int q)
 
 void geMainTimer::setMeter(int beats, int bars)
 {
-       string tmp = u::string::iToString(beats) + "/" + u::string::iToString(bars);
-       meter->copy_label(tmp.c_str());
+       std::string s = std::to_string(beats) + "/" + std::to_string(bars);
+       meter->copy_label(s.c_str());
 }
 
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 03ac84054d1ddb0e7724a37fcd33270dec01c405..54e73be418db81cda97b1bd1d43002ee7a0aeaa4 100644 (file)
@@ -41,38 +41,41 @@ namespace v
 {
 class geMainTimer : public Fl_Group
 {
-private:
-
-       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);
-       static void cb_quantizer (Fl_Widget* v, void* p);
-       static void cb_multiplier(Fl_Widget* v, void* p);
-       static void cb_divider   (Fl_Widget* v, void* p);
-       inline void cb_bpm();
-       inline void cb_meter();
-       inline void cb_quantizer();
-       inline void cb_multiplier();
-       inline void cb_divider();
-
 public:
 
        geMainTimer(int x, int y);
+       
+       void refresh();
+       void rebuild();
 
        void setBpm(const char* v);
        void setBpm(float v);
        void setMeter(int beats, int bars);
        void setQuantizer(int q);
 
-  /* setLock
-  Locks bpm, beter and multipliers. Used during audio recordings. */
+       /* setLock
+       Locks bpm, beter and multipliers. Used during audio recordings. */
 
-  void setLock(bool v);
+       void setLock(bool v);
+
+private:
+
+       static void cb_bpm       (Fl_Widget* v, void* p);
+       static void cb_meter     (Fl_Widget* v, void* p);
+       static void cb_quantizer (Fl_Widget* v, void* p);
+       static void cb_multiplier(Fl_Widget* v, void* p);
+       static void cb_divider   (Fl_Widget* v, void* p);
+       void cb_bpm();
+       void cb_meter();
+       void cb_quantizer();
+       void cb_multiplier();
+       void cb_divider();
+
+       geButton* bpm;
+       geButton* meter;
+       geChoice* quantizer;
+       geButton* multiplier;
+       geButton* divider;
 };
 }} // giada::v::
 
index d4f0b3ec0de8f3eff12220688dcb75fb0f0e621a..52b56bdbac5597807fc9465dd8ddd9cfbc96b37b 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/graphics.h"
-#include "../../../core/conf.h"
-#include "../../../glue/transport.h"
-#include "../../../glue/io.h"
-#include "../../elems/basics/button.h"
+#include "core/graphics.h"
+#include "core/conf.h"
+#include "core/clock.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/recManager.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "glue/main.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/statusButton.h"
 #include "mainTransport.h"
 
 
@@ -37,39 +44,35 @@ namespace giada {
 namespace v
 {
 geMainTransport::geMainTransport(int x, int y)
-       : Fl_Group(x, y, 174, 25)
+: Fl_Pack(x, y, 200, 25)
 {
-       begin();
-
-       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);
-       recTriggerMode = new geButton(play->x()+play->w()+16, y+5, 15, 15, "", recTriggerModeOff_xpm, recTriggerModeOn_xpm);
-       recAction      = new geButton(recTriggerMode->x()+recTriggerMode->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()+16, y+5, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
-
-       end();
-
-       resizable(nullptr);   // don't resize any widget
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+
+       rewind         = new geButton      (0, 0, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+       play           = new geStatusButton(0, 0, 25, 25, play_xpm, pause_xpm);
+                        new geBox         (0, 0, 10, 25);
+       recTriggerMode = new geButton      (0, 0, 15, 25, "", recTriggerModeOff_xpm, recTriggerModeOn_xpm);
+       recAction      = new geStatusButton(0, 0, 25, 25, recOff_xpm, recOn_xpm);
+       recInput       = new geStatusButton(0, 0, 25, 25, inputRecOff_xpm, inputRecOn_xpm);
+                        new geBox         (0, 0, 10, 25);
+       metronome      = new geStatusButton(0, 0, 15, 25, metronomeOff_xpm, metronomeOn_xpm);
 
        rewind->callback([](Fl_Widget* w, void* v) { 
-               c::transport::rewindSeq(/*gui=*/true);
+               m::mh::rewindSequencer();
        });
 
        play->callback([](Fl_Widget* w, void* v) { 
-               c::transport::startStopSeq(/*gui=*/true);
+               m::mh::toggleSequencer();
        });
-       play->type(FL_TOGGLE_BUTTON);
 
        recAction->callback([](Fl_Widget* w, void* v) { 
-               c::io::toggleActionRec(/*gui=*/true);
+               c::main::toggleActionRec();
        });
-       recAction->type(FL_TOGGLE_BUTTON);
 
        recInput->callback([](Fl_Widget* w, void* v) { 
-               c::io::toggleInputRec(/*gui=*/true);
+               c::main::toggleInputRec();
        });
-       recInput->type(FL_TOGGLE_BUTTON);
 
        recTriggerMode->value(m::conf::recTriggerMode);
        recTriggerMode->type(FL_TOGGLE_BUTTON);
@@ -77,59 +80,22 @@ geMainTransport::geMainTransport(int x, int y)
                m::conf::recTriggerMode = static_cast<geButton*>(w)->value();
        });
 
+       metronome->type(FL_TOGGLE_BUTTON);
        metronome->callback([](Fl_Widget* w, void* v) {
-               c::transport::toggleMetronome(/*gui=*/true);
+               m::mixer::toggleMetronome();
        });
-       metronome->type(FL_TOGGLE_BUTTON);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMainTransport::updatePlay(int v)
-{
-       play->value(v);
-       play->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMainTransport::updateMetronome(int v)
-{
-       metronome->value(v);
-       metronome->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMainTransport::updateRecInput(int v)
-{
-       recInput->value(v);
-       recInput->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMainTransport::updateRecAction(int v)
-{
-       recAction->value(v);
-       recAction->redraw();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTransport::setRecTriggerModeActive(bool v)
+void geMainTransport::refresh()
 {
-       v ? recTriggerMode->activate() : recTriggerMode->deactivate();
+       play->setStatus(m::clock::isRunning());
+       recAction->setStatus(m::recManager::isRecordingAction());
+       recInput->setStatus(m::recManager::isRecordingInput());
+       metronome->setStatus(m::mixer::isMetronomeOn());
 }
 
-}} // giada::v::
\ No newline at end of file
+}} // giada::v::
index 1172c67b8ea767e8acdd4db8e08d65d5de88f880..1ce3b8dc97dc9fa43e37bf70833516d218923c01 100644 (file)
 #define GE_MAIN_TRANSPORT_H
 
 
-#include <FL/Fl_Group.H>
-#include "../../../core/types.h"
+#include <FL/Fl_Pack.H>
 
 
 class geButton;
+class geStatusButton;
 
 
 namespace giada {
 namespace v
 {
-class geMainTransport : public Fl_Group
+class geMainTransport : public Fl_Pack
 {
-private:
+public:
 
-       geButton* rewind;
-       geButton* play;
-       
-       geButton* recTriggerMode;
-       geButton* recAction;
-       geButton* recInput;
+       geMainTransport(int x, int y);
 
-       geButton* metronome;
+       void refresh();
 
-public:
+private:
 
-       geMainTransport(int x, int y);
+       geButton* rewind;
+       geStatusButton* play;
        
-       void updatePlay(int v);
-       void updateMetronome(int v);
-       void updateRecInput(int v);
-       void updateRecAction(int v);
-       void setRecTriggerModeActive(bool v);
+       geButton* recTriggerMode;
+       geStatusButton* recAction;
+       geStatusButton* recInput;
 
+       geStatusButton* metronome;
 };
 }} // giada::v::
 
index c17b50c79af8b9da6cd0cbd65394909b1a3c89da..0e7bdf6b1c2ae7c8fed6f3cffcec9c8f9fe81d16 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../utils/string.h"
-#include "../dialogs/midiIO/midiInputBase.h"
+#include "utils/string.h"
+#include "core/midiDispatcher.h"
+#include "core/midiEvent.h"
+#include "glue/io.h"
+#include "gui/dialogs/midiIO/midiInputBase.h"
 #include "basics/boxtypes.h"
 #include "basics/button.h"
 #include "basics/box.h"
 #include "midiLearner.h"
 
 
-using std::string;
-using namespace giada;
-
-
-geMidiLearner::geMidiLearner(int X, int Y, int W, const char* l,
-  m::midiDispatcher::cb_midiLearn* cb, uint32_t* param, m::Channel* ch)
-: Fl_Group(X, Y, W, 20),
-  callback(cb),
-  ch      (ch),
-  param   (param)
+namespace giada {
+namespace v 
+{
+geMidiLearner::geMidiLearner(int X, int Y, int W, const char* l, 
+       std::atomic<uint32_t>& param, ID channelId)
+: Fl_Group   (X, Y, W, 20),
+  m_channelId(channelId),
+  m_param    (param)
 {
        begin();
-       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");
+       m_text   = new geBox(x(), y(), 156, 20, l);
+       m_value  = new geButton(m_text->x()+m_text->w()+4, y(), 80, 20);
+       m_button = new geButton(m_value->x()+m_value->w()+4, y(), 40, 20, "learn");
        end();
 
-       text->box(G_CUSTOM_BORDER_BOX);
-       text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+       m_text->box(G_CUSTOM_BORDER_BOX);
+       m_text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
 
-       value->box(G_CUSTOM_BORDER_BOX);
-       value->callback(cb_value, (void*)this);
-       value->when(FL_WHEN_RELEASE);
-       updateValue();
+       m_value->box(G_CUSTOM_BORDER_BOX);
+       m_value->callback(cb_value, (void*)this);
+       m_value->when(FL_WHEN_RELEASE);
 
-       button->type(FL_TOGGLE_BUTTON);
-       button->callback(cb_button, (void*)this);
+       m_button->type(FL_TOGGLE_BUTTON);
+       m_button->callback(cb_button, (void*)this);
+       
+       refresh();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geMidiLearner::updateValue()
+void geMidiLearner::refresh()
 {
-       string tmp;
-       if (*param != 0x0) {
-               tmp = "0x" + u::string::iToString(*param, true); // true: hex mode
+       std::string tmp = "(not set)";
+       
+       if (m_param != 0x0) {
+               tmp = "0x" + u::string::iToString(m_param.load(), true); // true: hex mode
                tmp.pop_back();  // Remove last two digits, useless in MIDI messages
                tmp.pop_back();  // Remove last two digits, useless in MIDI messages
        }
-       else
-               tmp = "(not set)";
-       value->copy_label(tmp.c_str());
-       button->value(0);
+
+       m_value->copy_label(tmp.c_str());
+       m_button->value(0);
 }
 
 
@@ -93,11 +95,8 @@ void geMidiLearner::cb_value(Fl_Widget* v, void* p) { ((geMidiLearner*)p)->cb_va
 
 void geMidiLearner::cb_value()
 {
-       if (Fl::event_button() == FL_RIGHT_MOUSE) {
-               *param = 0x0;
-               updateValue();
-       }
-       /// TODO - elif (LEFT_MOUSE) : insert values by hand
+       if (Fl::event_button() == FL_RIGHT_MOUSE)
+               c::io::midiLearn(m::MidiEvent(), m_param, m_channelId);  // Empty event (0x0)
 }
 
 
@@ -106,12 +105,12 @@ void geMidiLearner::cb_value()
 
 void geMidiLearner::cb_button()
 {
-       if (button->value() == 1) {
-               cbData.window  = static_cast<gdMidiInputBase*>(parent()); // parent = gdMidiInput
-               cbData.learner = this;
-               cbData.channel = ch;
-               m::midiDispatcher::startMidiLearn(callback, (void*)&cbData);
-       }
+       if (m_button->value() == 1)
+               m::midiDispatcher::startMidiLearn([this](m::MidiEvent e) 
+               {
+                       c::io::midiLearn(e, m_param, m_channelId);
+               });
        else
-               m::midiDispatcher::stopMidiLearn();
+               m::midiDispatcher::stopMidiLearn();     
 }
+}} // giada::v::
index c3ece9c499761765daef562d15f71dd904283998..9649a34b4c0dd012a8b341ff00f7d344df56949d 100644 (file)
 #define GE_MIDI_LEARNER_H
 
 
+#include <atomic>
 #include <FL/Fl_Group.H>
-#include "../../core/midiDispatcher.h"
-#include "../../core/channel.h"
+#include "core/types.h"
 
 
-class gdMidiInputBase;
-class geMidiLearner;
 class geBox;
 class geButton;
 
 
+namespace giada {
+namespace m
+{
+class Channel; 
+}
+namespace v 
+{
 class geMidiLearner : public Fl_Group
 {
-private:
-
-       /* callback
-       Callback to pass to midiDispatcher. Requires two parameters:
-        * uint32_t msg - MIDI message
-        * void   *data - extra data */
-
-       giada::m::midiDispatcher::cb_midiLearn* callback;
+public:
 
-       /* Channel it belongs to. Might be nullptr if the learner comes from the MIDI
-       input master window. */
+       geMidiLearner(int x, int y, int w, const char* l, std::atomic<uint32_t>& param, 
+               ID channelId);
 
-       giada::m::Channel* ch;
+       void refresh();
 
-       geBox* text;
-       geButton* value;
-       geButton* button;
+private:
 
        static void cb_button(Fl_Widget* v, void* p);
        static void cb_value (Fl_Widget* v, void* p);
        void cb_button();
        void cb_value();
 
-public:
-
-  /* cbData_t
-  Struct we pass to midiDispatcher as extra parameter. */
-
-  struct cbData_t
-  {
-               gdMidiInputBase*   window;
-               geMidiLearner*     learner;
-               giada::m::Channel* channel;
-       } cbData;
+       /* m_channelId
+       Channel it belongs to. Might be 0 if the learner comes from the MIDI input 
+       master window. */
 
-       /* param
-        * pointer to ch->midiIn[value] */
+       ID m_channelId;
 
-       uint32_t* param;
+       /* m_param
+       Reference to ch->midiIn[value]. */
 
-       geMidiLearner(int x, int y, int w, const char* l, 
-               giada::m::midiDispatcher::cb_midiLearn* cb, uint32_t* param, giada::m::Channel* ch);
+       std::atomic<uint32_t>& m_param;
 
-       void updateValue();
+       geBox*    m_text;
+       geButton* m_value;
+       geButton* m_button;
 };
+}} // giada::v::
 
 
 #endif
index a8c7b86ccbb7d115d9aba4dedbbe9006c1f09fc3..2fea15603ef0fc3b18572a7eef3fbd7fa1c24da3 100644 (file)
 
 
 #include <FL/fl_draw.H>
-#include "../../../core/plugin.h"
-#include "../../../core/const.h"
-#include "../../../core/pluginManager.h"
-#include "../../../core/pluginHost.h"
-#include "../basics/boxtypes.h"
+#include "core/plugin.h"
+#include "core/const.h"
+#include "core/pluginManager.h"
+#include "core/pluginHost.h"
+#include "gui/elems/basics/boxtypes.h"
 #include "pluginBrowser.h"
 
 
-using std::vector;
-using std::string;
-using namespace giada::m;
-
-
+namespace giada {
+namespace v
+{
 gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
        : Fl_Browser(x, y, w, h)
 {
@@ -65,8 +63,8 @@ gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
 
        computeWidths();
 
-  column_widths(widths);
-  column_char('\t');       // tabs as column delimiters
+       column_widths(widths);
+       column_char('\t');       // tabs as column delimiters
 
        refresh();
 
@@ -84,16 +82,16 @@ void gePluginBrowser::refresh()
        add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
        add("---\t---\t---\t---\t---");
 
-       for (int i=0; i<pluginManager::countAvailablePlugins(); i++) {
-               pluginManager::PluginInfo pi = pluginManager::getAvailablePluginInfo(i);
-               string m = pluginManager::doesPluginExist(pi.uid) ? "" : "@-";
-               string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
-                               pi.category +   "\t" + m + pi.format + "\t" + m + pi.uid;
+       for (int i=0; i<m::pluginManager::countAvailablePlugins(); i++) {
+               m::pluginManager::PluginInfo pi = m::pluginManager::getAvailablePluginInfo(i);
+               std::string m = m::pluginManager::doesPluginExist(pi.uid) ? "" : "@-";
+               std::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<pluginManager::countUnknownPlugins(); i++) {
-               string s = "?\t?\t?\t?\t? " + pluginManager::getUnknownPluginInfo(i) + " ?";
+       for (unsigned i=0; i<m::pluginManager::countUnknownPlugins(); i++) {
+               std::string s = "?\t?\t?\t?\t? " + m::pluginManager::getUnknownPluginInfo(i) + " ?";
                add(s.c_str());
        }
 }
@@ -105,8 +103,8 @@ void gePluginBrowser::refresh()
 void gePluginBrowser::computeWidths()
 {
        int w0, w1, w3;
-       for (int i=0; i<pluginManager::countAvailablePlugins(); i++) {
-               pluginManager::PluginInfo pi = pluginManager::getAvailablePluginInfo(i);
+       for (int i=0; i<m::pluginManager::countAvailablePlugins(); i++) {
+               m::pluginManager::PluginInfo pi = m::pluginManager::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());
@@ -120,6 +118,7 @@ void gePluginBrowser::computeWidths()
        widths[3] += 60;
        widths[4] = 0;
 }
+}} // giada::v::
 
 
 #endif
index 6f7999054fbff54fc6618b7ae12157ab2f221082..c9d42e87b80d1add07bb4910582dfdf59c85f003 100644 (file)
@@ -27,6 +27,7 @@
 
 #ifdef WITH_VST
 
+
 #ifndef GE_PLUGIN_BROWSER_H
 #define GE_PLUGIN_BROWSER_H
 
 #include <FL/Fl_Browser.H>
 
 
+namespace giada {
+namespace v
+{
 class gePluginBrowser : public Fl_Browser
 {
-private:
-
-       int widths[5] = {0};
-
-       void computeWidths();
-
 public:
 
        gePluginBrowser(int x, int y, int w, int h);
 
        void refresh();
+
+private:
+
+    void computeWidths();
+
+    int widths[5] = {0};
 };
+}} // giada::v::
+
 
 #endif
 
index 5ed188669c21433648644dfe50778aea46bb104a..29e4f0f99b21d3e700a7464f434298e6c481ab4e 100644 (file)
 #ifdef WITH_VST
 
 
+#include <cassert>
 #include <string>
-#include "../../../core/graphics.h"
-#include "../../../core/pluginHost.h"
-#include "../../../core/plugin.h"
-#include "../../../utils/gui.h"
-#include "../../../utils/log.h"
-#include "../../../glue/plugin.h"
-#include "../../elems/basics/button.h"
-#include "../../elems/basics/choice.h"
-#include "../../dialogs/mainWindow.h"
-#include "../../dialogs/pluginList.h"
-#include "../../dialogs/pluginWindowGUI.h"
-#include "../../dialogs/pluginWindow.h"
+#include "core/channels/channel.h"
+#include "core/model/model.h"
+#include "core/graphics.h"
+#include "core/pluginHost.h"
+#include "core/plugin.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "glue/plugin.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/pluginList.h"
+#include "gui/dialogs/pluginWindowGUI.h"
+#include "gui/dialogs/pluginWindow.h"
 #include "pluginElement.h"
 
 
-extern gdMainWindow* G_MainWin;
-
-
-using std::string;
-using namespace giada;
-
-
-gePluginElement::gePluginElement(gdPluginList* gdp, m::Plugin* p, int X, int Y, int W)
-       : Fl_Group   (X, Y, W, 20), 
-         m_parentWin(gdp), 
-         m_plugin    (p)
+namespace giada {
+namespace v
+{
+gePluginElement::gePluginElement(ID pluginId, ID channelId, int X, int Y, int W)
+: Fl_Pack    (X, Y, W, G_GUI_UNIT), 
+  m_channelId(channelId),
+  m_pluginId (pluginId)
 {
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
        begin();
-       button    = new geButton(8, y(), 220, 20);
-       program   = new geChoice(button->x()+button->w()+4, y(), 132, 20);
-       bypass    = new geButton(program->x()+program->w()+4, y(), 20, 20);
-       shiftUp   = new geButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
-       shiftDown = new geButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
-       remove    = new geButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
+               button    = new geButton(0, 0, 196, G_GUI_UNIT);
+               program   = new geChoice(0, 0, 132, G_GUI_UNIT);
+               bypass    = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT);
+               shiftUp   = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
+               shiftDown = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
+               remove    = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
        end();
 
-       button->copy_label(m_plugin->getName().c_str());
+       m::model::PluginsLock l(m::model::plugins);
+
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+
+       remove->callback(cb_removePlugin, (void*)this);
+
+       if (!p.valid) {
+               button->copy_label(p.getUniqueId().c_str());
+               button->deactivate();
+               bypass->deactivate();
+               shiftUp->deactivate();
+               shiftDown->deactivate();
+               return;
+       }
+       button->copy_label(p.getName().c_str());
        button->callback(cb_openPluginWindow, (void*)this);
 
        program->callback(cb_setProgram, (void*)this);
 
-       for (int i=0; i<m_plugin->getNumPrograms(); i++)
-               program->add(u::gui::removeFltkChars(m_plugin->getProgramName(i)).c_str());
+       for (int i=0; i<p.getNumPrograms(); i++)
+               program->add(u::gui::removeFltkChars(p.getProgramName(i)).c_str());
 
        if (program->size() == 0) {
                program->add("-- no programs --\0");
                program->deactivate();
        }
        else
-               program->value(m_plugin->getCurrentProgram());
+               program->value(p.getCurrentProgram());
 
        bypass->callback(cb_setBypass, (void*)this);
        bypass->type(FL_TOGGLE_BUTTON);
-       bypass->value(m_plugin->isBypassed() ? 0 : 1);
+       bypass->value(p.isBypassed() ? 0 : 1);
 
        shiftUp->callback(cb_shiftUp, (void*)this);
        shiftDown->callback(cb_shiftDown, (void*)this);
-       remove->callback(cb_removePlugin, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+ID gePluginElement::getPluginId() const
+{
+       return m_pluginId;
 }
 
 
@@ -106,19 +129,9 @@ void gePluginElement::cb_setProgram      (Fl_Widget* v, void* p) { ((gePluginEle
 
 void gePluginElement::cb_shiftUp()
 {
-       /*nothing to do if there's only one plugin */
-
-       if (m::pluginHost::countPlugins(m_parentWin->stackType, m_parentWin->ch) == 1)
-               return;
+       const gdPluginList* parent = static_cast<const gdPluginList*>(window());
 
-       int pluginIndex = m::pluginHost::getPluginIndex(m_plugin->getId(),
-               m_parentWin->stackType, m_parentWin->ch);
-
-       if (pluginIndex == 0)  // first of the stack, do nothing
-               return;
-
-       c::plugin::swapPlugins(m_parentWin->ch, pluginIndex, pluginIndex-1, m_parentWin->stackType);
-       m_parentWin->refreshList();
+       c::plugin::swapPlugins(m_pluginId, parent->getPrevElement(*this).getPluginId(), m_channelId);
 }
 
 
@@ -127,19 +140,9 @@ void gePluginElement::cb_shiftUp()
 
 void gePluginElement::cb_shiftDown()
 {
-       /*nothing to do if there's only one plugin */
-
-       if (m::pluginHost::countPlugins(m_parentWin->stackType, m_parentWin->ch) == 1)
-               return;
-
-       int pluginIndex = m::pluginHost::getPluginIndex(m_plugin->getId(), m_parentWin->stackType, m_parentWin->ch);
-       int stackSize   = m::pluginHost::getStack(m_parentWin->stackType, m_parentWin->ch).size();
+       const gdPluginList* parent = static_cast<const gdPluginList*>(window());
 
-       if (pluginIndex == stackSize-1)  // last one in the stack, do nothing
-               return;
-
-       c::plugin::swapPlugins(m_parentWin->ch, pluginIndex, pluginIndex+1, m_parentWin->stackType);
-       m_parentWin->refreshList();
+       c::plugin::swapPlugins(m_pluginId, parent->getNextElement(*this).getPluginId(), m_channelId);
 }
 
 
@@ -150,11 +153,10 @@ void gePluginElement::cb_removePlugin()
 {
        /* Any subwindow linked to the plugin must be destroyed first. The 
        pluginWindow has id = id_plugin + 1, because id=0 is reserved for the parent 
-       window 'add plugin' (i.e. this).*/
+       window 'add plugin'.*/
        
-       m_parentWin->delSubWindow(m_plugin->getId() + 1);
-       c::plugin::freePlugin(m_parentWin->ch, m_plugin->getId(), m_parentWin->stackType);
-       m_parentWin->refreshList();
+       static_cast<gdWindow*>(window())->delSubWindow(m_pluginId + 1);
+       c::plugin::freePlugin(m_pluginId, m_channelId);
 }
 
 
@@ -163,30 +165,29 @@ void gePluginElement::cb_removePlugin()
 
 void gePluginElement::cb_openPluginWindow()
 {
+       m::model::PluginsLock l(m::model::plugins);
+
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+
        /* The new pluginWindow has id = id_plugin + 1, because id=0 is reserved for 
-       the parent window 'add plugin' (i.e. this). */
+       the parent window 'add plugin'. */
 
-       int pwid = m_plugin->getId() + 1;
-       
-       gdWindow* w;
-       if (m_plugin->hasEditor()) {
-               if (m_plugin->isEditorOpen()) {
-                       gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has editor but it's already visible\n");
-                       m_parentWin->getChild(pwid)->show();  // Raise it to top
-                       return;
-               }
-               gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has editor, window id=%d\n", pwid);
-               w = new gdPluginWindowGUI(m_plugin);
+       int pwid = m_pluginId + 1;
+
+       gdWindow* parent = static_cast<gdWindow*>(window());
+       gdWindow* child  = parent->getChild(pwid);
+
+       if (child != nullptr) {
+               child->show();  // Raise it to top
        }
        else {
-               w = new gdPluginWindow(m_plugin);
-               gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid);
+               if (p.hasEditor())
+                       child = new gdPluginWindowGUI(m_pluginId);
+               else 
+                       child = new gdPluginWindow(m_pluginId);
+               child->setId(pwid);
+               parent->addSubWindow(child);
        }
-       
-       if (m_parentWin->hasWindow(pwid))
-               m_parentWin->delSubWindow(pwid);
-       w->setId(pwid);
-       m_parentWin->addSubWindow(w);
 }
 
 
@@ -195,7 +196,7 @@ void gePluginElement::cb_openPluginWindow()
 
 void gePluginElement::cb_setBypass()
 {
-       m_plugin->toggleBypass();
+       c::plugin::toggleBypass(m_pluginId);
 }
 
 
@@ -204,8 +205,9 @@ void gePluginElement::cb_setBypass()
 
 void gePluginElement::cb_setProgram()
 {
-       c::plugin::setProgram(m_plugin, program->value());
+       c::plugin::setProgram(m_pluginId, program->value());
 }
+}} // giada::v::
 
 
 #endif // #ifdef WITH_VST
index c3b1f456c1b1b7636425e3471fb1d810b0f58887..db4de5776a9ccaf3ea255b00526d44217d9064a0 100644 (file)
 #define GE_PLUGIN_ELEMENT_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
-class gdPluginList;
 class geChoice;
 class geButton;
 
 
-class gePluginElement : public Fl_Group
+namespace giada {
+namespace v
 {
-private:
+class gdPluginList;
+class gePluginElement : public Fl_Pack
+{
+public:
+
+       gePluginElement(ID pluginId, ID channelId, int x, int y, int w);
+
+       ID getPluginId() const;
 
-       gdPluginList*     m_parentWin;
-       giada::m::Plugin* m_plugin;
+       geButton* button;
+       geChoice* program;
+       geButton* bypass;
+       geButton* shiftUp;
+       geButton* shiftDown;
+       geButton* remove;
+
+private:
 
        static void cb_removePlugin(Fl_Widget* v, void* p);
        static void cb_openPluginWindow(Fl_Widget* v, void* p);
@@ -60,17 +73,11 @@ private:
        void cb_shiftDown();
        void cb_setProgram();
 
-public:
-
-       geButton* button;
-       geChoice* program;
-       geButton* bypass;
-       geButton* shiftUp;
-       geButton* shiftDown;
-       geButton* remove;
-
-       gePluginElement(gdPluginList* gdp, giada::m::Plugin* p, int x, int y, int w);
+       ID m_channelId;
+       ID m_pluginId;
 };
+}} // giada::v::
+
 
 #endif
 
index f7a9624e2c2bbb9012cac39dfe4d65f17f771821..0fba1cb520af3808e0a6cc6747077c546a27bf1b 100644 (file)
 #ifdef WITH_VST
 
 
-#include "../../../core/plugin.h"
-#include "../../../core/const.h"
-#include "../../../glue/plugin.h"
-#include "../basics/boxtypes.h"
-#include "../basics/box.h"
-#include "../basics/slider.h"
+#include "core/model/model.h"
+#include "core/plugin.h"
+#include "core/const.h"
+#include "glue/plugin.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/slider.h"
 #include "pluginParameter.h"
 
 
-using std::string;
-using namespace giada;
-using namespace giada::c;
-
-
-gePluginParameter::gePluginParameter(int paramIndex, m::Plugin* p, int X, int Y, 
-       int W, int labelWidth)
-       : Fl_Group    (X, Y, W, G_GUI_UNIT), 
-         m_paramIndex(paramIndex), 
-         m_plugin    (p)
+namespace giada {
+namespace v
 {
+gePluginParameter::gePluginParameter(int paramIndex, ID pluginId, 
+       int X, int Y, int W, int labelWidth)
+: Fl_Group    (X, Y, W, G_GUI_UNIT), 
+  m_pluginId  (pluginId),
+  m_paramIndex(paramIndex)
+{
+       m::model::PluginsLock l(m::model::plugins);
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+
        begin();
 
+               const int VALUE_WIDTH = 100;
+
                m_label = new geBox(x(), y(), labelWidth, G_GUI_UNIT);
-               m_label->copy_label(m_plugin->getParameterName(m_paramIndex).c_str());
+               m_label->copy_label(p.getParameterName(m_paramIndex).c_str());
                m_label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
 
                m_slider = new geSlider(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN, y(), 
                        w()-(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN)-VALUE_WIDTH, G_GUI_UNIT);
-               m_slider->value(m_plugin->getParameter(m_paramIndex));
+               m_slider->value(p.getParameter(m_paramIndex));
                m_slider->callback(cb_setValue, (void*)this);
 
                m_value = new geBox(m_slider->x()+m_slider->w()+G_GUI_OUTER_MARGIN, y(), VALUE_WIDTH, G_GUI_UNIT);
@@ -80,7 +84,8 @@ void gePluginParameter::cb_setValue(Fl_Widget* v, void* p)  { ((gePluginParamete
 
 void gePluginParameter::cb_setValue()
 {
-       plugin::setParameter(m_plugin, m_paramIndex, m_slider->value());
+       c::plugin::setParameter(m_pluginId, m_paramIndex, m_slider->value(), 
+               /*gui=*/true);
 }
 
 
@@ -89,12 +94,18 @@ void gePluginParameter::cb_setValue()
 
 void gePluginParameter::update(bool changeSlider)
 {
-       string v = m_plugin->getParameterText(m_paramIndex) + " " +
-                       m_plugin->getParameterLabel(m_paramIndex);
+       m::model::PluginsLock l(m::model::plugins);
+       const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
+
+       std::string v = p.getParameterText(m_paramIndex) + " " +
+                       p.getParameterLabel(m_paramIndex);
+
        m_value->copy_label(v.c_str());
+
        if (changeSlider)
-               m_slider->value(m_plugin->getParameter(m_paramIndex));
+               m_slider->value(p.getParameter(m_paramIndex));
 }
+}} // giada::v::
 
 
 #endif // #ifdef WITH_VST
index 266de5e39008d5a5d6011afc58d3577d931a7559..a190db0d14ffe0cdc0b25670f1538f5e13fa10f7 100644 (file)
 
 
 #include <FL/Fl_Group.H>
+#include "core/types.h"
 
 
 class geBox;
 class geSlider;
 
 
+namespace giada {
+namespace v
+{
 class gePluginParameter : public Fl_Group
 {
-private:
+public:
 
-       static const int VALUE_WIDTH = 100;
+       gePluginParameter(int paramIndex, ID pluginId, int x, int y, int w,
+               int labelWidth);
 
-       int m_paramIndex;
-       giada::m::Plugin* m_plugin;
+       void update(bool changeSlider);
 
-       geBox*    m_label;
-       geSlider* m_slider;
-       geBox*    m_value;
+private:
 
        static void cb_setValue(Fl_Widget* v, void* p);
        void cb_setValue();
 
-public:
-
-       gePluginParameter(int paramIndex, giada::m::Plugin* p, int x, int y, int w, int labelWidth);
+       ID  m_pluginId;
+       int m_paramIndex;
 
-       void update(bool changeSlider);
+       geBox*    m_label;
+       geSlider* m_slider;
+       geBox*    m_value;
 };
+}} // giada::v::
 
 
 #endif
index 35ba256b1ac71134e9a9a75802e9c2650dca912a..dfe0f8264751e9934f5be150ff446c9317f71c73 100644 (file)
 
 
 #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/string.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 "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/waveFx.h"  
+#include "glue/channel.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "utils/math.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
 #include "waveTools.h"
 #include "boostTool.h"
 
 
-using namespace giada;
-
-
-geBoostTool::geBoostTool(int X, int Y, m::SampleChannel* ch)
-  : Fl_Group(X, Y, 220, 20),
-    ch      (ch)
+namespace giada {
+namespace v 
+{
+geBoostTool::geBoostTool(int X, int Y)
+: Fl_Pack(X, Y, 220, G_GUI_UNIT)
 {
-  begin();
-    label     = new geBox(x(), y(), u::gui::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();
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
 
-  dial->range(1.0f, 10.0f);
-  dial->callback(cb_setBoost, (void*)this);
-  dial->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
+       begin();
+               label     = new geBox   (0, 0, u::gui::getStringWidth("Boost"), G_GUI_UNIT, "Boost", FL_ALIGN_RIGHT);
+               dial      = new geDial  (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+               input     = new geInput (0, 0, 70, G_GUI_UNIT);
+               normalize = new geButton(0, 0, 70, G_GUI_UNIT, "Normalize");
+       end();
 
-  input->callback(cb_setBoostNum, (void*)this);
+       dial->range(1.0f, 10.0f);
+       dial->callback(cb_setBoost, (void*)this);
+       dial->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
 
-  normalize->callback(cb_normalize, (void*)this);
+       input->callback(cb_setBoostNum, (void*)this);
 
-  refresh();
+       normalize->callback(cb_normalize, (void*)this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geBoostTool::refresh()
+void geBoostTool::rebuild()
 {
-  using namespace giada::u;
+       /*
+       const m::SampleChannel* ch = static_cast<gdSampleEditor*>(window())->ch;
 
-  input->value(string::fToString(math::linearToDB(ch->getBoost()), 2).c_str());  // 2 digits
-  // A dial greater than it's max value goes crazy
-  dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);
+       input->value(u::string::fToString(u::math::linearToDB(ch->getBoost()), 2).c_str());  // 2 digits
+       // A dial greater than it's max value goes crazy
+       dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);*/
 }
 
 
@@ -94,15 +95,9 @@ void geBoostTool::cb_normalize  (Fl_Widget* w, void* p) { ((geBoostTool*)p)->cb_
 
 void geBoostTool::cb_setBoost()
 {
-  using namespace giada::c;
-
-  if (Fl::event() == FL_DRAG)
-    channel::setBoost(ch, dial->value());
-  else 
-  if (Fl::event() == FL_RELEASE) {
-    channel::setBoost(ch, dial->value());
-    static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
-  }
+       /*const m::SampleChannel* ch = static_cast<gdSampleEditor*>(window())->ch;
+
+       c::channel::setBoost(ch->id, dial->value());*/
 }
 
 
@@ -111,10 +106,9 @@ void geBoostTool::cb_setBoost()
 
 void geBoostTool::cb_setBoostNum()
 {
-  using namespace giada;
+       /*const m::SampleChannel* ch = static_cast<gdSampleEditor*>(window())->ch;
 
-  c::channel::setBoost(ch, u::math::dBtoLinear(atof(input->value())));
-  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
+       c::channel::setBoost(ch->id, u::math::dBtoLinear(atof(input->value())));*/
 }
 
 
@@ -123,10 +117,6 @@ void geBoostTool::cb_setBoostNum()
 
 void geBoostTool::cb_normalize()
 {
-  using namespace giada;
-
-  float val = m::wfx::normalizeSoft(*ch->wave);
-  c::channel::setBoost(ch, val); // it's like a fake user moving the dial 
-  static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
 }
 
+}} // giada::v::
index 496717ca4d0033e35424b1e3200a88cae809e5be..bd47c6dbbae45c01dce1798bb23b54b638de0b33 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_BOOST_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geDial;
@@ -38,16 +38,18 @@ class geButton;
 class geBox;
 
 
-class geBoostTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
+class geBoostTool : public Fl_Pack
+{
+public:
 
-       giada::m::SampleChannel* ch;
+       geBoostTool(int x, int y);
 
-       geBox*    label;
-       geDial*   dial;
-       geInput*  input;
-       geButton* normalize;
+       void rebuild();
+
+private:
 
        static void cb_setBoost(Fl_Widget* w, void* p);
        static void cb_setBoostNum(Fl_Widget* w, void* p);
@@ -56,12 +58,12 @@ private:
        void cb_setBoostNum();
        void cb_normalize();
 
-public:
-
-       geBoostTool(int x, int y, giada::m::SampleChannel* ch);
-
-       void refresh();
+       geBox*    label;
+       geDial*   dial;
+       geInput*  input;
+       geButton* normalize;
 };
+}} // giada::v::
 
 
 #endif
index 8d12056c349e6b886f6778f4527c8f706cd839b8..c8560f3da182b1cc3b4fc73c3046c2e3666ad4e2 100644 (file)
 
 
 #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 "../../../utils/string.h"
-#include "../../dialogs/sampleEditor.h"
-#include "../basics/dial.h"
-#include "../basics/input.h"
-#include "../basics/box.h"
-#include "../basics/button.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/waveFx.h"  
+#include "glue/channel.h"
+#include "utils/gui.h"
+#include "utils/math.h"
+#include "utils/string.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
 #include "waveTools.h"
 #include "panTool.h"
 
 
-using std::string;
-using namespace giada;
-
-
-gePanTool::gePanTool(int x, int y, m::SampleChannel* ch)
-  : Fl_Group(x, y, 200, 20),
-    ch      (ch)
+namespace giada {
+namespace v 
+{
+gePanTool::gePanTool(ID channelId, int x, int y)
+: Fl_Pack    (x, y, 200, G_GUI_UNIT),
+  m_channelId(channelId)
 {
-  begin();
-    label = new geBox(x, y, u::gui::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();
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
 
-  dial->range(0.0f, 1.0f);
-  dial->callback(cb_panning, (void*)this);
+       begin();
+               label = new geBox   (0, 0, u::gui::getStringWidth("Pan"), G_GUI_UNIT, "Pan", FL_ALIGN_RIGHT);
+               dial  = new geDial  (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+               input = new geInput (0, 0, 70, G_GUI_UNIT);
+               reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
+       end();
 
-  input->align(FL_ALIGN_RIGHT);
-  input->readonly(1);
-  input->cursor_color(FL_WHITE);
+       dial->range(0.0f, 1.0f);
+       dial->callback(cb_panning, (void*)this);
 
-  reset->callback(cb_panReset, (void*)this);
+       input->align(FL_ALIGN_RIGHT);
+       input->readonly(1);
+       input->cursor_color(FL_WHITE);
 
-  refresh();
+       reset->callback(cb_panReset, (void*)this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gePanTool::refresh()
+void gePanTool::rebuild()
 {
-  dial->value(ch->getPan());
-
-  if (ch->getPan() < 0.5f) {
-    string tmp = u::string::iToString((int) ((-ch->getPan() * 200.0f) + 100.0f)) + " L";
-    input->value(tmp.c_str());
-  }
-  else 
-  if (ch->getPan() == 0.5)
-    input->value("C");
-  else {
-    string tmp = u::string::iToString((int) ((ch->getPan() * 200.0f) - 100.0f)) + " R";
-    input->value(tmp.c_str());
-  }
+       float p;
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               p = static_cast<m::SampleChannel&>(c).getPan();
+       });
+
+       dial->value(p);
+
+       if (p < 0.5f) {
+               std::string tmp = u::string::iToString((int) ((-p * 200.0f) + 100.0f)) + " L";
+               input->value(tmp.c_str());
+       }
+       else 
+       if (p == 0.5)
+               input->value("C");
+       else {
+               std::string tmp = u::string::iToString((int) ((p * 200.0f) - 100.0f)) + " R";
+               input->value(tmp.c_str());
+       }
 }
 
 
@@ -104,7 +111,7 @@ void gePanTool::cb_panReset(Fl_Widget* w, void* p) { ((gePanTool*)p)->cb_panRese
 
 void gePanTool::cb_panning()
 {
-  c::channel::setPanning(ch, dial->value());
+       c::channel::setPan(m_channelId, dial->value());
 }
 
 
@@ -113,5 +120,7 @@ void gePanTool::cb_panning()
 
 void gePanTool::cb_panReset()
 {
-  c::channel::setPanning(ch, 0.5f);
-}
\ No newline at end of file
+       c::channel::setPan(m_channelId, 0.5f);
+}
+
+}} // giada::v::
index 17170166a3e32a01687d65e4df43d5e5238023e2..d3784b841ccdf0f401bb9337fec7f859985f1b19 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_PAN_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geDial;
@@ -38,28 +38,33 @@ class geButton;
 class geBox;
 
 
-class gePanTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
+class gePanTool : public Fl_Pack
+{
+public:
 
-  giada::m::SampleChannel* ch;
+  gePanTool(ID channelId, int x, int y);
 
-  geBox*    label;
-  geDial*   dial;
-  geInput*  input;
-  geButton* reset;
+  void rebuild();
+
+private:
 
   static void cb_panning (Fl_Widget* w, void* p);
   static void cb_panReset(Fl_Widget* w, void* p);
   void cb_panning();
   void cb_panReset();
 
-public:
+  ID m_channelId;
 
-  gePanTool(int x, int y, giada::m::SampleChannel* ch);
+  geBox*    label;
+  geDial*   dial;
+  geInput*  input;
+  geButton* reset;
 
-  void refresh();
 };
+}} // giada::v::
 
 
 #endif
index 5770435fe3eeb96022df7d48e48310bd727224a4..83d8b7aeea871e569943f82729733c7d3522fd0f 100644 (file)
@@ -1,3 +1,4 @@
+
 /* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
 
 
 #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 "../../../utils/string.h"
-#include "../../dialogs/sampleEditor.h"
-#include "../basics/dial.h"
-#include "../basics/input.h"
-#include "../basics/box.h"
-#include "../basics/button.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/graphics.h"  
+#include "core/clock.h"
+#include "glue/channel.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
 #include "pitchTool.h"
 
 
-using namespace giada;
-
-
-gePitchTool::gePitchTool(int x, int y, m::SampleChannel* ch)
-  : Fl_Group(x, y, 600, 20),
-    ch      (ch)
+namespace giada {
+namespace v 
+{
+gePitchTool::gePitchTool(ID channelId, int x, int y)
+: Fl_Pack    (x, y, 600, G_GUI_UNIT),
+  m_channelId(channelId)
 {
-  begin();
-    label       = new geBox(x, y, u::gui::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();
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+       
+       begin();
+               label       = new geBox   (0, 0, u::gui::getStringWidth("Pitch"), G_GUI_UNIT, "Pitch", FL_ALIGN_RIGHT);
+               dial        = new geDial  (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+               input       = new geInput (0, 0, 70, G_GUI_UNIT);
+               pitchToBar  = new geButton(0, 0, 70, G_GUI_UNIT, "To bar");
+               pitchToSong = new geButton(0, 0, 70, G_GUI_UNIT, "To song");
+               pitchHalf   = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm);
+               pitchDouble = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm);
+               pitchReset  = new geButton(0, 0, 70, G_GUI_UNIT, "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);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gePitchTool::refresh()
+void gePitchTool::rebuild()
 {
-  dial->value(ch->getPitch());
-  input->value(u::string::fToString(ch->getPitch(), 4).c_str()); // 4 digits
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               float p = static_cast<m::SampleChannel&>(c).getPitch();
+               
+               dial->value(p);
+               input->value(u::string::fToString(p, 4).c_str()); // 4 digits
+       });
 }
 
 
@@ -104,7 +112,7 @@ void gePitchTool::cb_setPitchNum   (Fl_Widget* w, void* p) { ((gePitchTool*)p)->
 
 void gePitchTool::cb_setPitch()
 {
-  c::channel::setPitch(ch, dial->value());
+       c::channel::setPitch(m_channelId, dial->value());
 }
 
 
@@ -113,7 +121,7 @@ void gePitchTool::cb_setPitch()
 
 void gePitchTool::cb_setPitchNum()
 {
-  c::channel::setPitch(ch, atof(input->value()));
+       c::channel::setPitch(m_channelId, atof(input->value()));
 }
 
 
@@ -122,7 +130,7 @@ void gePitchTool::cb_setPitchNum()
 
 void gePitchTool::cb_setPitchHalf()
 {
-  c::channel::setPitch(ch, dial->value()/2);
+       c::channel::setPitch(m_channelId, dial->value()/2);
 }
 
 
@@ -131,7 +139,7 @@ void gePitchTool::cb_setPitchHalf()
 
 void gePitchTool::cb_setPitchDouble()
 {
-  c::channel::setPitch(ch, dial->value()*2);
+       c::channel::setPitch(m_channelId, dial->value()*2);
 }
 
 
@@ -140,8 +148,13 @@ void gePitchTool::cb_setPitchDouble()
 
 void gePitchTool::cb_setPitchToBar()
 {
-  // TODO - opaque channel's count
-  c::channel::setPitch(ch, (ch->getEnd()) / (float) m::clock::getFramesInBar());
+       Frame end;
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               end = static_cast<m::SampleChannel&>(c).getEnd();
+       });
+
+       c::channel::setPitch(m_channelId, end / (float) m::clock::getFramesInBar());
 }
 
 
@@ -150,8 +163,13 @@ void gePitchTool::cb_setPitchToBar()
 
 void gePitchTool::cb_setPitchToSong()
 {
-  // TODO - opaque channel's count
-  c::channel::setPitch(ch, ch->getEnd() / (float) m::clock::getFramesInLoop());
+       Frame end;
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               end = static_cast<m::SampleChannel&>(c).getEnd();
+       });
+
+       c::channel::setPitch(m_channelId, end / (float) m::clock::getFramesInLoop());
 }
 
 
@@ -160,5 +178,7 @@ void gePitchTool::cb_setPitchToSong()
 
 void gePitchTool::cb_resetPitch()
 {
-  c::channel::setPitch(ch, G_DEFAULT_PITCH);
+       c::channel::setPitch(m_channelId, G_DEFAULT_PITCH);
 }
+
+}} // giada::v::
index ee6594e4b5479a8440407b104422869d04689691..04422ce7045e6fe8ecf2406b5d36ead6db5ea435 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_PITCH_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geDial;
@@ -38,42 +38,46 @@ class geButton;
 class geBox;
 
 
-class gePitchTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
+class gePitchTool : public Fl_Pack
+{
+public:
 
-  giada::m::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);
-  void cb_setPitch();
-  void cb_setPitchToBar();
-  void cb_setPitchToSong();
-  void cb_setPitchHalf();
-  void cb_setPitchDouble();
-  void cb_resetPitch();
-  void cb_setPitchNum();
+       gePitchTool(ID channelId, int x, int y);
 
-public:
+       void rebuild();
 
-  gePitchTool(int x, int y, giada::m::SampleChannel* ch);
+private:
 
-  void refresh();
+       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);
+       void cb_setPitch();
+       void cb_setPitchToBar();
+       void cb_setPitchToSong();
+       void cb_setPitchHalf();
+       void cb_setPitchDouble();
+       void cb_resetPitch();
+       void cb_setPitchNum();
+
+       ID m_channelId;
+
+       geBox*    label;
+       geDial*   dial;
+       geInput*  input;
+       geButton* pitchToBar;
+       geButton* pitchToSong;
+       geButton* pitchHalf;
+       geButton* pitchDouble;
+       geButton* pitchReset;
 };
+}} // giada::v::
 
 
 #endif
index 9cf66d5b10222e49857a87a0f27ea3d44790d099..5a091d10582dff5eb28aef971eea98a38368e614 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <FL/Fl.H>
-#include "../../../core/sampleChannel.h"
-#include "../../../core/wave.h"
-#include "../../../glue/channel.h"
-#include "../../../glue/sampleEditor.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 "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/wave.h"
+#include "glue/channel.h"
+#include "glue/sampleEditor.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
 #include "waveTools.h"
 #include "rangeTool.h"
 
 
-using namespace giada;
-
-
-geRangeTool::geRangeTool(int x, int y, giada::m::SampleChannel* ch)
-       : Fl_Group(x, y, 280, G_GUI_UNIT),
-               m_ch    (ch)
+namespace giada {
+namespace v 
 {
+geRangeTool::geRangeTool(ID channelId, ID waveId, int x, int y)
+: Fl_Pack    (x, y, 280, G_GUI_UNIT),
+  m_channelId(channelId),
+  m_waveId   (waveId)
+{
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+
        begin();
-               m_label = new geBox(x, y, u::gui::getStringWidth("Range"), G_GUI_UNIT, "Range", FL_ALIGN_RIGHT);
-               m_begin = new geInput(m_label->x()+m_label->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT);
-               m_end   = new geInput(m_begin->x()+m_begin->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT);
-               m_reset = new geButton(m_end->x()+m_end->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT, "Reset");
+               m_label = new geBox   (0, 0, u::gui::getStringWidth("Range"), G_GUI_UNIT, "Range", FL_ALIGN_RIGHT);
+               m_begin = new geInput (0, 0, 70, G_GUI_UNIT);
+               m_end   = new geInput (0, 0, 70, G_GUI_UNIT);
+               m_reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
        end();
 
        m_begin->type(FL_INT_INPUT);
@@ -63,18 +69,19 @@ geRangeTool::geRangeTool(int x, int y, giada::m::SampleChannel* ch)
        m_end->callback(cb_setChanPos, this);
 
        m_reset->callback(cb_resetStartEnd, this);
-
-       refresh();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geRangeTool::refresh()
+void geRangeTool::rebuild()
 {
-       m_begin->value(u::string::iToString(m_ch->getBegin()).c_str());
-       m_end->value(u::string::iToString(m_ch->getEnd()).c_str());
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               m_begin->value(std::to_string(static_cast<m::SampleChannel&>(c).getBegin()).c_str());
+               m_end->value(std::to_string(static_cast<m::SampleChannel&>(c).getEnd()).c_str());
+       });
 }
 
 
@@ -90,8 +97,7 @@ void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->c
 
 void geRangeTool::cb_setChanPos()
 {
-       c::sampleEditor::setBeginEnd(m_ch, atoi(m_begin->value()), atoi(m_end->value()));
-       static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform(); // TODO - glue's business!
+       c::sampleEditor::setBeginEnd(m_channelId, atoi(m_begin->value()), atoi(m_end->value()));
 }
 
 
@@ -100,6 +106,13 @@ void geRangeTool::cb_setChanPos()
 
 void geRangeTool::cb_resetStartEnd()
 {
-       c::sampleEditor::setBeginEnd(m_ch, 0, m_ch->wave->getSize() - 1);
-       static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform(); // TODO - glue's business!
+       Frame waveSize;
+       m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w)
+       { 
+               waveSize = w.getSize();
+       });
+       
+       c::sampleEditor::setBeginEnd(m_channelId, 0, waveSize - 1);
 }
+
+}} // giada::v::
index 2e41bf01326c4934d52ba0d73466978ba4578831..d96f1a116b2a3f28b5aeddc0a7eed86af7b45b29 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_RANGE_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geInput;
@@ -37,28 +37,33 @@ class geButton;
 class geBox;
 
 
-class geRangeTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
+class geRangeTool : public Fl_Pack
+{
+public:
 
-  giada::m::SampleChannel* m_ch;
+       geRangeTool(ID channelId, ID waveId, int x, int y);
 
-  geBox*    m_label;
-  geInput*  m_begin;
-  geInput*  m_end;
-  geButton* m_reset;
+       void rebuild();
 
-  static void cb_setChanPos   (Fl_Widget* w, void* p);
-  static void cb_resetStartEnd(Fl_Widget* w, void* p);
-  void cb_setChanPos();
-  void cb_resetStartEnd();
+private:
 
-public:
+       static void cb_setChanPos   (Fl_Widget* w, void* p);
+       static void cb_resetStartEnd(Fl_Widget* w, void* p);
+       void cb_setChanPos();
+       void cb_resetStartEnd();
 
-  geRangeTool(int x, int y, giada::m::SampleChannel* ch);
+       ID m_channelId;
+       ID m_waveId;
 
-  void refresh();
+       geBox*    m_label;
+       geInput*  m_begin;
+       geInput*  m_end;
+       geButton* m_reset;
 };
+}} // giada::v::
 
 
 #endif
index 5ec899d18cd70f10ed37cd235f2834d2870922d5..e95a64f4d23a67312adba519ce498357e44eef71 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#include <cassert>
 #include <cstdlib>
-#include "../../../core/const.h"
-#include "../../../core/sampleChannel.h"
-#include "../../../utils/gui.h"
-#include "../../../utils/string.h"
-#include "../../../glue/sampleEditor.h"
-#include "../../dialogs/warnings.h"
-#include "../basics/input.h"
-#include "../basics/box.h"
-#include "../basics/button.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/const.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include "glue/sampleEditor.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
 #include "shiftTool.h"
 
 
-using namespace giada;
-
-
-geShiftTool::geShiftTool(int x, int y, giada::m::SampleChannel* ch)
-       : Fl_Group(x, y, 300, G_GUI_UNIT),
-               m_ch    (ch)
+namespace giada {
+namespace v 
+{
+geShiftTool::geShiftTool(ID channelId, ID waveId, int x, int y)
+: Fl_Pack    (x, y, 300, G_GUI_UNIT),
+  m_channelId(channelId),
+  m_waveId   (waveId)
 {
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
+
        begin();
-               m_label = new geBox(x, y, u::gui::getStringWidth("Shift"), G_GUI_UNIT, "Shift", FL_ALIGN_RIGHT);
-               m_shift = new geInput(m_label->x()+m_label->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT);
-               m_reset = new geButton(m_shift->x()+m_shift->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT, "Reset");
+               m_label = new geBox   (0, 0, u::gui::getStringWidth("Shift"), G_GUI_UNIT, "Shift", FL_ALIGN_RIGHT);
+               m_shift = new geInput (0, 0, 70, G_GUI_UNIT);
+               m_reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
        end();
 
        m_shift->type(FL_INT_INPUT);
        m_shift->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
-       m_shift->value(u::string::iToString(ch->shift).c_str());
        m_shift->callback(cb_setShift, (void*)this);
 
        m_reset->callback(cb_reset, (void*)this);
-
-       refresh();
 }
 
 
@@ -90,9 +94,12 @@ void geShiftTool::cb_reset()
 /* -------------------------------------------------------------------------- */
 
 
-void geShiftTool::refresh()
+void geShiftTool::rebuild()
 {
-       m_shift->value(u::string::iToString(m_ch->shift).c_str());
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               m_shift->value(std::to_string(static_cast<m::SampleChannel&>(c).shift).c_str());
+       });
 }
 
 
@@ -101,8 +108,6 @@ void geShiftTool::refresh()
 
 void geShiftTool::shift(int f)
 {
-       if (m_ch->isPlaying())
-               gdAlert("Can't shift sample while playing.");
-       else
-               c::sampleEditor::shift(m_ch, f);        
+       c::sampleEditor::shift(m_channelId, m_waveId, f);
 }
+}} // giada::v::
index 4734c0f1b391b1a7a5f99e5bd0b92a914a11c91e..35acae088db2618b0203a1c36bd03137261f7eec 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_SHIFT_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geInput;
@@ -37,15 +37,18 @@ class geButton;
 class geBox;
 
 
-class geShiftTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
+class geShiftTool : public Fl_Pack
+{
+public:
 
-       giada::m::SampleChannel* m_ch;
+       geShiftTool(ID channelId, ID waveId, int x, int y);
 
-       geBox*    m_label;
-       geInput*  m_shift;
-       geButton* m_reset;
+       void rebuild();
+
+private:
 
        static void cb_setShift(Fl_Widget* w, void* p);
        static void cb_reset(Fl_Widget* w, void* p);
@@ -54,12 +57,14 @@ private:
 
        void shift(int f);
 
-public:
-
-       geShiftTool(int x, int y, giada::m::SampleChannel* ch);
-
-       void refresh();
+       ID m_channelId;
+       ID m_waveId;
+       
+       geBox*    m_label;
+       geInput*  m_shift;
+       geButton* m_reset;
 };
+}} // giada::v::
 
 
 #endif
index 97e06d8e45602c8425650d987b767d20caf6b1e8..29eab51a31c585fcb67cc542b34237457e17c3d5 100644 (file)
 #include <cmath>
 #include <cstdlib>
 #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 "../../../utils/string.h"
-#include "../basics/dial.h"
-#include "../basics/input.h"
-#include "../basics/box.h"
-#include "../mainWindow/keyboard/channel.h"
+#include "core/channels/sampleChannel.h"
+#include "core/const.h"
+#include "core/model/model.h"
+#include "glue/channel.h"
+#include "utils/gui.h"
+#include "utils/math.h"
+#include "utils/string.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
 #include "volumeTool.h"
 
 
-using std::string;
-using namespace giada;
-
-
-geVolumeTool::geVolumeTool(int X, int Y, m::SampleChannel* ch)
-  : Fl_Group(X, Y, 150, 20),
-    ch      (ch)
+namespace giada {
+namespace v 
+{
+geVolumeTool::geVolumeTool(ID channelId, int X, int Y)
+: Fl_Pack    (X, Y, 150, G_GUI_UNIT),
+  m_channelId(channelId)
 {
-  begin();
-    label = new geBox  (x(), y(), u::gui::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();
+       type(Fl_Pack::HORIZONTAL);
+       spacing(G_GUI_INNER_MARGIN);
 
-  dial->range(0.0f, 1.0f);
-  dial->callback(cb_setVolume, (void*)this);
+       begin();
+               label = new geBox  (0, 0, u::gui::getStringWidth("Volume"), G_GUI_UNIT, "Volume", FL_ALIGN_RIGHT);
+               dial  = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
+               input = new geInput(0, 0, 70, G_GUI_UNIT);
+       end();
 
-  input->callback(cb_setVolumeNum, (void*)this);
+       dial->range(0.0f, 1.0f);
+       dial->callback(cb_setVolume, (void*)this);
 
-  refresh();
+       input->callback(cb_setVolumeNum, (void*)this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geVolumeTool::refresh()
+void geVolumeTool::rebuild()
 {
-  string tmp;
-  float dB = u::math::linearToDB(ch->volume);
-  if (dB > -INFINITY) tmp = u::string::fToString(dB, 2);  // 2 digits
-  else                tmp = "-inf";
-  input->value(tmp.c_str());
-  dial->value(ch->guiChannel->vol->value());
+       float volume;
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c) { volume = c.volume; });
+
+       std::string tmp = "-inf";
+       float dB = u::math::linearToDB(volume);
+       if (dB > -INFINITY) 
+               tmp = u::string::fToString(dB, 2);  // 2 digits
+       input->value(tmp.c_str());
+       dial->value(volume);
 }
 
 
@@ -90,10 +95,7 @@ void geVolumeTool::cb_setVolumeNum(Fl_Widget* w, void* p) { ((geVolumeTool*)p)->
 
 void geVolumeTool::cb_setVolume()
 {
-  using namespace giada;
-
-  c::channel::setVolume(ch, dial->value(), false, true);
-  refresh();
+       c::channel::setVolume(m_channelId, dial->value(), false, true);
 }
 
 
@@ -102,9 +104,8 @@ void geVolumeTool::cb_setVolume()
 
 void geVolumeTool::cb_setVolumeNum()
 {
-  using namespace giada;
-
-  float value = pow(10, (atof(input->value()) / 20)); // linear = 10^(dB/20)
-  c::channel::setVolume(ch, value, false, true);
-  dial->value(ch->guiChannel->vol->value());
+       float value = pow(10, (atof(input->value()) / 20)); // linear = 10^(dB/20)
+       c::channel::setVolume(m_channelId, value, false, true);
 }
+
+}} // giada::v::
index be9484a66e34b9f54f9a1e7fedd31549d49c082e..c9996a37f3e9656b03489b6366dba1b0c135d35e 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_VOLUME_TOOL_H
 
 
-#include <FL/Fl_Group.H>
+#include <FL/Fl_Pack.H>
 
 
 class geDial;
@@ -37,27 +37,31 @@ class geInput;
 class geBox;
 
 
-class geVolumeTool : public Fl_Group
+namespace giada {
+namespace v 
 {
-private:
-
-  giada::m::SampleChannel* ch;
+class geVolumeTool : public Fl_Pack
+{
+public:
 
-  geBox*   label;
-  geDial*  dial;
-  geInput* input;
+       geVolumeTool(ID channelId, int x, int y);
 
-  static void cb_setVolume   (Fl_Widget* w, void* p);
-       static void cb_setVolumeNum(Fl_Widget* w, void* p);
-  void cb_setVolume   ();
-  void cb_setVolumeNum();
+       void rebuild();
+       
+private:
 
-public:
+       ID m_channelId;
 
-  geVolumeTool(int x, int y, giada::m::SampleChannel* ch);
+       geBox*   label;
+       geDial*  dial;
+       geInput* input;
 
-  void refresh();
+       static void cb_setVolume   (Fl_Widget* w, void* p);
+       static void cb_setVolumeNum(Fl_Widget* w, void* p);
+       void cb_setVolume   ();
+       void cb_setVolumeNum();
 };
+}} // giada::v::
 
 
 #endif
index f3d6fce81d82fc8ec230fe6f1a861fd5a1f0d1b1..6853cce5964fbb6a7bd1ac2b7cef391bc1a412fb 100644 (file)
 #include <cstdint>
 #include <FL/Fl_Menu_Item.H>
 #include <FL/Fl_Menu_Button.H>
-#include "../../../core/sampleChannel.h"
-#include "../../../core/waveFx.h"
-#include "../../../glue/sampleEditor.h"
-#include "../basics/boxtypes.h"
-#include "../../../core/const.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/waveFx.h"
+#include "core/const.h"
+#include "glue/sampleEditor.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/dialogs/sampleEditor.h"
 #include "waveform.h"
 #include "waveTools.h"
 
 
-using namespace giada;
-
-
+namespace giada {
+namespace v 
+{
 namespace
 {
 enum class Menu
@@ -62,62 +64,68 @@ enum class Menu
 /* -------------------------------------------------------------------------- */
 
 
-void menuCallback(Fl_Widget* w, void* v)
+void menuCallback_(Fl_Widget* w, void* v)
 {
-       geWaveTools* wavetools = static_cast<geWaveTools*>(w);
-       Menu selectedItem = (Menu) (intptr_t) v;
+       const geWaveTools* wt = static_cast<geWaveTools*>(w);
+       
+       ID     channelId    = wt->channelId;
+       size_t waveId    = wt->waveId;
+       Menu   selectedItem = (Menu) (intptr_t) v;
 
-       int a = wavetools->waveform->getSelectionA();
-       int b = wavetools->waveform->getSelectionB();
+       int a = wt->waveform->getSelectionA();
+       int b = wt->waveform->getSelectionB();
 
        switch (selectedItem) {
                case Menu::CUT:
-                       c::sampleEditor::cut(wavetools->ch, a, b);
+                       c::sampleEditor::cut(channelId, waveId, a, b);
                        break;          
                case Menu::COPY:
-                       c::sampleEditor::copy(wavetools->ch, a, b);
+                       c::sampleEditor::copy(waveId, a, b);
                        break;          
                case Menu::PASTE:
-                       c::sampleEditor::paste(wavetools->ch, a);
+                       c::sampleEditor::paste(channelId, waveId, a);
                        break;
                case Menu::TRIM:
-                       c::sampleEditor::trim(wavetools->ch, a, b);
+                       c::sampleEditor::trim(channelId, waveId, a, b);
                        break;
                case Menu::SILENCE:
-                       c::sampleEditor::silence(wavetools->ch, a, b);
+                       c::sampleEditor::silence(waveId, a, b);
                        break;    
                case Menu::REVERSE:
-                       c::sampleEditor::reverse(wavetools->ch, a, b);
+                       c::sampleEditor::reverse(waveId, a, b);
                        break;                  
                case Menu::NORMALIZE:
-                       c::sampleEditor::normalizeHard(wavetools->ch, a, b);
+                       c::sampleEditor::normalizeHard(waveId, a, b);
                        break;  
                case Menu::FADE_IN:
-                       c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_IN);
+                       c::sampleEditor::fade(waveId, a, b, m::wfx::FADE_IN);
                        break;
                case Menu::FADE_OUT:
-                       c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_OUT);
+                       c::sampleEditor::fade(waveId, a, b, m::wfx::FADE_OUT);
                        break;
                case Menu::SMOOTH_EDGES:
-                       c::sampleEditor::smoothEdges(wavetools->ch, a, b);
+                       c::sampleEditor::smoothEdges(waveId, a, b);
                        break;
                case Menu::SET_BEGIN_END:
-                       c::sampleEditor::setBeginEnd(wavetools->ch, a, b);
+                       c::sampleEditor::setBeginEnd(waveId, a, b);
                        break;          
                case Menu::TO_NEW_CHANNEL:
-                       c::sampleEditor::toNewChannel(wavetools->ch, a, b);
+                       c::sampleEditor::toNewChannel(waveId, a, b);
                        break;
        }
 }
 }; // {anonymous}
 
 
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
-geWaveTools::geWaveTools(int x, int y, int w, int h, m::SampleChannel* ch, const char* l)
-       : Fl_Scroll(x, y, w, h, l),
-               ch       (ch)
+geWaveTools::geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h)
+: Fl_Scroll(x, y, w, h, nullptr),
+  channelId(channelId),
+  waveId(waveId)
 {
        type(Fl_Scroll::HORIZONTAL_ALWAYS);
        hscrollbar.color(G_COLOR_GREY_2);
@@ -125,7 +133,7 @@ geWaveTools::geWaveTools(int x, int y, int w, int h, m::SampleChannel* ch, const
        hscrollbar.labelcolor(G_COLOR_LIGHT_1);
        hscrollbar.slider(G_CUSTOM_BORDER_BOX);
 
-       waveform = new geWaveform(x, y, w, h-24, ch);
+       waveform = new v::geWaveform(channelId, waveId, x, y, w, h-24);
 }
 
 
@@ -133,19 +141,22 @@ geWaveTools::geWaveTools(int x, int y, int w, int h, m::SampleChannel* ch, const
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveTools::updateWaveform()
+void geWaveTools::rebuild()
 {
-       waveform->refresh();
+       waveform->rebuild();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveTools::redrawWaveformAsync()
+void geWaveTools::refresh()
 {
-       if (ch->isPreview())
-               waveform->redraw();
+       m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+       {
+               if (c.isPreview())
+                       waveform->redraw();
+       });
 }
 
 
@@ -154,13 +165,11 @@ void geWaveTools::redrawWaveformAsync()
 
 void geWaveTools::resize(int x, int y, int w, int h)
 {
+       Fl_Widget::resize(x, y, w, 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);
+               waveform->rebuild();
        }
 
        if (this->w() > waveform->w())
@@ -179,7 +188,7 @@ int geWaveTools::handle(int e)
 {
        switch (e) {
                case FL_MOUSEWHEEL: {
-                       waveform->setZoom(Fl::event_dy());
+                       waveform->setZoom(Fl::event_dy() == 1 ? geWaveform::Zoom::OUT : geWaveform::Zoom::IN);
                        redraw();
                        return 1;
                }
@@ -202,26 +211,21 @@ int geWaveTools::handle(int e)
 void geWaveTools::openMenu()
 {
        Fl_Menu_Item menu[] = {
-               {"Cut",                 0, menuCallback, (void*) Menu::CUT},
-               {"Copy",                0, menuCallback, (void*) Menu::COPY},
-               {"Paste",               0, menuCallback, (void*) Menu::PASTE},
-               {"Trim",                0, menuCallback, (void*) Menu::TRIM},
-               {"Silence",             0, menuCallback, (void*) Menu::SILENCE},
-               {"Reverse",             0, menuCallback, (void*) Menu::REVERSE},
-               {"Normalize",           0, menuCallback, (void*) Menu::NORMALIZE},
-               {"Fade in",             0, menuCallback, (void*) Menu::FADE_IN},
-               {"Fade out",            0, menuCallback, (void*) Menu::FADE_OUT},
-               {"Smooth edges",        0, menuCallback, (void*) Menu::SMOOTH_EDGES},
-               {"Set begin/end here",  0, menuCallback, (void*) Menu::SET_BEGIN_END},
-               {"Copy to new channel", 0, menuCallback, (void*) Menu::TO_NEW_CHANNEL},
+               {"Cut",                 0, menuCallback_, (void*) Menu::CUT},
+               {"Copy",                0, menuCallback_, (void*) Menu::COPY},
+               {"Paste",               0, menuCallback_, (void*) Menu::PASTE},
+               {"Trim",                0, menuCallback_, (void*) Menu::TRIM},
+               {"Silence",             0, menuCallback_, (void*) Menu::SILENCE},
+               {"Reverse",             0, menuCallback_, (void*) Menu::REVERSE},
+               {"Normalize",           0, menuCallback_, (void*) Menu::NORMALIZE},
+               {"Fade in",             0, menuCallback_, (void*) Menu::FADE_IN},
+               {"Fade out",            0, menuCallback_, (void*) Menu::FADE_OUT},
+               {"Smooth edges",        0, menuCallback_, (void*) Menu::SMOOTH_EDGES},
+               {"Set begin/end here",  0, menuCallback_, (void*) Menu::SET_BEGIN_END},
+               {"Copy to new channel", 0, menuCallback_, (void*) Menu::TO_NEW_CHANNEL},
                {0}
        };
 
-       if (ch->status == ChannelStatus::PLAY) {
-               menu[(int)Menu::CUT].deactivate();
-               menu[(int)Menu::TRIM].deactivate();
-       }
-
        if (!waveform->isSelected()) {
                menu[(int)Menu::CUT].deactivate();
                menu[(int)Menu::COPY].deactivate();             
@@ -236,14 +240,17 @@ void geWaveTools::openMenu()
                menu[(int)Menu::TO_NEW_CHANNEL].deactivate();           
        }
 
-       Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50);
-       b->box(G_CUSTOM_BORDER_BOX);
-       b->textsize(G_GUI_FONT_SIZE_BASE);
-       b->textcolor(G_COLOR_LIGHT_2);
-       b->color(G_COLOR_GREY_2);
+       Fl_Menu_Button b(0, 0, 100, 50);
+       b.box(G_CUSTOM_BORDER_BOX);
+       b.textsize(G_GUI_FONT_SIZE_BASE);
+       b.textcolor(G_COLOR_LIGHT_2);
+       b.color(G_COLOR_GREY_2);
 
-       const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
-       if (m)
+       const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
+       if (m != nullptr)
                m->do_callback(this, m->user_data());
+
        return;
 }
+
+}} // giada::v::
index af095dc809bdf1409f51b00900bbe8efbfb70ed8..46846ab99862484c72b8c596c52f6b34f5d3b2a7 100644 (file)
 #include <FL/Fl_Scroll.H>
 
 
+namespace giada {
+namespace v 
+{
 class geWaveform;
-
-
 class geWaveTools : public Fl_Scroll
 {
-private:
-
-       void openMenu();
-
 public:
 
-       giada::m::SampleChannel* ch;
-       geWaveform* waveform;
+       geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h);
 
-       geWaveTools(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l=0);
-       void resize(int x, int y, int w, int h);
-       int  handle(int e);
+       void resize(int x, int y, int w, int h) override;
+       int  handle(int e) override;
 
-       /* updateWaveform
+       /* rebuild
        Updates the waveform by realloc-ing new data (i.e. when the waveform has
        changed). */
 
-       void updateWaveform();
+       void rebuild();
 
-       /* redrawWaveformAsync
+       /* refresh
        Redraws the waveform, called by the video thread. This is meant to be called
        repeatedly when you need to update the play head inside the waveform. The
        method is smart enough to skip painting if the channel is stopped. */
 
-       void redrawWaveformAsync();
+       void refresh();
+       
+       v::geWaveform* waveform;
+
+       ID channelId;
+       ID waveId;
+
+private:
+
+       void openMenu();
 };
+}} // giada::v::
+
 
 #endif
index 476c1e827463cfd11f21acec8100c3dd3e535c0c..6b0ca4dc710cc428958c6f2c02fe2c001423d11c 100644 (file)
 #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 "../../../glue/sampleEditor.h"
-#include "../../../utils/log.h"
-#include "../../dialogs/sampleEditor.h"
-#include "../basics/boxtypes.h"
+#include "core/channels/sampleChannel.h"
+#include "core/model/model.h"
+#include "core/wave.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/mixer.h"
+#include "core/waveFx.h"
+#include "glue/channel.h"
+#include "glue/sampleEditor.h"
+#include "utils/log.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/boxtypes.h"
 #include "waveTools.h"
 #include "waveform.h"
 
 
-using namespace giada::m;
-using namespace giada::c;
-
-
-geWaveform::geWaveform(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l)
-: Fl_Widget     (x, y, w, h, l),
-       m_selection   {},
-       m_ch          (ch),
-       m_chanStart   (0),
-       m_chanStartLit(false),
-       m_chanEnd     (0),
-       m_chanEndLit  (false),
-       m_pushed      (false),
-       m_dragged     (false),
-       m_resizedA    (false),
-       m_resizedB    (false),
-       m_ratio       (0.0f)
+namespace giada {
+namespace v 
 {
-       m_data.sup  = nullptr;
-       m_data.inf  = nullptr;
-       m_data.size = 0;
-
-       m_grid.snap  = conf::sampleEditorGridOn;
-       m_grid.level = conf::sampleEditorGridVal;
-
-       alloc(w);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geWaveform::~geWaveform()
+geWaveform::geWaveform(ID channelId, ID waveId, int x, int y, int w, int h)
+: Fl_Widget     (x, y, w, h, nullptr),
+  m_selection   {},
+  m_channelId   (channelId),
+  m_waveId      (waveId),
+  m_chanStart   (0),
+  m_chanStartLit(false),
+  m_chanEnd     (0),
+  m_chanEndLit  (false),
+  m_pushed      (false),
+  m_dragged     (false),
+  m_resizedA    (false),
+  m_resizedB    (false),
+  m_ratio       (0.0f)
 {
-       freeData();
+       m_data.size = w;
+
+       m_grid.snap  = m::conf::sampleEditorGridOn;
+       m_grid.level = m::conf::sampleEditorGridVal;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveform::freeData()
+void geWaveform::clearData()
 {
-       if (m_data.sup) {
-               delete[] m_data.sup;
-               delete[] m_data.inf;
-               m_data.sup  = nullptr;
-               m_data.inf  = nullptr;
-               m_data.size = 0;
-       }
+       m_data.sup.clear();
+       m_data.inf.clear();
+       m_data.size = 0;
        m_grid.points.clear();
 }
 
@@ -103,32 +87,29 @@ void geWaveform::freeData()
 
 int geWaveform::alloc(int datasize, bool force)
 {
-       const Wave* wave = m_ch->wave.get();
+       m::model::WavesLock lock(m::model::waves);
+       
+       const m::Wave& wave = m::model::get(m::model::waves, m_waveId);
 
-       m_ratio = wave->getSize() / (float) datasize;
+       m_ratio = wave.getSize() / (float) datasize;
 
        /* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */
 
        if (m_ratio < 1) {  
-               datasize = wave->getSize();
+               datasize = wave.getSize();
                m_ratio = 1;
        }
 
        if (datasize == m_data.size && !force)
                return 0;
 
-       freeData();
+       clearData();
 
        m_data.size = datasize;
-       m_data.sup  = new (std::nothrow) int[m_data.size];
-       m_data.inf  = new (std::nothrow) int[m_data.size];
+       m_data.sup.resize(m_data.size);
+       m_data.inf.resize(m_data.size);
 
-       if (!m_data.sup || !m_data.inf) {
-               gu_log("[geWaveform::alloc] unable to allocate memory for the waveform!\n");
-               return 0;
-       }
-
-       gu_log("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_data.size, m_ratio);
+       u::log::print("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_data.size, m_ratio);
 
        int offset = h() / 2;
        int zero   = y() + offset; // center, zero amplitude (-inf dB)
@@ -136,12 +117,12 @@ int geWaveform::alloc(int datasize, bool force)
        /* Frid frequency: store a grid point every 'gridFreq' frame (if grid is
        enabled). TODO - this will cause round off errors, since gridFreq is integer. */
 
-       int gridFreq = m_grid.level != 0 ? wave->getSize() / m_grid.level : 0;
+       int gridFreq = m_grid.level != 0 ? wave.getSize() / m_grid.level : 0;
 
        /* Resampling the waveform, hardcore way. Many thanks to 
        http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */
 
-       for (int i=0; i<m_data.size; i++) {
+       for (int i = 0; i < m_data.size; i++) {
                
                /* Scan the original waveform in chunks [pc, pn]. */
 
@@ -151,18 +132,18 @@ int geWaveform::alloc(int datasize, bool force)
                float peaksup = 0.0f;
                float peakinf = 0.0f;
 
-               for (int k=pc; k<pn; k++) { // TODO - int until we switch to uint32_t for Wave size...
+               for (int k = pc; k < pn; k++) { // TODO - int until we switch to uint32_t for Wave size...
 
-                       if (k >= wave->getSize())
+                       if (k >= wave.getSize())
                                continue;
 
                        /* Compute average of stereo signal. */
 
                        float avg = 0.0f;
-                       float* frame = wave->getFrame(k);
-                       for (int j=0; j<wave->getChannels(); j++)
+                       float* frame = wave.getFrame(k);
+                       for (int j = 0; j < wave.getChannels(); j++)
                                avg += frame[j];
-                       avg /= wave->getChannels();
+                       avg /= wave.getChannels();
                        
                        /* Find peaks (greater and lower). */
 
@@ -175,8 +156,8 @@ int geWaveform::alloc(int datasize, bool force)
                                m_grid.points.push_back(k);
                }
 
-               m_data.sup[i] = zero - (peaksup * m_ch->getBoost() * offset);
-               m_data.inf[i] = zero - (peakinf * m_ch->getBoost() * offset);
+               m_data.sup[i] = zero - (peaksup * offset);
+               m_data.inf[i] = zero - (peakinf * offset);
 
                // avoid window overflow
 
@@ -194,8 +175,11 @@ int geWaveform::alloc(int datasize, bool force)
 
 void geWaveform::recalcPoints()
 {
-       m_chanStart = m_ch->getBegin();
-       m_chanEnd   = m_ch->getEnd();
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               m_chanStart = static_cast<m::SampleChannel&>(c).getBegin();
+               m_chanEnd   = static_cast<m::SampleChannel&>(c).getEnd();
+       });
 }
 
 
@@ -267,7 +251,7 @@ void geWaveform::drawStartEndPoints()
        int lineX = frameToPixel(m_chanStart) + x();
 
        if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2);
-       else              fl_color(G_COLOR_LIGHT_1);
+       else                fl_color(G_COLOR_LIGHT_1);
 
        /* vertical line */
 
@@ -302,7 +286,13 @@ void geWaveform::drawStartEndPoints()
 
 void geWaveform::drawPlayHead()
 {
-       int p = frameToPixel(m_ch->trackerPreview) + x();
+       float tp;       
+       m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
+       {
+               tp = static_cast<m::SampleChannel&>(c).trackerPreview.load();
+       });
+
+       int p = frameToPixel(tp) + x();
        fl_color(G_COLOR_LIGHT_2);
        fl_line(p, y() + 1, p, y() + h() - 2);
 }
@@ -313,8 +303,8 @@ void geWaveform::drawPlayHead()
 
 void geWaveform::draw()
 {
-       assert(m_data.sup != nullptr);
-       assert(m_data.inf != nullptr);
+       assert(m_data.sup.size() > 0);
+       assert(m_data.inf.size() > 0);
 
        fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2);  // blank canvas
 
@@ -342,6 +332,10 @@ void geWaveform::draw()
 
 int geWaveform::handle(int e)
 {
+       m::model::WavesLock lock(m::model::waves);
+       
+       const m::Wave& wave = m::model::get(m::model::waves, m_waveId);
+
        m_mouseX = pixelToFrame(Fl::event_x() - x());
        m_mouseY = pixelToFrame(Fl::event_y() - y());
 
@@ -349,10 +343,10 @@ int geWaveform::handle(int e)
 
                case FL_KEYDOWN: {
                        if (Fl::event_key() == ' ')
-                               static_cast<gdSampleEditor*>(window())->cb_togglePreview();
+                               static_cast<v::gdSampleEditor*>(window())->cb_togglePreview();
                        else
                        if (Fl::event_key() == FL_BackSpace)
-                               sampleEditor::rewindPreview(m_ch);
+                               c::sampleEditor::rewindPreview(m_channelId);
                        return 1;
                }
 
@@ -384,7 +378,7 @@ int geWaveform::handle(int e)
 
                case FL_RELEASE: {
 
-                       sampleEditor::setPlayHead(m_ch, m_mouseX);
+                       c::sampleEditor::setPlayHead(m_channelId, m_mouseX);
 
                        /* If selection has been done (m_dragged or resized), make sure that point A 
                        is always lower than B. */
@@ -395,7 +389,7 @@ int geWaveform::handle(int e)
                        /* Handle begin/end markers interaction. */
 
                        if (m_chanStartLit || m_chanEndLit)
-                               sampleEditor::setBeginEnd(m_ch, m_chanStart, m_chanEnd);
+                               c::sampleEditor::setBeginEnd(m_channelId, m_chanStart, m_chanEnd);
 
                        m_pushed   = false;
                        m_dragged  = false;
@@ -457,7 +451,6 @@ int geWaveform::handle(int e)
                        /* here the mouse is on the m_chanStart tool */
 
                        if (m_chanStartLit && m_pushed) {
-
                                m_chanStart = snap(m_mouseX);
 
                                if (m_chanStart < 0)
@@ -473,8 +466,8 @@ int geWaveform::handle(int e)
 
                                m_chanEnd = snap(m_mouseX);
 
-                               if (m_chanEnd > m_ch->wave->getSize())
-                                       m_chanEnd = m_ch->wave->getSize();
+                               if (m_chanEnd > wave.getSize())
+                                       m_chanEnd = wave.getSize();
                                else
                                if (m_chanEnd <= m_chanStart)
                                        m_chanEnd = m_chanStart + 2;
@@ -513,6 +506,7 @@ int geWaveform::handle(int e)
 
 int geWaveform::snap(int pos)
 {
+       // TODO use math::quantize
        if (!m_grid.snap)
                return pos;
        for (int pf : m_grid.points) {
@@ -529,7 +523,7 @@ int geWaveform::snap(int pos)
 /* -------------------------------------------------------------------------- */
 
 
-bool geWaveform::mouseOnStart()
+bool geWaveform::mouseOnStart() const
 {
        int mouseXp    = frameToPixel(m_mouseX);
        int mouseYp    = frameToPixel(m_mouseY);
@@ -543,7 +537,7 @@ bool geWaveform::mouseOnStart()
 /* -------------------------------------------------------------------------- */
 
 
-bool geWaveform::mouseOnEnd()
+bool geWaveform::mouseOnEnd() const
 {
        int mouseXp  = frameToPixel(m_mouseX);
        int mouseYp  = frameToPixel(m_mouseY);
@@ -557,7 +551,7 @@ bool geWaveform::mouseOnEnd()
 /* -------------------------------------------------------------------------- */
 
 
-bool geWaveform::mouseOnSelectionA()
+bool geWaveform::mouseOnSelectionA() const
 {
        int mouseXp = frameToPixel(m_mouseX);
        int selAp   = frameToPixel(m_selection.a);
@@ -565,7 +559,7 @@ bool geWaveform::mouseOnSelectionA()
 }
 
 
-bool geWaveform::mouseOnSelectionB()
+bool geWaveform::mouseOnSelectionB() const
 {
        int mouseXp = frameToPixel(m_mouseX);
        int selBp   = frameToPixel(m_selection.b);
@@ -576,12 +570,15 @@ bool geWaveform::mouseOnSelectionB()
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::pixelToFrame(int p)
+int geWaveform::pixelToFrame(int p) const
 {
+       Frame waveSize;
+       m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w) { waveSize = w.getSize(); });
+
        if (p <= 0)
                return 0;
        if (p > m_data.size)
-               return m_ch->wave->getSize() - 1;
+               return waveSize - 1;
        return p * m_ratio;
 }
 
@@ -589,7 +586,7 @@ int geWaveform::pixelToFrame(int p)
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::frameToPixel(int p)
+int geWaveform::frameToPixel(int p) const
 {
        return ceil(p / m_ratio);
 }
@@ -602,14 +599,15 @@ void geWaveform::fixSelection()
 {
        if (m_selection.a > m_selection.b) // inverted m_selection
                std::swap(m_selection.a, m_selection.b);
-       sampleEditor::setPlayHead(m_ch, m_selection.a);
+
+       c::sampleEditor::setPlayHead(m_channelId, m_selection.a);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveform::clearSel()
+void geWaveform::clearSelection()
 {
        m_selection.a = 0;
        m_selection.b = 0;  
@@ -619,9 +617,9 @@ void geWaveform::clearSel()
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveform::setZoom(int type)
+void geWaveform::setZoom(Zoom z)
 {
-       if (!alloc(type == ZOOM_IN ? m_data.size * G_GUI_ZOOM_FACTOR : m_data.size / G_GUI_ZOOM_FACTOR)) 
+       if (!alloc(z == Zoom::IN ? m_data.size * G_GUI_ZOOM_FACTOR : m_data.size / G_GUI_ZOOM_FACTOR)) 
                return;
 
        size(m_data.size, h());
@@ -670,9 +668,10 @@ void geWaveform::stretchToWindow()
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveform::refresh()
+void geWaveform::rebuild()
 {
-       alloc(m_data.size, true); // force
+       clearSelection();
+       alloc(m_data.size, /*force=*/true);
        redraw();
 }
 
@@ -680,7 +679,7 @@ void geWaveform::refresh()
 /* -------------------------------------------------------------------------- */
 
 
-bool geWaveform::smaller()
+bool geWaveform::smaller() const
 {
        return w() < parent()->w();
 }
@@ -701,7 +700,7 @@ void geWaveform::setGridLevel(int l)
 /* -------------------------------------------------------------------------- */
 
 
-bool geWaveform::isSelected()
+bool geWaveform::isSelected() const
 {
        return m_selection.a != m_selection.b;
 }
@@ -711,28 +710,24 @@ bool geWaveform::isSelected()
 
 
 void geWaveform::setSnap(bool v) { m_grid.snap = v; }
-bool geWaveform::getSnap() { return m_grid.snap; }
-int geWaveform::getSize() { return m_data.size; }
+bool geWaveform::getSnap() const { return m_grid.snap; }
+int geWaveform::getSize() const { return m_data.size; }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int geWaveform::getSelectionA()
-{
-       return m_selection.a;
-}
-
-
-int geWaveform::getSelectionB()
-{
-       return m_selection.b;
-}
+int geWaveform::getSelectionA() const { return m_selection.a; }
+int geWaveform::getSelectionB() const { return m_selection.b; }
 
 
 void geWaveform::selectAll()
 {
+       Frame waveSize;
+       m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w) { waveSize = w.getSize(); });
+
        m_selection.a = 0;
-       m_selection.b = m_ch->wave->getSize() - 1;
+       m_selection.b = waveSize - 1;
        redraw();
 }
+}} // giada::v::
index fdeb185bfd1a213d8389f39efdd8f19533354d16..d4cf0337c121714b23f6f4b0cb268ac3fb13c7d1 100644 (file)
 
 #include <vector>
 #include <FL/Fl_Widget.H>
+#include "core/const.h"
 
 
+namespace giada {
+namespace v 
+{
 class geWaveform : public Fl_Widget
 {
+public:
+
+#ifdef G_OS_WINDOWS
+       /* Fuck... */
+       #undef IN
+       #undef OUT
+#endif
+       enum class Zoom { IN, OUT };
+
+       geWaveform(ID channelId, ID waveId, int x, int y, int w, int h);
+
+       void draw() override;
+       int  handle(int e) override;
+
+       /* isSelected
+       Tells whether a portion of the waveform has been selected. */
+
+       bool isSelected() const;
+
+       int getSelectionA() const;
+       int getSelectionB() const;
+
+       bool getSnap() const;
+       int getSize() const;
+
+       /* recalcPoints
+       Recomputes m_chanStart, m_chanEnd, ... */
+
+       void recalcPoints();
+
+       /* zoom
+       Type == 1 : zoom out, type == -1: zoom in */
+
+       void setZoom(Zoom z);
+
+       /* strecthToWindow
+       Shrinks or enlarge the waveform to match parent's width (gWaveTools) */
+
+       void stretchToWindow();
+
+       /* rebuild
+       Redraws the waveform. */
+
+       void rebuild();
+
+       /* setGridLevel
+       Sets a new frequency level for the grid. 0 means disabled. */
+
+       void setGridLevel(int l);
+
+       void setSnap(bool v);
+
+       /* clearSelection
+       Removes any active selection. */
+
+       void clearSelection();
+
+       /* setWaveId
+       Call this when the Wave ID has changed (e.g. after a reload). */
+
+       void setWaveId(ID id) { m_waveId = id; };
+
 private:
 
        static const int FLAG_WIDTH  = 20;
@@ -56,8 +122,8 @@ private:
 
        struct
        {
-               int* sup;   // upper part of the waveform
-               int* inf;   // lower part of the waveform
+               std::vector<int> sup;   // upper part of the waveform
+               std::vector<int> inf;   // lower part of the waveform
                int  size;  // width of the waveform to draw (in pixel)
        } m_data;
 
@@ -67,34 +133,26 @@ private:
                int level;
                std::vector<int> points;
        } m_grid;
-
-       giada::m::SampleChannel* m_ch;
-       int m_chanStart;
-       bool m_chanStartLit;
-       int m_chanEnd;
-       bool m_chanEndLit;
-       bool m_pushed;
-       bool m_dragged;
-       bool m_resizedA;
-       bool m_resizedB;
-       float m_ratio;
-       int m_mouseX;
-       int m_mouseY;
-
+       
        /* mouseOnStart/end
        Is mouse on start or end flag? */
 
-       bool mouseOnStart();
-       bool mouseOnEnd();
+       bool mouseOnStart() const;
+       bool mouseOnEnd() const;
 
        /* mouseOnSelectionA/B
        As above, for the selection. */
 
-       bool mouseOnSelectionA();
-       bool mouseOnSelectionB();
+       bool mouseOnSelectionA() const;
+       bool mouseOnSelectionB() const;
 
-       int pixelToFrame(int p);  // TODO - move these to utils::, will be needed in actionEditor 
-       int frameToPixel(int f);  // TODO - move these to utils::, will be needed in actionEditor 
+       /* smaller
+       Is the waveform smaller than the parent window? */
+
+       bool smaller() const;
+
+       int pixelToFrame(int p) const;  // TODO - move these to utils::, will be needed in actionEditor 
+       int frameToPixel(int f) const;  // TODO - move these to utils::, will be needed in actionEditor 
 
        /* fixSelection
        Helper function which flattens the selection if it was made from right to left 
@@ -103,15 +161,10 @@ private:
 
        void fixSelection();
 
-       /* freeData
+       /* clearData
        Destroys any graphical buffer. */
 
-       void freeData();
-
-       /* smaller
-       Is the waveform smaller than the parent window? */
-
-       bool smaller();
+       void clearData();
 
        /* snap
        Snaps a point at 'pos' pixel. */
@@ -129,65 +182,28 @@ private:
 
        void selectAll();
 
-public:
-
-       static const int ZOOM_IN  = -1;
-       static const int ZOOM_OUT = 0;
-
-       geWaveform(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l=0);
-       ~geWaveform();
-
-       void draw() override;
-       int  handle(int e) override;
-
        /* alloc
        Allocates memory for the picture. It's smart enough not to reallocate if 
        datasize hasn't changed, but it can be forced otherwise. */
 
        int alloc(int datasize, bool force=false);
 
-       /* recalcPoints
-        * re-calc m_chanStart, m_chanEnd, ... */
-
-       void recalcPoints();
-
-       /* 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();
-
-       /* refresh
-       Redraws the waveform. */
-
-       void refresh();
-
-       /* setGridLevel
-        * set a new frequency level for the grid. 0 means disabled. */
-
-       void setGridLevel(int l);
-
-       void setSnap(bool v);
-       bool getSnap();
-       int getSize();
-
-       /* isSelected
-       Tells whether a portion of the waveform has been selected. */
-
-       bool isSelected();
-
-       int getSelectionA();
-       int getSelectionB();
-
-       /* clearSel
-       Removes any active selection. */
-
-       void clearSel();
+       ID m_channelId;
+       ID m_waveId;
+
+       int   m_chanStart;
+       bool  m_chanStartLit;
+       int   m_chanEnd;
+       bool  m_chanEndLit;
+       bool  m_pushed;
+       bool  m_dragged;
+       bool  m_resizedA;
+       bool  m_resizedB;
+       float m_ratio;
+       int   m_mouseX;
+       int   m_mouseY;
 };
+}} // giada::v::
 
 
 #endif
index 34a7d7c3996f20ab618b15f8f7c65f67e6589e64..cb855deb3abb54ac71b25ec3602bdfdbe68f814b 100644 (file)
 
 #include <cmath>
 #include <FL/fl_draw.H>
-#include "../../core/const.h"
-#include "../../core/kernelAudio.h"
-#include "../../utils/math.h"
+#include "core/const.h"
+#include "core/kernelAudio.h"
+#include "utils/math.h"
 #include "soundMeter.h"
 
 
-using namespace giada;
-
-
+namespace giada {
+namespace v
+{
 geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char* l)
 : Fl_Box      (x, y, w, h, l),
   mixerPeak   (0.0f),
@@ -70,5 +70,6 @@ void geSoundMeter::draw()
        float pxLevel = ((w()/G_MIN_DB_SCALE) * m_dbLevelCur) + w();
 
        fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);
-       fl_rectf(x()+1, y()+1, (int) pxLevel, h()-2, clip || !m::kernelAudio::getStatus() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4);
+       fl_rectf(x()+1, y()+1, (int) pxLevel, h()-2, clip || !m::kernelAudio::isReady() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4);
 }
+}} // giada::v::
index 65c42155152e686262ad8d32f2157765714c8819..c974794c3918fe510b84878fd4eeb8240d3e6ed8 100644 (file)
@@ -32,6 +32,9 @@
 #include <FL/Fl_Box.H>
 
 
+namespace giada {
+namespace v
+{
 class geSoundMeter : public Fl_Box
 {
 public:
@@ -47,6 +50,7 @@ private:
        float m_dbLevelCur;
        float m_dbLevelOld;
 };
+}} // giada::v::
 
 
 #endif
diff --git a/src/gui/updater.cpp b/src/gui/updater.cpp
new file mode 100644 (file)
index 0000000..2f88395
--- /dev/null
@@ -0,0 +1,55 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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/model/model.h"
+#include "utils/gui.h"
+#include "updater.h"
+
+
+namespace giada {
+namespace v {
+namespace updater
+{
+void update(void* p)
+{
+       if (m::model::waves.changed.load()    == true ||
+               m::model::actions.changed.load()  == true ||
+               m::model::channels.changed.load() == true)
+       {
+               u::gui::rebuild();
+               m::model::waves.changed.store(false);
+               m::model::actions.changed.store(false);
+               m::model::channels.changed.store(false);
+       }
+       else
+               u::gui::refresh();
+
+       Fl::add_timeout(G_GUI_REFRESH_RATE, update, nullptr);
+}
+}}} // giada::v::updater
diff --git a/src/gui/updater.h b/src/gui/updater.h
new file mode 100644 (file)
index 0000000..246cfa3
--- /dev/null
@@ -0,0 +1,40 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_V_UPDATER_H
+#define G_V_UPDATER_H
+
+
+namespace giada {
+namespace v {
+namespace updater
+{
+void update(void* p);
+}}} // giada::v::updater
+
+
+#endif
\ No newline at end of file
index 8303e1ba806f50aaa56e3ecd37c32c3d05fe2bdb..431d000c29a163da6bc916ff0defbdcc7e47f7d4 100644 (file)
@@ -30,7 +30,6 @@
 #include "core/init.h"
 
 
-std::atomic<bool> G_quit(false);
 class gdMainWindow* G_MainWin = nullptr;
 
 
index d3c83089d0b9892935b41414fe77b9eb03431e3e..495582625773e632bb2437396fc52850c138301a 100644 (file)
 #else
        #include <unistd.h>
 #endif
-
 #include <cstdarg>
-#include <sys/stat.h>   // stat (gu_dirExists)
+#include <sys/stat.h>       // stat (fs::dirExists)
 #include <errno.h>
 #include <cstdlib>
-#ifdef __APPLE__  // our Clang still doesn't know about cstdint (c++11 stuff)
-       #include <stdint.h>
-#else
-       #include <cstdint>
-#endif
+#include <cstdint>
 #include <string>
 #include <cstring>
 #include <climits>
        #include <libgen.h>     // basename unix
        #include <pwd.h>        // getpwuid
 #endif
-#include "../core/const.h"
-#include "string.h"
-#include "log.h"
-#include "fs.h"
+#include "core/const.h"
+#include "utils/string.h"
+#include "utils/log.h"
+#include "utils/fs.h"
+
+
+namespace giada {
+namespace u     {
+namespace fs 
+{
+namespace
+{
+std::string normalize_(const std::string& s)
+{
+       if (s.back() == G_SLASH) {
+               std::string t = s;
+               t.pop_back();
+               return t;
+       }
+       return s;
+}
+} // {anonymous}
 
 
-using std::string;
-using std::vector;
-using namespace giada;
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 
 
-bool gu_fileExists(const string &filename)
+bool fileExists(const std::string &filename)
 {
-       FILE *fh = fopen(filename.c_str(), "rb");
+       FILEfh = fopen(filename.c_str(), "rb");
        if (!fh) {
                return 0;
        }
@@ -77,11 +90,11 @@ bool gu_fileExists(const string &filename)
 /* -------------------------------------------------------------------------- */
 
 
-bool gu_isDir(const string &path)
+bool isDir(const std::string &path)
 {
        bool ret;
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 
        struct stat s1;
        stat(path.c_str(), &s1);
@@ -101,7 +114,7 @@ bool gu_isDir(const string &path)
                 * FIXME - consider native functions CFBundle... */
 
                if (ret) {
-                       if (gu_fileExists(path + "/Contents/Info.plist"))
+                       if (fileExists(path + "/Contents/Info.plist"))
                                ret = false;
                }
        }
@@ -113,14 +126,14 @@ bool gu_isDir(const string &path)
         (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
 #endif
 
-       return ret & !gu_isProject(path);
+       return ret & !isProject(path);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool gu_dirExists(const string &path)
+bool dirExists(const std::string &path)
 {
        struct stat st;
        if (stat(path.c_str(), &st) != 0 && errno == ENOENT)
@@ -132,10 +145,10 @@ bool gu_dirExists(const string &path)
 /* -------------------------------------------------------------------------- */
 
 
-bool gu_mkdir(const string &path)
+bool mkdir(const std::string &path)
 {
-#if defined(__linux__) || defined(__APPLE__)
-       if (mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
+       if (::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
 #else
        if (_mkdir(path.c_str()) == 0)
 #endif
@@ -147,9 +160,9 @@ bool gu_mkdir(const string &path)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_basename(const string& s)
+std::string basename(const std::string& s)
 {
-       string out = s;
+       std::string out = s;
        out.erase(0, out.find_last_of(G_SLASH_STR) + 1);
        return out;
 }
@@ -158,11 +171,11 @@ string gu_basename(const string& s)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_dirname(const string& path)
+std::string dirname(const std::string& path)
 {
        if (path.empty())
                return "";
-       string out = path;
+       std::string out = path;
        out.erase(out.find_last_of(G_SLASH_STR));
        return out;
 }
@@ -171,7 +184,7 @@ string gu_dirname(const string& path)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_getCurrentPath()
+std::string getCurrentPath()
 {
  char buf[PATH_MAX];
 #if defined(__WIN32)
@@ -188,19 +201,19 @@ string gu_getCurrentPath()
 /* -------------------------------------------------------------------------- */
 
 
-string gu_getExt(const string& file)
+std::string getExt(const std::string& file)
 {
        // TODO - use std functions
        int len = strlen(file.c_str());
        int pos = len;
-       while (pos>0) {
+       while (pos > 0) {
                if (file[pos] == '.')
                        break;
                pos--;
        }
        if (pos==0)
                return "";
-       string out = file;
+       std::string out = file;
        return out.substr(pos+1, len);
 }
 
@@ -208,7 +221,7 @@ string gu_getExt(const string& file)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_stripExt(const string& s)
+std::string stripExt(const std::string& s)
 {
        return s.substr(0, s.find_last_of("."));
 }
@@ -217,11 +230,11 @@ string gu_stripExt(const string& s)
 /* -------------------------------------------------------------------------- */
 
 
-bool gu_isProject(const string& path)
+bool isProject(const std::string& path)
 {
        /** FIXME - checks too weak */
 
-       if (gu_getExt(path.c_str()) == "gprj" && gu_dirExists(path))
+       if (getExt(path.c_str()) == "gprj" && dirExists(path))
                return 1;
        return 0;
 }
@@ -230,9 +243,9 @@ bool gu_isProject(const string& path)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_stripFileUrl(const string& f)
+std::string stripFileUrl(const std::string& f)
 {
-       string out = f;
+       std::string out = f;
        out = u::string::replace(out, "file://", "");
        out = u::string::replace(out, "%20", " ");
        return out;
@@ -242,11 +255,11 @@ string gu_stripFileUrl(const string& f)
 /* -------------------------------------------------------------------------- */
 
 
-string gu_getHomePath()
+std::string getHomePath()
 {
        char path[PATH_MAX];
 
-#if   defined(__linux__)
+#if   defined(__linux__) || defined(__FreeBSD__)
 
        snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME"));
 
@@ -258,7 +271,7 @@ string gu_getHomePath()
 
        struct passwd* p = getpwuid(getuid());
        if (p == nullptr) {
-               gu_log("[gu_getHomePath] unable to fetch user infos\n");
+               log::print("[getHomePath] unable to fetch user infos\n");
                return "";
        }
        else {
@@ -268,14 +281,14 @@ string gu_getHomePath()
 
 #endif
 
-       return string(path);
+       return std::string(path);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-bool gu_isRootDir(const std::string& s)
+bool isRootDir(const std::string& s)
 {
        if (s == "")
                return false;
@@ -295,15 +308,17 @@ bool gu_isRootDir(const std::string& s)
 /* -------------------------------------------------------------------------- */
 
 
-std::string gu_getUpDir(const std::string& s)
+std::string getUpDir(const std::string& s)
 {
 #ifdef G_OS_WINDOWS
 
        /* If root, let the user browse the drives list by returning "". */
-       if (gu_isRootDir(s))
+       if (isRootDir(s))
                return "";
 
 #endif
 
-       return s.substr(0, s.find_last_of(G_SLASH_STR)) + G_SLASH_STR;
-}
\ No newline at end of file
+       std::string t = normalize_(s);
+       return t.substr(0, t.find_last_of(G_SLASH_STR)) + G_SLASH_STR;
+}
+}}}  // giada::u::fs::
\ No newline at end of file
index 63aba64c1d1ff170124ddefa67c82eab64eb7a12..27d32459d366b7db33ae895a67305026bf8f530e 100644 (file)
 #include <string>
 
 
-bool gu_fileExists(const std::string& path);
-bool gu_dirExists(const std::string& path);
-bool gu_isDir(const std::string& path);
+namespace giada {
+namespace u     {
+namespace fs 
+{
+bool fileExists(const std::string& path);
+bool dirExists(const std::string& path);
+bool isDir(const std::string& path);
 
 /* isRootDir
 Tells whether 's' is '/' on Unix or '[X]:\' on Windows. */
 
-bool gu_isRootDir(const std::string& s);
+bool isRootDir(const std::string& s);
 
-bool gu_isProject(const std::string& path);
-bool gu_mkdir(const std::string& path);
-std::string gu_getCurrentPath();
-std::string gu_getHomePath();
+bool isProject(const std::string& path);
+bool mkdir(const std::string& path);
+std::string getCurrentPath();
+std::string getHomePath();
 
-/* gu_basename
+/* basename
 /path/to/file.txt -> file.txt */
 
-std::string gu_basename(const std::string& s);
+std::string basename(const std::string& s);
 
-/* gu_dirname
+/* dirname
 /path/to/file.txt -> /path/to */
 
-std::string gu_dirname(const std::string& s);
+std::string dirname(const std::string& s);
 
-/* gu_getExt
+/* getExt
 /path/to/file.txt -> txt */
 
-std::string gu_getExt(const std::string& s);
+std::string getExt(const std::string& s);
 
-/* gu_stripExt
+/* stripExt
 /path/to/file.txt -> /path/to/file */
 
-std::string gu_stripExt(const std::string& s);
+std::string stripExt(const std::string& s);
 
-std::string gu_stripFileUrl(const std::string& s);
+std::string stripFileUrl(const std::string& s);
 
-/* gu_getUpDir
+/* getUpDir
 Returns the upper directory:
 /path/to/my/directory -> /path/to/my/ */
 
-std::string gu_getUpDir(const std::string& s);
+std::string getUpDir(const std::string& s);
+}}}  // giada::u::fs::
 
 
 #endif
index 035ee0aa91f56af76996d2466bcc98b9dc2cc915..9a1ae0fec5e5bc0b3a9adbe8df65a33233936a88 100644 (file)
 #include <FL/fl_draw.H>
 #if defined(_WIN32)
        #include "../ext/resource.h"
-#elif defined(__linux__)
+#elif defined(__linux__) || defined(__FreeBSD__)
        #include <X11/xpm.h>
 #endif
-#include "../core/mixer.h"
-#include "../core/clock.h"
-#include "../core/pluginHost.h"
-#include "../core/channel.h"
-#include "../core/conf.h"
-#include "../core/graphics.h"
-#include "../gui/dialogs/warnings.h"
-#include "../gui/dialogs/mainWindow.h"
-#include "../gui/dialogs/actionEditor/baseActionEditor.h"
-#include "../gui/dialogs/window.h"
-#include "../gui/dialogs/sampleEditor.h"
-#include "../gui/elems/mainWindow/mainIO.h"
-#include "../gui/elems/mainWindow/mainTimer.h"
-#include "../gui/elems/mainWindow/mainTransport.h"
-#include "../gui/elems/mainWindow/beatMeter.h"
-#include "../gui/elems/mainWindow/keyboard/keyboard.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
-#include "../gui/elems/sampleEditor/waveTools.h"
+#include "core/channels/channel.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/clock.h"
+#include "core/pluginHost.h"
+#include "core/conf.h"
+#include "core/graphics.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/actionEditor/baseActionEditor.h"
+#include "gui/dialogs/window.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/mainWindow/mainTransport.h"
+#include "gui/elems/mainWindow/beatMeter.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/sampleEditor/waveTools.h"
 #include "log.h"
 #include "string.h"
 #include "gui.h"
 
 
-extern gdMainWindow* G_MainWin;
+extern giada::v::gdMainWindow* G_MainWin;
 
 
 namespace giada {
@@ -74,32 +75,54 @@ int blinker_ = 0;
 /* -------------------------------------------------------------------------- */
 
 
-void refreshUI()
+void rebuildSubWindow(int wid)
 {
-       Fl::lock();
+       v::gdWindow* w = getSubwindow(G_MainWin, wid);
+       if(w != nullptr)  // If its open
+               w->rebuild();   
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void refreshSubWindow(int wid)
+{
+       v::gdWindow* w = getSubwindow(G_MainWin, wid);
+       if(w != nullptr)  // If its open
+               w->refresh();           
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void refresh()
+{
+       /* Update dynamic elements inside main window: in and out meters, beat meter
+       and each channel. */
+
+       G_MainWin->refresh();
 
-       /* update dynamic elements: in and out meters, beat meter and
-        * each channel */
+       /* Compute timer for blinker. */
 
-       G_MainWin->mainIO->refresh();
-       G_MainWin->beatMeter->redraw();
-       G_MainWin->keyboard->refreshColumns();
+       blinker_ = (blinker_ + 1) % 12;
 
-       /* compute timer for blinker */
+       /* Refresh Sample Editor (if open) for dynamic play head. */
 
-       if (blinker_++ > 12)
-               blinker_ = 0;
+       refreshSubWindow(WID_SAMPLE_EDITOR);
+}
 
-       /* If Sample Editor is open, repaint it (for dynamic play head). */
 
-       gdSampleEditor* se = static_cast<gdSampleEditor*>(getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
-       if (se != nullptr)
-               se->waveTools->redrawWaveformAsync();
+/* -------------------------------------------------------------------------- */
 
-       /* redraw GUI */
 
-       Fl::unlock();
-       Fl::awake();
+void rebuild()
+{
+       G_MainWin->rebuild();
+       rebuildSubWindow(WID_FX_LIST);
+       rebuildSubWindow(WID_SAMPLE_EDITOR);
+       rebuildSubWindow(WID_ACTION_EDITOR);
 }
 
 
@@ -115,29 +138,23 @@ bool shouldBlink()
 /* -------------------------------------------------------------------------- */
 
 
-void updateControls()
+void updateStaticWidgets()
 {
        using namespace giada::m;
 
-       for (const Channel* ch : mixer::channels)
-               ch->guiChannel->update();
-
-       G_MainWin->mainIO->setOutVol(mixer::outVol.load());
-       G_MainWin->mainIO->setInVol(mixer::inVol.load());
+       G_MainWin->mainIO->setOutVol(mh::getOutVol());
+       G_MainWin->mainIO->setInVol(mh::getInVol());
 
 #ifdef WITH_VST
 
-       G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::StackType::MASTER_OUT).size() > 0);
-       G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::StackType::MASTER_IN).size() > 0);
+//     G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::StackType::MASTER_OUT).plugins.size() > 0);
+//     G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::StackType::MASTER_IN).plugins.size() > 0);
        
 #endif
 
        G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
        G_MainWin->mainTimer->setBpm(clock::getBpm());
        G_MainWin->mainTimer->setQuantizer(clock::getQuantize());
-
-       G_MainWin->mainTransport->updatePlay(clock::isRunning());
-       G_MainWin->mainTransport->updateMetronome(mixer::isMetronomeOn());
 }
 
 
@@ -154,9 +171,9 @@ void updateMainWinLabel(const std::string& s)
 /* -------------------------------------------------------------------------- */
 
 
-void setFavicon(Fl_Window* w)
+void setFavicon(v::gdWindow* w)
 {
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 
        fl_open_display();
        Pixmap p, mask;
@@ -175,10 +192,10 @@ void setFavicon(Fl_Window* w)
 /* -------------------------------------------------------------------------- */
 
 
-void openSubWindow(gdWindow* parent, gdWindow* child, int id)
+void openSubWindow(v::gdWindow* parent, v::gdWindow* child, int id)
 {
        if (parent->hasWindow(id)) {
-               gu_log("[GU] parent has subwindow with id=%d, deleting\n", id);
+               u::log::print("[GU] parent has subwindow with id=%d, deleting\n", id);
                parent->delSubWindow(id);
        }
        child->setId(id);
@@ -200,7 +217,7 @@ void refreshActionEditor()
 /* -------------------------------------------------------------------------- */
 
 
-gdWindow* getSubwindow(gdWindow* parent, int id)
+v::gdWindow* getSubwindow(v::gdWindow* parent, int id)
 {
        if (parent->hasWindow(id))
                return parent->getChild(id);
@@ -248,4 +265,4 @@ std::string removeFltkChars(const std::string& s)
        return out;
 }
 
-}}} // giada::u::gui::
\ No newline at end of file
+}}} // giada::u::gui::
index 745b5145d6e1a31d41dd4e3bb0945ee008603151..4af75a618de7ba099e98173c740a9ef056c9a45d 100644 (file)
 #include <string>
 
 
-class Fl_Window;
+namespace giada
+{
+namespace v
+{
 class gdWindow;
-
-
-namespace giada {
+}
 namespace u {
 namespace gui 
 {
 /* refresh
 Repaints some dynamic GUI elements. */
 
-void refreshUI();
+void refresh();
+
+/* rebuild
+Rebuilds the UI from scratch. Used when the model has changed. */
+
+void rebuild();
+
+/* [rebuild|refresh]SubWindow 
+Rebuilds or refreshes subwindow with ID 'wid' if it exists. i.e. if its open. */
+
+void rebuildSubWindow(int wid);
+void refreshSubWindow(int wid);
 
 /* shouldBlink
 Return whether is time to blink something or not. This is used to make widgets 
@@ -51,20 +63,23 @@ blink. */
 
 bool shouldBlink();
 
-/* updateControls
-Updates attributes of control elements (sample names, volumes, ...). Useful when 
-loading a new patch. */
+/* updateStaticWidgets
+Updates attributes of static widgets, i.e. those elements that don't get
+automatically refreshed during the UI update loop. Useful when loading a new 
+patch. */
 
-void updateControls();
+void updateStaticWidgets();
 
 /* updateMainWinLabel
 Updates the name of the main window */
 
 void updateMainWinLabel(const std::string& s);
 
-void setFavicon(Fl_Window* w);
+void setFavicon(v::gdWindow* w);
+
+void openSubWindow(v::gdWindow* parent, v::gdWindow* child, int id);
 
-void openSubWindow(gdWindow* parent, gdWindow* child, int id);
+// TODO closeSubWindow(...)
 
 /* refreshActionEditor
 Reloads the action editor window by closing and reopening it. It's used when you
@@ -80,7 +95,7 @@ void closeAllSubwindows();
 /* getSubwindow
 Returns a pointer to an open subwindow, otherwise nullptr. */
 
-gdWindow* getSubwindow(gdWindow* parent, int id);
+v::gdWindow* getSubwindow(v::gdWindow* parent, int id);
 
 /* removeFltkChars
 Strips special chars used by FLTK to split menus into sub-menus. */
diff --git a/src/utils/json.cpp b/src/utils/json.cpp
new file mode 100644 (file)
index 0000000..baaf27b
--- /dev/null
@@ -0,0 +1,137 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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 <jansson.h>
+#include "utils/log.h"
+#include "json.h"
+
+
+namespace giada {
+namespace u     {
+namespace json 
+{
+namespace
+{
+/* jsonIs, jsonGet
+Tiny wrappers around the old C-style macros provided by Jansson. This way we can
+pass them as template parameters. */
+
+bool jsonIsString_(json_t* j) { return json_is_string(j); }
+bool jsonIsInt_   (json_t* j) { return json_is_integer(j); }
+bool jsonIsFloat_ (json_t* j) { return json_is_real(j); }
+bool jsonIsBool_  (json_t* j) { return json_is_boolean(j); }
+bool jsonIsArray_ (json_t* j) { return json_is_array(j); }
+bool jsonIsObject_(json_t* j) { return json_is_object(j); }
+
+std::string jsonGetString_(json_t* j) { return json_string_value(j); }
+uint32_t    jsonGetInt_   (json_t* j) { return json_integer_value(j); }
+float       jsonGetFloat_ (json_t* j) { return json_real_value(j); }
+bool        jsonGetBool_  (json_t* j) { return json_boolean_value(j); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename F>
+bool is_(json_t* j, F f)
+{
+       if (!f(j)) {
+               u::log::print("[patch::is_] malformed json!\n");
+               json_decref(j);
+               return false;
+       }
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+template<typename O, typename FC, typename FG>
+O read_(json_t* j, const char* key, FC checker, FG getter, O def)
+{
+       json_t* jo = json_object_get(j, key);
+       if (jo == nullptr) {
+               u::log::print("[patch::read_] key '%s' not found, using default value\n", key);
+               return def;
+       }
+       if (!checker(jo)) {
+               u::log::print("[patch::read_] key '%s' is of the wrong type, using default value\n", key);
+               return def;
+       }
+       return getter(jo);
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+std::string readString(json_t* j, const char* key, const std::string& def)
+{
+       return read_(j, key, jsonIsString_, jsonGetString_, def);
+}
+
+uint32_t readInt(json_t* j, const char* key, uint32_t def)
+{
+       return read_(j, key, jsonIsInt_, jsonGetInt_, def);
+}
+
+float readFloat(json_t* j, const char* key, float def)
+{
+       return read_(j, key, jsonIsFloat_, jsonGetFloat_, def);
+}
+
+bool readBool(json_t* j, const char* key, bool def)
+{
+       return read_(j, key, jsonIsBool_, jsonGetBool_, def);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isArray(json_t* j)  { return is_(j, jsonIsArray_); };
+bool isObject(json_t* j) { return is_(j, jsonIsObject_); };
+
+
+/* -------------------------------------------------------------------------- */
+
+
+json_t* load(const std::string& file)
+{
+       json_error_t jerr;
+       json_t* j = json_load_file(file.c_str(), 0, &jerr);
+       if (j == nullptr)
+               u::log::print("[u::json::load] unable to read json file! Error on line %d: %s\n",
+                       jerr.line, jerr.text);
+       return j;
+}
+
+}}}  // giada::u::json::
diff --git a/src/utils/json.h b/src/utils/json.h
new file mode 100644 (file)
index 0000000..54e5eea
--- /dev/null
@@ -0,0 +1,52 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2019 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_JSON_H
+#define G_UTILS_JSON_H
+
+
+#include <string>
+#include <jansson.h>
+
+
+namespace giada {
+namespace u     {
+namespace json 
+{
+std::string readString(json_t* j, const char* key, const std::string& def="");
+uint32_t    readInt(json_t* j, const char* key, uint32_t def=0);
+float       readFloat(json_t* j, const char* key, float def=0.0f);
+bool        readBool(json_t* j, const char* key, bool def=false);
+
+bool isArray(json_t* j);
+bool isObject(json_t* j);
+
+json_t* load(const std::string& file);
+}}}  // giada::u::json::
+
+
+#endif
index 262ecf2225a99ea40f59df56549dcdb71d9948df..5fafea26ca67ae7830c5ec7c980ffed7bfdb2189 100644 (file)
 #include <cstdio>
 #include <cstdarg>
 #include <string>
-#include "../utils/fs.h"
-#include "../core/const.h"
+#include "utils/fs.h"
+#include "core/const.h"
 #include "log.h"
 
 
-using std::string;
+namespace giada {
+namespace u {
+namespace log 
+{
+namespace
+{
+FILE* f;
+int   mode;
+bool  stat;
+} // {anonymouse}
 
 
-static FILE *f;
-static int   mode;
-static bool  stat;
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
 
 
-int gu_logInit(int m)
+int init(int m)
 {
        mode = m;
        stat = true;
        if (mode == LOG_MODE_FILE) {
-               string fpath = gu_getHomePath() + G_SLASH + "giada.log";
+               std::string fpath = fs::getHomePath() + G_SLASH + "giada.log";
                f = fopen(fpath.c_str(), "a");
                if (!f) {
                        stat = false;
@@ -62,7 +71,7 @@ int gu_logInit(int m)
 /* -------------------------------------------------------------------------- */
 
 
-void gu_logClose()
+void close()
 {
        if (mode == LOG_MODE_FILE)
                fclose(f);
@@ -72,19 +81,20 @@ void gu_logClose()
 /* -------------------------------------------------------------------------- */
 
 
-void gu_log(const char *format, ...)
+void print(const char* format, ...)
 {
        if (mode == LOG_MODE_MUTE)
                return;
-  va_list args;
-  va_start(args, format);
-  if (mode == LOG_MODE_FILE && stat == true) {
+       va_list args;
+       va_start(args, format);
+       if (mode == LOG_MODE_FILE && stat == true) {
                vfprintf(f, format, args);
 #ifdef _WIN32
                fflush(f);
 #endif
        }
-  else
+       else
                vprintf(format, args);
-  va_end(args);
+       va_end(args);
 }
+}}}  // giada::u::log::
\ No newline at end of file
index 80657e3baa2b31a33a6c311eb02dad43514153a6..6687d8f434c957840ba82a1ad6a08fcdbd4d4dbb 100644 (file)
 #define G_UTILS_LOG_H
 
 
+namespace giada {
+namespace u {
+namespace log 
+{
 /* init
- * init logger. Mode defines where to write the output: LOG_MODE_STDOUT,
- * LOG_MODE_FILE and LOG_MODE_MUTE. */
+Initializes logger. Mode defines where to write the output: LOG_MODE_STDOUT,
+LOG_MODE_FILE and LOG_MODE_MUTE. */
 
-int  gu_logInit(int mode);
+int init(int mode);
 
-void gu_logClose();
+void close();
 
-void gu_log(const char *format, ...);
+void print(const char* format, ...);
+}}}  // giada::u::log::
 
 
 #endif
index a5d7fba5fdcd189ee9ea18dd42ef740cade6a655..8dc144af120a8e6fc800266c40efa3edb62b3e6f 100644 (file)
@@ -37,22 +37,52 @@ float linearToDB(float f);
 float dBtoLinear(float f);
 int quantize(int x, int step);
 
-/* map (template)
+
+/* -------------------------------------------------------------------------- */
+
+/* map (1)
 Maps 'x' in range [a, b] to a new range [w, z]. Source:
        https://en.wikipedia.org/wiki/Linear_equation#Two-point_form*/
 
 template <typename TI, typename TO>
 TO map(TI x, TI a, TI b, TO w, TO z)
 {
-       return (((x - a) / (float) (b - a)) * (z - w)) + w;
+       return (((x - a) / (double) (b - a)) * (z - w)) + w;
+}
+
+
+/* map (2)
+Maps 'x' in range [0, b) to a new range [0, z]. */
+
+template <typename TI, typename TO>
+TO map(TI x, TI b, TO z)
+{
+       return (x / (double) b) * z;
 }
 
 
+/* -------------------------------------------------------------------------- */
+
+/* bound (1)
+Returns 'def' if 'x' is outside the range ('min', 'max'). */
+
 template <typename T>
 T bound(T x, T min, T max, T def)
 {
     return x < min || x > max ? def : x;
 }
+
+
+/* bound (2)
+Clamps 'x' in the range ('min', 'max'). */
+
+template <typename T>
+T bound(T x, T min, T max)
+{
+    if (x < min) return min; 
+       if (x > max) return max;
+       return x;
+}
 }}}  // giada::u::math::
 
 
index 2438f395db1e94c50f6dd9b1d210ca644cae90e1..da77af5f61e630e60c46003f9ad46c01b6e80017 100644 (file)
@@ -42,7 +42,7 @@ std::string getRealPath(const std::string& path)
 {
        std::string out = "";
 
-#if defined(G_OS_LINUX) || defined(G_OS_MAC)
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) || defined(G_OS_MAC)
 
        char *buf = realpath(path.c_str(), nullptr);
 
@@ -147,4 +147,4 @@ std::vector<std::string> split(std::string in, std::string sep)
        return out;
 }
 
-}}} // giada::u::string
\ No newline at end of file
+}}} // giada::u::string
index a21f2f9761f04c09d0b74634345d8fb925036b4c..78aee51043727aeec2f59a537c5fb73a9a3d0b83 100644 (file)
 
 
 namespace giada {
-namespace u     {
+namespace u {
 namespace vector 
 {
-template <typename T>
-int indexOf(std::vector<T>& v, T obj)
+template <typename T, typename P>
+size_t indexOf(T& v, const P& p)
 {
-    auto it = std::find(v.begin(), v.end(), obj);
-    return it != v.end() ? std::distance(v.begin(), it) : -1;
+       return std::distance(v.begin(), std::find(v.begin(), v.end(), p));
 }
 
 
+template <typename T, typename P>
+size_t indexOfIf(T& v, P p)
+{
+       return std::distance(v.begin(), std::find_if(v.begin(), v.end(), p));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 template <typename T, typename F>
-int indexOf(std::vector<T>& v, F&& func)
+void removeIf(T& v, F&& func)
+{
+    v.erase(std::remove_if(v.begin(), v.end(), func), v.end());
+}
+
+
+template <typename T, typename V>
+void remove(T& v, V val)
 {
-       auto it = std::find_if(v.begin(), v.end(), func);
-       return it != v.end() ? std::distance(v.begin(), it) : -1;
+    v.erase(std::remove(v.begin(), v.end(), val), v.end());
 }
 }}};  // giada::u::vector::
 
-#endif
\ No newline at end of file
+#endif
index cace9efde2f877b95cb6d0a46c30996803ade3ba..da2a7582a75a2c060a7621a00ffc8eea4aa4ee49 100644 (file)
@@ -75,23 +75,4 @@ string getRtMidiVersion()
   return RtMidi::getVersion();
 #endif
 }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool isLess(int a1, int b1, int c1, int a2, int b2, int c2)
-{
-       const int s = 3;
-       int v1[s] = {a1, b1, c1};
-       int v2[s] = {a2, b2, c2};
-
-       for (int i=0; i<s; i++) {
-               if (v1[i] == v2[i])
-                       continue;
-               return v1[i] < v2[i];
-       }
-       return false;
-}
-
-}}};  // giada::u::ver::
\ No newline at end of file
+}}};  // giada::u::ver::
index 96b5f166deec42147167dbf548f600d915ef29ec..06b7f0e8c3b057da8c8239b5d1f136234c171c41 100644 (file)
@@ -39,8 +39,7 @@ namespace ver
 std::string getLibsndfileVersion();
 std::string getRtAudioVersion();
 std::string getRtMidiVersion();
-
-bool isLess(int a1, int b1, int c1, int a2, int b2, int c2);
 }}};  // giada::u::ver::
 
-#endif
\ No newline at end of file
+
+#endif
index dc20323a2e4c44b0af13f95e801d1654de170bb4..32522b94e1aae9f026150d2e15cc0bb5c9f5618f 100644 (file)
@@ -2,8 +2,7 @@
 #define CATCH_CONFIG_FAST_COMPILE
 #include <catch.hpp>
 
-/* There's no main.cpp in the test suite and the following global vars are 
-unfortunately defined there. Let's fake them. */
+/* There's no main.cpp in the test suite and the following global var is 
+unfortunately defined there. Let's fake it. */
 
-class gdMainWindow* G_MainWin;
-bool G_quit;
\ No newline at end of file
+class gdMainWindow* G_MainWin;
\ No newline at end of file
index 3a72fbcd2451c966d1814c12faa583fd68f6c729..90f55e7b1deba79fbbfee7665f8186dccc9dac60 100644 (file)
@@ -12,6 +12,7 @@ using namespace giada::m;
 
 TEST_CASE("patch")
 {
+#if 0
        string filename = "./test-patch.json";
 
        SECTION("test write")
@@ -236,4 +237,5 @@ TEST_CASE("patch")
                REQUIRE(masterPlugin1.params.at(6) == Approx(0.333f));
 #endif
        }
+#endif
 }
diff --git a/tests/rcuList.cpp b/tests/rcuList.cpp
new file mode 100644 (file)
index 0000000..6114c5a
--- /dev/null
@@ -0,0 +1,93 @@
+#include "../src/core/rcuList.h"
+#include "../src/core/types.h"
+#include <catch.hpp>
+
+
+using namespace giada;
+using namespace giada::m;
+
+
+TEST_CASE("RCUList")
+{
+       struct Object
+       {
+               Object(ID id) : id(id) {}
+               ID id;
+       };
+
+       RCUList<Object> list;
+
+    REQUIRE(list.size() == 0);
+    REQUIRE(list.changed == false);
+
+       SECTION("test push")
+       {
+               list.push(std::make_unique<Object>(1));
+               list.push(std::make_unique<Object>(2));
+               list.push(std::make_unique<Object>(3));
+
+       REQUIRE(list.size() == 3);
+       REQUIRE(list.changed == true);
+
+               SECTION("test retrieval")
+               {
+                       RCUList<Object>::Lock l(list);
+
+                       REQUIRE(list.get(0)->id == 1);
+                       REQUIRE(list.get(1)->id == 2);
+                       REQUIRE(list.get(2)->id == 3);
+                       REQUIRE(list[0]->id == 1);
+                       REQUIRE(list[1]->id == 2);
+                       REQUIRE(list[2]->id == 3);
+                       REQUIRE(list.back()->id == 3);
+               }
+
+               SECTION("test iterator")
+               {
+                       RCUList<Object>::Lock l(list);
+
+                       ID id = 1;
+                       for (Object* o : list)
+                               REQUIRE(o->id == id++);
+               }
+               
+               SECTION("test const iterator")
+               {
+                       RCUList<Object>::Lock l(list);
+
+                       ID id = 1;
+                       for (const Object* o : list)
+                               REQUIRE(o->id == id++);
+               }
+
+               SECTION("test pop")
+               {
+                       list.pop(0);
+
+                       REQUIRE(list.size() == 2);
+                       REQUIRE(list.changed == true);
+               }
+
+               SECTION("test clear")
+               {
+                       list.clear();
+
+                       REQUIRE(list.size() == 0);
+                       REQUIRE(list.changed == true);
+               }
+       }
+
+       SECTION("test swap")
+       {
+               list.push(std::make_unique<Object>(1));
+
+               list.swap(std::make_unique<Object>(16));
+
+               REQUIRE(list.size() == 1);
+               REQUIRE(list.changed == true);
+
+               RCUList<Object>::Lock l(list);
+               
+               REQUIRE(list.get(0)->id == 16);
+       }
+}
index 5cc6d4c23f39d22bcdf87316cb6d7eda0ebe5253..8557a50e2921d8a6a6ad458e61a162922a208130 100644 (file)
@@ -10,11 +10,7 @@ TEST_CASE("recorder")
        using namespace giada;
        using namespace giada::m;
 
-       pthread_mutex_t mutex;
-       pthread_mutex_init(&mutex, nullptr);
-
-       recorder::init(&mutex);
-       recorder::enable();
+       recorder::init();
 
        REQUIRE(recorder::hasActions(/*ch=*/0) == false);
 
@@ -26,16 +22,16 @@ TEST_CASE("recorder")
                const MidiEvent e1 = MidiEvent(MidiEvent::NOTE_ON, 0x00, 0x00);
                const MidiEvent e2 = MidiEvent(MidiEvent::NOTE_OFF, 0x00, 0x00);
 
-               const Action* a1 = recorder::rec(ch, f1, e1);
-               const Action* a2 = recorder::rec(ch, f2, e2);
+               const Action a1 = recorder::rec(ch, f1, e1);
+               const Action a2 = recorder::rec(ch, f2, e2);
 
                REQUIRE(recorder::hasActions(ch) == true);
-               REQUIRE(a1->frame == f1);
-               REQUIRE(a2->frame == f2);
-               REQUIRE(a1->prev == nullptr);
-               REQUIRE(a1->next == nullptr);
-               REQUIRE(a2->prev == nullptr);
-               REQUIRE(a2->next == nullptr);
+               REQUIRE(a1.frame == f1);
+               REQUIRE(a2.frame == f2);
+               REQUIRE(a1.prevId == 0);
+               REQUIRE(a1.nextId == 0);
+               REQUIRE(a2.prevId == 0);
+               REQUIRE(a2.nextId == 0);
 
                SECTION("Test clear actions by channel")
                {
@@ -69,4 +65,4 @@ TEST_CASE("recorder")
                        REQUIRE(recorder::hasActions(/*channel=*/0) == false);
                }
        }
-}
\ No newline at end of file
+}
index 9362f97c0207e78fcfbf9eb4197d432a6cf10e6a..dcb59f362894d7ba971d82a00b89d4039e10df1f 100644 (file)
@@ -1,16 +1,15 @@
-#include "../src/core/sampleChannel.h"
-#include "../src/core/wave.h"
-#include "../src/core/waveManager.h"
+#include "../src/core/channels/sampleChannel.h"
+#include "../src/core/model/model.h"
 #include <catch.hpp>
 
 
-using namespace giada;
-using namespace giada::m;
-
-
 TEST_CASE("sampleChannel")
 {
+       using namespace giada;
+       using namespace giada::m;
+
        const int BUFFER_SIZE = 1024;
+       const int WAVE_SIZE   = 50000;
 
        std::vector<ChannelMode> modes = { ChannelMode::LOOP_BASIC, 
                ChannelMode::LOOP_ONCE, ChannelMode::LOOP_REPEAT, 
@@ -18,21 +17,31 @@ TEST_CASE("sampleChannel")
                ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG, 
                ChannelMode::SINGLE_ENDLESS };
 
-       SampleChannel ch(false, BUFFER_SIZE);
-       waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav");
-       int waveSize = res.wave->getSize();
-       ch.pushWave(std::move(res.wave));
+       model::channels.clear();
+       model::channels.push(std::make_unique<SampleChannel>(false, BUFFER_SIZE, 1, 1));
+
+       model::onSwap(model::channels, 1, [&](Channel& c)
+       {
+               static_cast<SampleChannel&>(c).pushWave(1, WAVE_SIZE);
+       });
+
+       model::channels.lock();
+       SampleChannel& ch = static_cast<SampleChannel&>(*model::channels.get(0));
+       model::channels.unlock();
 
        SECTION("push wave")
        {
-               REQUIRE(ch.status == ChannelStatus::OFF);
+               REQUIRE(ch.playStatus == ChannelStatus::OFF);
                REQUIRE(ch.begin == 0);
-               REQUIRE(ch.end == waveSize - 1);
+               REQUIRE(ch.end == WAVE_SIZE);
                REQUIRE(ch.name == "");         
        }
 
        SECTION("begin/end")
        {
+               /* TODO - This section requires model::waves interaction. Let's wait for 
+               the non-virtual channel refactoring... */
+               /*
                ch.setBegin(-100);
 
                REQUIRE(ch.getBegin() == 0);
@@ -41,9 +50,9 @@ TEST_CASE("sampleChannel")
 
                ch.setBegin(100000);
 
-               REQUIRE(ch.getBegin() == waveSize);
-               REQUIRE(ch.tracker == waveSize);
-               REQUIRE(ch.trackerPreview == waveSize);
+               REQUIRE(ch.getBegin() == WAVE_SIZE);
+               REQUIRE(ch.tracker == WAVE_SIZE);
+               REQUIRE(ch.trackerPreview == WAVE_SIZE);
 
                ch.setBegin(16);
 
@@ -57,7 +66,7 @@ TEST_CASE("sampleChannel")
 
                ch.setEnd(100000);
 
-               REQUIRE(ch.getEnd() == waveSize - 1);
+               REQUIRE(ch.getEnd() == WAVE_SIZE - 1);
 
                ch.setEnd(32);
 
@@ -66,6 +75,7 @@ TEST_CASE("sampleChannel")
                ch.setBegin(64);
 
                REQUIRE(ch.getBegin() == 31);
+               */
        }
 
        SECTION("pitch")
@@ -87,8 +97,8 @@ TEST_CASE("sampleChannel")
        {
                REQUIRE(ch.getPosition() == -1);  // Initially OFF
 
-               ch.status  = ChannelStatus::PLAY;
-               ch.tracker = 1000;
+               ch.playStatus = ChannelStatus::PLAY;
+               ch.tracker    = 1000;
 
                REQUIRE(ch.getPosition() == 1000);
 
@@ -101,14 +111,14 @@ TEST_CASE("sampleChannel")
        {
                ch.empty();
 
-               REQUIRE(ch.status == ChannelStatus::EMPTY);
+               REQUIRE(ch.playStatus == ChannelStatus::EMPTY);
                REQUIRE(ch.begin == 0);
                REQUIRE(ch.end == 0);
                REQUIRE(ch.tracker == 0);
                REQUIRE(ch.volume == G_DEFAULT_VOL);
-               REQUIRE(ch.boost == G_DEFAULT_BOOST);
                REQUIRE(ch.hasActions == false);
-               REQUIRE(ch.wave == nullptr);
+               REQUIRE(ch.hasWave == false);
+               REQUIRE(ch.waveId == 0);
        }
 
        SECTION("can record audio")
index 510363dd999fd04a0916bdd793cace8ddded7d3f..a6fba5443c5452f6cf780302fc843abf731824d0 100644 (file)
@@ -1,7 +1,6 @@
-#include "../src/core/sampleChannel.h"
-#include "../src/core/sampleChannelProc.h"
-#include "../src/core/wave.h"
-#include "../src/core/waveManager.h"
+#if 0
+#include "../src/core/channels/sampleChannel.h"
+#include "../src/core/channels/sampleChannelProc.h"
 #include <catch.hpp>
 
 
@@ -19,10 +18,13 @@ TEST_CASE("sampleChannelProc")
                ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG, 
                ChannelMode::SINGLE_ENDLESS };
 
-       SampleChannel ch(false, BUFFER_SIZE);
-       waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav");
+       SampleChannel ch(false, BUFFER_SIZE, 1, 1);
 
-       REQUIRE(ch.status == ChannelStatus::EMPTY);
+       AudioBuffer out;
+       AudioBuffer in;
+       AudioBuffer inToOut;
+
+       REQUIRE(ch.playStatus == ChannelStatus::EMPTY);
        REQUIRE(ch.mode == ChannelMode::SINGLE_BASIC);
 
        SECTION("buffer")
@@ -30,26 +32,28 @@ TEST_CASE("sampleChannelProc")
                SECTION("prepare")
                {
                        /* With no wave data in it. */
-                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
-
+                       sampleChannelProc::render(&ch, out, in, inToOut, /*audible=*/true, 
+                               /*running=*/false);
                        REQUIRE(ch.tracker == 0);
 
                        /* With data, stopped. */
-                       ch.pushWave(std::move(res.wave));
-                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
+                       ch.pushWave(1, 1024);
+                       sampleChannelProc::render(&ch, out, in, inToOut, /*audible=*/true, 
+                               /*running=*/false);
 
                        REQUIRE(ch.tracker == 0);
 
                        /* With data, playing. */
-                       ch.status = ChannelStatus::PLAY;
-                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
+                       ch.playStatus = ChannelStatus::PLAY;
+                       sampleChannelProc::render(&ch, out, in, inToOut, /*audible=*/true, 
+                               /*running=*/false);
 
                        REQUIRE(ch.tracker == BUFFER_SIZE);
                }
 
                SECTION("fill")
                {
-                       ch.pushWave(std::move(res.wave));
+                       ch.pushWave(1, 1024);
 
                        /* Zero offset. */
                        REQUIRE(ch.fillBuffer(ch.buffer, 0, 0) == BUFFER_SIZE);
@@ -64,40 +68,40 @@ TEST_CASE("sampleChannelProc")
 
        SECTION("statuses")
        {
-               ch.pushWave(std::move(res.wave));
+               ch.pushWave(1, 1024);
 
                SECTION("start from OFF")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode   = mode;
-                               ch.status = ChannelStatus::OFF;
+                               ch.mode       = mode;
+                               ch.playStatus = ChannelStatus::OFF;
                                sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
 
                                if (ch.isAnyLoopMode())
-                                       REQUIRE(ch.status == ChannelStatus::WAIT);
+                                       REQUIRE(ch.playStatus == ChannelStatus::WAIT);
                                else
-                                       REQUIRE(ch.status == ChannelStatus::PLAY);
+                                       REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                        }
                }
 
                SECTION("start from PLAY")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode    = mode;
-                               ch.status  = ChannelStatus::PLAY;
-                               ch.tracker = 16; // simulate processing
+                               ch.mode       = mode;
+                               ch.playStatus = ChannelStatus::PLAY;
+                               ch.tracker    = 16; // simulate processing
                                sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
                                
                                if (ch.mode == ChannelMode::SINGLE_RETRIG) {
-                                       REQUIRE(ch.status == ChannelStatus::PLAY);
+                                       REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                        REQUIRE(ch.tracker == 0);
                                }
                                else
                                if (ch.isAnyLoopMode() || ch.mode == ChannelMode::SINGLE_ENDLESS) 
-                                       REQUIRE(ch.status == ChannelStatus::ENDING);
+                                       REQUIRE(ch.playStatus == ChannelStatus::ENDING);
                                else
                                if (ch.mode == ChannelMode::SINGLE_BASIC) {
-                                       REQUIRE(ch.status == ChannelStatus::OFF);
+                                       REQUIRE(ch.playStatus == ChannelStatus::OFF);
                                        REQUIRE(ch.tracker == 0);
                                }
                        }
@@ -106,40 +110,40 @@ TEST_CASE("sampleChannelProc")
                SECTION("start from WAIT")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode   = mode;
-                               ch.status = ChannelStatus::WAIT;
+                               ch.mode       = mode;
+                               ch.playStatus = ChannelStatus::WAIT;
                                sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
 
-                               REQUIRE(ch.status == ChannelStatus::OFF);
+                               REQUIRE(ch.playStatus == ChannelStatus::OFF);
                        }
                }
 
                SECTION("start from ENDING")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode   = mode;
-                               ch.status = ChannelStatus::ENDING;
+                               ch.mode       = mode;
+                               ch.playStatus = ChannelStatus::ENDING;
                                sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
 
-                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                        }
                }
 
                SECTION("stop from PLAY")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode   = mode;
-                               ch.status = ChannelStatus::PLAY;
-                               ch.tracker = 16; // simulate processing
+                               ch.mode       = mode;
+                               ch.playStatus = ChannelStatus::PLAY;
+                               ch.tracker    = 16; // simulate processing
                                sampleChannelProc::stop(&ch);
 
                                if (ch.mode == ChannelMode::SINGLE_PRESS) {
-                                       REQUIRE(ch.status == ChannelStatus::OFF);
+                                       REQUIRE(ch.playStatus == ChannelStatus::OFF);
                                        REQUIRE(ch.tracker == 0);
                                }
                                else {
                                        /* Nothing should change for other modes. */
-                                       REQUIRE(ch.status == ChannelStatus::PLAY);
+                                       REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                        REQUIRE(ch.tracker == 16);                                      
                                }
                        }
@@ -153,15 +157,15 @@ TEST_CASE("sampleChannelProc")
 
                        for (ChannelMode mode : modes) {
                                for (ChannelStatus status : statuses) {
-                                       ch.mode    = mode;
-                                       ch.status  = status;
-                                       ch.tracker = 16; // simulate processing
+                                       ch.mode       = mode;
+                                       ch.playStatus = status;
+                                       ch.tracker    = 16; // simulate processing
                                        sampleChannelProc::kill(&ch, 0);
                                        
-                                       if (ch.status == ChannelStatus::WAIT || 
-                                           ch.status == ChannelStatus::PLAY ||
-                                           ch.status == ChannelStatus::ENDING) {
-                                               REQUIRE(ch.status == ChannelStatus::OFF);
+                                       if (ch.playStatus == ChannelStatus::WAIT || 
+                                           ch.playStatus == ChannelStatus::PLAY ||
+                                           ch.playStatus == ChannelStatus::ENDING) {
+                                               REQUIRE(ch.playStatus == ChannelStatus::OFF);
                                                REQUIRE(ch.tracker == 0);
                                        }
                                }
@@ -171,14 +175,14 @@ TEST_CASE("sampleChannelProc")
                SECTION("quantized start")
                {
                        for (ChannelMode mode : modes) {
-                               ch.mode   = mode;               
-                               ch.status = ChannelStatus::OFF; 
+                               ch.mode       = mode;           
+                               ch.playStatus = ChannelStatus::OFF;     
                                sampleChannelProc::start(&ch, 0, /*doQuantize=*/true, /*velocity=*/0);
 
                                if (ch.isAnyLoopMode())
-                                       REQUIRE(ch.status == ChannelStatus::WAIT);      
+                                       REQUIRE(ch.playStatus == ChannelStatus::WAIT);  
                                else {
-                                       REQUIRE(ch.status == ChannelStatus::OFF);       
+                                       REQUIRE(ch.playStatus == ChannelStatus::OFF);   
                                        REQUIRE(ch.quantizing == true); 
                                }
                        }
@@ -189,19 +193,19 @@ TEST_CASE("sampleChannelProc")
        {
                /* Start all sample channels in any loop mode that were armed. */
                for (ChannelMode mode : modes) {
-                       ch.mode    = mode;
-                       ch.status  = ChannelStatus::OFF;        
-                       ch.armed   = true;
-                       ch.tracker = 16;
+                       ch.mode       = mode;
+                       ch.playStatus = ChannelStatus::OFF;     
+                       ch.armed      = true;
+                       ch.tracker    = 16;
                        
                        sampleChannelProc::stopInputRec(&ch, /*globalFrame=*/666);
 
                        if (ch.isAnyLoopMode()) {
-                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                REQUIRE(ch.tracker == 666);
                        }
                        else {
-                               REQUIRE(ch.status == ChannelStatus::OFF);
+                               REQUIRE(ch.playStatus == ChannelStatus::OFF);
                                REQUIRE(ch.tracker == 16);                              
                        }
                }
@@ -209,23 +213,23 @@ TEST_CASE("sampleChannelProc")
 
        SECTION("rewind by sequencer")
        {
-               ch.pushWave(std::move(res.wave));
+               ch.pushWave(1, 1024);
 
                /* Test loop modes. */
 
                for (ChannelMode mode : modes) {
-                       ch.mode    = mode;
-                       ch.status  = ChannelStatus::PLAY;       
-                       ch.tracker = 16; // simulate processing
+                       ch.mode       = mode;
+                       ch.playStatus = ChannelStatus::PLAY;    
+                       ch.tracker    = 16; // simulate processing
 
                        sampleChannelProc::rewindBySeq(&ch);
 
                        if (ch.isAnyLoopMode()) {
-                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                REQUIRE(ch.tracker == 0);
                        }
                        else {
-                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                REQUIRE(ch.tracker == 16);                              
                        }
                }
@@ -233,17 +237,18 @@ TEST_CASE("sampleChannelProc")
                /* Test single modes that are reading actions. */
 
                for (ChannelMode mode : modes) {
-                       ch.mode      = mode;
-                       ch.status    = ChannelStatus::PLAY;     
-                       ch.tracker   = 16; // simulate processing
-                       ch.recStatus = ChannelStatus::PLAY;
+                       ch.mode       = mode;
+                       ch.playStatus = ChannelStatus::PLAY;    
+                       ch.tracker    = 16; // simulate processing
+                       ch.recStatus  = ChannelStatus::PLAY;
 
                        sampleChannelProc::rewindBySeq(&ch);
 
                        if (ch.isAnySingleMode()) {
-                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.playStatus == ChannelStatus::PLAY);
                                REQUIRE(ch.tracker == 0);
                        }
                }               
        }
 }
+#endif
index c115d078472aa98761dc55444feb107292fe4034..79ffc18dd6dd545228dbd3ba3f5522be1d98fd11 100644 (file)
@@ -1,5 +1,7 @@
-#include "../src/core/sampleChannel.h"
-#include "../src/core/sampleChannelRec.h"
+#if 0
+
+#include "../src/core/channels/sampleChannel.h"
+#include "../src/core/channels/sampleChannelRec.h"
 #include <catch.hpp>
 
 
@@ -11,7 +13,7 @@ TEST_CASE("sampleChannelRec")
 {
        const int BUFFER_SIZE = 1024;
 
-       SampleChannel ch(false, BUFFER_SIZE);
+       SampleChannel ch(false, BUFFER_SIZE, 1, 1);
 
        SECTION("start reading actions, don't treat recs as loop")
        {
@@ -72,14 +74,15 @@ TEST_CASE("sampleChannelRec")
 
        SECTION("set read actions status to false with recsStopOnChanHalt")
        {
-               ch.status  = ChannelStatus::PLAY;
-               ch.tracker = 1024;
+               ch.playStatus = ChannelStatus::PLAY;
+               ch.tracker    = 1024;
 
                sampleChannelRec::setReadActions(&ch, false, /*recsStopOnChanHalt=*/true);
 
                REQUIRE(ch.readActions == false);
-               REQUIRE(ch.status == ChannelStatus::OFF);
+               REQUIRE(ch.playStatus == ChannelStatus::OFF);
                REQUIRE(ch.tracker == 0);
 
        }
 }
+#endif
index 1a8ba4f4e58772dabf3348f171856dd89b2025de..1cbc4b10207332d71a0fb353ecaaf6c66d3bb025 100644 (file)
@@ -1,41 +1,41 @@
 #include "../src/utils/fs.h"
 #include "../src/utils/string.h"
 #include "../src/utils/math.h"
-#include "../src/utils/ver.h"
 #include <catch.hpp>
 
 
 TEST_CASE("u::fs")
 {
-       REQUIRE(gu_fileExists("README.md") == true);
-       REQUIRE(gu_fileExists("ghost_file") == false);
-       REQUIRE(gu_dirExists("src/") == true);
-       REQUIRE(gu_dirExists("ghost_dir/") == false);
-       REQUIRE(gu_isDir("src/") == true);
-       REQUIRE(gu_isDir("giada_tests") == false);
-       REQUIRE(gu_basename("tests/utils.cpp") == "utils.cpp");
-       REQUIRE(gu_dirname("tests/utils.cpp") == "tests");
-       REQUIRE(gu_getExt("tests/utils.cpp") == "cpp");
-       REQUIRE(gu_stripExt("tests/utils.cpp") == "tests/utils");
+       using namespace giada::u::fs;
+
+       REQUIRE(fileExists("README.md") == true);
+       REQUIRE(fileExists("ghost_file") == false);
+       REQUIRE(dirExists("src/") == true);
+       REQUIRE(dirExists("ghost_dir/") == false);
+       REQUIRE(isDir("src/") == true);
+       REQUIRE(isDir("giada_tests") == false);
+       REQUIRE(basename("tests/utils.cpp") == "utils.cpp");
+       REQUIRE(dirname("tests/utils.cpp") == "tests");
+       REQUIRE(getExt("tests/utils.cpp") == "cpp");
+       REQUIRE(stripExt("tests/utils.cpp") == "tests/utils");
 #if defined(_WIN32)
-       REQUIRE(gu_isRootDir("C:\\") == true);
-       REQUIRE(gu_isRootDir("C:\\path\\to\\something") == false);
-       REQUIRE(gu_getUpDir("C:\\path\\to\\something") == "C:\\path\\to\\");
-       REQUIRE(gu_getUpDir("C:\\path") == "C:\\");
-       REQUIRE(gu_getUpDir("C:\\") == "");
+       REQUIRE(isRootDir("C:\\") == true);
+       REQUIRE(isRootDir("C:\\path\\to\\something") == false);
+       REQUIRE(getUpDir("C:\\path\\to\\something") == "C:\\path\\to\\");
+       REQUIRE(getUpDir("C:\\path") == "C:\\");
+       REQUIRE(getUpDir("C:\\") == "");
 #else
-       REQUIRE(gu_isRootDir("/") == true);
-       REQUIRE(gu_isRootDir("/path/to/something") == false);
-       REQUIRE(gu_getUpDir("/path/to/something") == "/path/to/");
-       REQUIRE(gu_getUpDir("/path") == "/");
-       REQUIRE(gu_getUpDir("/") == "/");
+       REQUIRE(isRootDir("/") == true);
+       REQUIRE(isRootDir("/path/to/something") == false);
+       REQUIRE(getUpDir("/path/to/something") == "/path/to/");
+       REQUIRE(getUpDir("/path") == "/");
+       REQUIRE(getUpDir("/") == "/");
 #endif
 }
 
 
 TEST_CASE("u::string")
 {
-       using std::vector;
        using namespace giada::u::string;
 
        REQUIRE(replace("Giada is cool", "cool", "hot") == "Giada is hot");
@@ -45,7 +45,7 @@ TEST_CASE("u::string")
        REQUIRE(fToString(3.14159, 2) == "3.14");
        REQUIRE(format("I see %d men with %s hats", 5, "strange") == "I see 5 men with strange hats");
 
-       vector<std::string> v = split("Giada is cool", " ");
+       std::vector<std::string> v = split("Giada is cool", " ");
        REQUIRE(v.size() == 3);
        REQUIRE(v.at(0) == "Giada");
        REQUIRE(v.at(1) == "is");
@@ -61,14 +61,3 @@ TEST_CASE("::math")
        REQUIRE(map(30.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 1.0f);
        REQUIRE(map(15.0f, 0.0f, 30.0f, 0.0f, 1.0f) == Approx(0.5f));
 }
-
-
-TEST_CASE("u::ver")
-{
-       using namespace giada::u::ver;
-
-       REQUIRE(isLess(6, 6, 6, 0, 15, 0) == false);
-       REQUIRE(isLess(0, 15, 0, 6, 6, 6) == true);
-       REQUIRE(isLess(6, 6, 6, 6, 6, 6) == false);
-       REQUIRE(isLess(6, 6, 5, 6, 6, 6) == true);
-}
\ No newline at end of file
index 095a4c45539c83054227c3335424f0b5a62df454..0d216333142928569099e725a3b0eee2a0d06d6f 100644 (file)
@@ -3,23 +3,22 @@
 #include <catch.hpp>
 
 
-using std::string;
-
-
 TEST_CASE("Wave")
 {
+       using namespace giada;
+
        static const int SAMPLE_RATE = 44100;
        static const int BUFFER_SIZE = 4096;
        static const int CHANNELS = 2;
        static const int BIT_DEPTH = 32;
 
        /* Each SECTION the TEST_CASE is executed from the start. Any code between 
-       this comment and the first SECTION macro is exectuted before each SECTION. */
+       this comment and the first SECTION macro is executed before each SECTION. */
 
 
        SECTION("test allocation")
        {
-               Wave wave;
+               m::Wave wave(1);
                wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
 
                SECTION("test basename")
index 74738782dc8445ff9aee44dad7926a19ab4435fd..bc8bf21d0171a0b787d2b4384f3a70e5d7ae3717 100644 (file)
@@ -1,41 +1,57 @@
 #include <memory>
+#include "../src/core/model/model.h"
 #include "../src/core/const.h"
 #include "../src/core/wave.h"
 #include "../src/core/waveFx.h"
+#include "../src/core/types.h"
 #include <catch.hpp>
 
 
-using std::string;
+using namespace giada;
 using namespace giada::m;
 
 
-TEST_CASE("waveFx")
+Wave& getWave(ID id)
 {
-       static const int SAMPLE_RATE = 44100;
-       static const int BUFFER_SIZE = 4000;
-       static const int BIT_DEPTH = 32;
+       model::WavesLock l(model::waves);
+       return model::get(model::waves, id);
+}
+
 
-       Wave waveMono;
-       Wave waveStereo;
-       waveMono.alloc(BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
-       waveStereo.alloc(BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
+TEST_CASE("waveFx")
+{
+       static const ID  WAVE_MONO_ID   = 1;
+       static const ID  WAVE_STEREO_ID = 2;
+       static const int SAMPLE_RATE    = 44100;
+       static const int BUFFER_SIZE    = 4000;
+       static const int BIT_DEPTH      = 32;
+
+       std::unique_ptr<Wave> waveMono   = std::make_unique<Wave>(WAVE_MONO_ID);
+       std::unique_ptr<Wave> waveStereo = std::make_unique<Wave>(WAVE_STEREO_ID);
+
+       waveMono->alloc(BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-mono.wav");
+       waveStereo->alloc(BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-stereo.wav");
+       
+       model::waves.clear();
+       model::waves.push(std::move(waveMono));
+       model::waves.push(std::move(waveStereo));
 
        SECTION("test mono->stereo conversion")
        {
-               int prevSize = waveMono.getSize();
+               int prevSize = getWave(WAVE_MONO_ID).getSize();
 
-               REQUIRE(wfx::monoToStereo(waveMono) == G_RES_OK);
-               REQUIRE(waveMono.getSize() == prevSize);  // size does not change, channels do
-               REQUIRE(waveMono.getChannels() == 2);
+               REQUIRE(wfx::monoToStereo(getWave(WAVE_MONO_ID)) == G_RES_OK);
+               REQUIRE(getWave(WAVE_MONO_ID).getSize() == prevSize);  // size does not change, channels do
+               REQUIRE(getWave(WAVE_MONO_ID).getChannels() == 2);
 
                SECTION("test mono->stereo conversion for already stereo wave")
                {
                        /* Should do nothing. */
-                       int prevSize = waveStereo.getSize();
+                       int prevSize = getWave(WAVE_STEREO_ID).getSize();
 
-                       REQUIRE(wfx::monoToStereo(waveStereo) == G_RES_OK);
-                       REQUIRE(waveStereo.getSize() == prevSize);
-                       REQUIRE(waveStereo.getChannels() == 2);
+                       REQUIRE(wfx::monoToStereo(getWave(WAVE_STEREO_ID)) == G_RES_OK);
+                       REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize);
+                       REQUIRE(getWave(WAVE_STEREO_ID).getChannels() == 2);
                }
        }
 
@@ -43,19 +59,19 @@ TEST_CASE("waveFx")
        {
                int a = 20;
                int b = 57;
-               wfx::silence(waveStereo, a, b);
+               wfx::silence(getWave(WAVE_STEREO_ID).id, a, b);
 
                for (int i=a; i<b; i++)
-                       for (int k=0; k<waveStereo.getChannels(); k++)
-                               REQUIRE(waveStereo[i][k] == 0.0f);
+                       for (int k=0; k<getWave(WAVE_STEREO_ID).getChannels(); k++)
+                               REQUIRE(getWave(WAVE_STEREO_ID)[i][k] == 0.0f);
 
                SECTION("test silence (mono)")
                {
-                       wfx::silence(waveMono, a, b);
+                       wfx::silence(getWave(WAVE_MONO_ID).id, a, b);
 
                        for (int i=a; i<b; i++)
-                               for (int k=0; k<waveMono.getChannels(); k++)
-                                       REQUIRE(waveMono[i][k] == 0.0f);
+                               for (int k=0; k<getWave(WAVE_MONO_ID).getChannels(); k++)
+                                       REQUIRE(getWave(WAVE_MONO_ID)[i][k] == 0.0f);
                }
        }
 
@@ -64,16 +80,18 @@ TEST_CASE("waveFx")
                int a = 47;
                int b = 210;
                int range = b - a;
-               int prevSize = waveStereo.getSize();
+               int prevSize = getWave(WAVE_STEREO_ID).getSize();
 
-               REQUIRE(wfx::cut(waveStereo, a, b) == G_RES_OK);
-               REQUIRE(waveStereo.getSize() == prevSize - range);
+               wfx::cut(getWave(WAVE_STEREO_ID).id, a, b);
+
+               REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize - range);
 
                SECTION("test cut (mono)")
                {
-                       prevSize = waveMono.getSize();
-                       REQUIRE(wfx::cut(waveMono, a, b) == G_RES_OK);
-                       REQUIRE(waveMono.getSize() == prevSize - range);
+                       prevSize = getWave(WAVE_MONO_ID).getSize();
+                       wfx::cut(getWave(WAVE_MONO_ID).id, a, b);
+
+                       REQUIRE(getWave(WAVE_MONO_ID).getSize() == prevSize - range);
                }
        }
 
@@ -83,13 +101,15 @@ TEST_CASE("waveFx")
                int b = 210;
                int area = b - a;
 
-               REQUIRE(wfx::trim(waveStereo, a, b) == G_RES_OK);
-               REQUIRE(waveStereo.getSize() == area);
+               wfx::trim(getWave(WAVE_STEREO_ID).id, a, b);
+
+               REQUIRE(getWave(WAVE_STEREO_ID).getSize() == area);
 
                SECTION("test trim (mono)")
                {
-                       REQUIRE(wfx::trim(waveMono, a, b) == G_RES_OK);
-                       REQUIRE(waveMono.getSize() == area);
+                       wfx::trim(getWave(WAVE_MONO_ID).id, a, b);
+
+                       REQUIRE(getWave(WAVE_MONO_ID).getSize() == area);
                }
        }
 
@@ -98,21 +118,21 @@ TEST_CASE("waveFx")
                int a = 47;
                int b = 500;
 
-               wfx::fade(waveStereo, a, b, wfx::FADE_IN);
-               wfx::fade(waveStereo, a, b, wfx::FADE_OUT);
+               wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::FADE_IN);
+               wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::FADE_OUT);
 
-               REQUIRE(waveStereo.getFrame(a)[0] == 0.0f);
-               REQUIRE(waveStereo.getFrame(a)[1] == 0.0f);
-               REQUIRE(waveStereo.getFrame(b)[0] == 0.0f);
-               REQUIRE(waveStereo.getFrame(b)[1] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[0] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
 
                SECTION("test fade (mono)")
                {
-                       wfx::fade(waveMono, a, b, wfx::FADE_IN);
-                       wfx::fade(waveMono, a, b, wfx::FADE_OUT);
+                       wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::FADE_IN);
+                       wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::FADE_OUT);
 
-                       REQUIRE(waveMono.getFrame(a)[0] == 0.0f);
-                       REQUIRE(waveMono.getFrame(b)[0] == 0.0f);               
+                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
+                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);          
                }
        }
 
@@ -121,18 +141,18 @@ TEST_CASE("waveFx")
                int a = 11;
                int b = 79;
 
-               wfx::smooth(waveStereo, a, b);
+               wfx::smooth(getWave(WAVE_STEREO_ID).id, a, b);
 
-               REQUIRE(waveStereo.getFrame(a)[0] == 0.0f);
-               REQUIRE(waveStereo.getFrame(a)[1] == 0.0f);
-               REQUIRE(waveStereo.getFrame(b)[0] == 0.0f);
-               REQUIRE(waveStereo.getFrame(b)[1] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[0] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
+               REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
                
                SECTION("test smooth (mono)")
                {
-                       wfx::smooth(waveMono, a, b);
-                       REQUIRE(waveMono.getFrame(a)[0] == 0.0f);
-                       REQUIRE(waveMono.getFrame(b)[0] == 0.0f);               
+                       wfx::smooth(getWave(WAVE_MONO_ID).id, a, b);
+                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
+                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);          
                }
        }
 }
index 2fbeceec954a6f76914eb5b032281fee412c64a6..790828b72f72bbc19e3d96c96da424868ee176a4 100644 (file)
@@ -47,7 +47,7 @@ TEST_CASE("waveManager")
                waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav");
 
                int oldSize = res.wave->getSize();
-               waveManager::resample(res.wave.get(), 1, G_SAMPLE_RATE * 2);
+               waveManager::resample(*res.wave.get(), 1, G_SAMPLE_RATE * 2);
                
                REQUIRE(res.wave->getRate() == G_SAMPLE_RATE * 2);
                REQUIRE(res.wave->getSize() == oldSize * 2);