New upstream version 0.15.1+ds1
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Wed, 4 Jul 2018 19:29:11 +0000 (21:29 +0200)
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Wed, 4 Jul 2018 19:29:11 +0000 (21:29 +0200)
103 files changed:
ChangeLog
Makefile.am
src/core/audioBuffer.cpp
src/core/audioBuffer.h
src/core/channel.cpp
src/core/channel.h
src/core/channelManager.cpp
src/core/channelManager.h
src/core/clock.cpp
src/core/clock.h
src/core/const.h
src/core/init.cpp
src/core/kernelAudio.cpp
src/core/kernelAudio.h
src/core/kernelMidi.cpp
src/core/kernelMidi.h
src/core/midiChannel.cpp
src/core/midiChannel.h
src/core/midiChannelProc.cpp [new file with mode: 0644]
src/core/midiChannelProc.h [new file with mode: 0644]
src/core/midiDispatcher.cpp
src/core/mixer.cpp
src/core/mixer.h
src/core/mixerHandler.cpp
src/core/mixerHandler.h
src/core/patch.cpp
src/core/patch.h
src/core/plugin.cpp
src/core/pluginHost.cpp
src/core/recorder.cpp
src/core/recorder.h
src/core/sampleChannel.cpp
src/core/sampleChannel.h
src/core/sampleChannelProc.cpp [new file with mode: 0644]
src/core/sampleChannelProc.h [new file with mode: 0644]
src/core/sampleChannelRec.cpp [new file with mode: 0644]
src/core/sampleChannelRec.h [new file with mode: 0644]
src/core/types.h [new file with mode: 0644]
src/core/wave.cpp
src/core/wave.h
src/core/waveFx.cpp
src/core/waveManager.cpp
src/core/waveManager.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/recorder.cpp
src/glue/sampleEditor.cpp
src/glue/sampleEditor.h
src/glue/storage.cpp
src/gui/dialogs/about.cpp [new file with mode: 0644]
src/gui/dialogs/about.h [new file with mode: 0644]
src/gui/dialogs/gd_about.cpp [deleted file]
src/gui/dialogs/gd_about.h [deleted file]
src/gui/dialogs/gd_actionEditor.cpp
src/gui/dialogs/midiIO/midiInputChannel.cpp
src/gui/dialogs/pluginList.cpp
src/gui/dialogs/pluginList.h
src/gui/dialogs/pluginWindowGUI.cpp
src/gui/dialogs/pluginWindowGUI.h
src/gui/dialogs/sampleEditor.cpp
src/gui/elems/actionEditor/action.cpp
src/gui/elems/actionEditor/actionEditor.cpp
src/gui/elems/actionEditor/envelopeEditor.cpp
src/gui/elems/actionEditor/muteEditor.cpp
src/gui/elems/actionEditor/pianoItem.cpp
src/gui/elems/actionEditor/pianoItemOrphaned.cpp
src/gui/elems/mainWindow/keyboard/channel.cpp
src/gui/elems/mainWindow/keyboard/channel.h
src/gui/elems/mainWindow/keyboard/channelMode.cpp
src/gui/elems/mainWindow/keyboard/channelStatus.cpp
src/gui/elems/mainWindow/keyboard/column.cpp
src/gui/elems/mainWindow/keyboard/column.h
src/gui/elems/mainWindow/keyboard/midiChannel.cpp
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/mainMenu.cpp
src/gui/elems/mainWindow/mainTimer.cpp
src/gui/elems/mainWindow/mainTimer.h
src/gui/elems/plugin/pluginElement.cpp [new file with mode: 0644]
src/gui/elems/plugin/pluginElement.h [new file with mode: 0644]
src/gui/elems/sampleEditor/waveTools.cpp
src/utils/deps.cpp [deleted file]
src/utils/deps.h [deleted file]
src/utils/gui.cpp
src/utils/ver.cpp [new file with mode: 0644]
src/utils/ver.h [new file with mode: 0644]
tests/audioBuffer.cpp
tests/conf.cpp
tests/main.cpp
tests/midiMapConf.cpp
tests/patch.cpp
tests/recorder.cpp
tests/sampleChannel.cpp [new file with mode: 0644]
tests/sampleChannelProc.cpp [new file with mode: 0644]
tests/sampleChannelRec.cpp [new file with mode: 0644]
tests/utils.cpp
tests/wave.cpp
tests/waveFx.cpp
tests/waveManager.cpp

index f593b9d8af52110225014dbb7aa2b38090945647..bc3ffe30c0694204ea49e413111b843b2b648b6b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 --------------------------------------------------------------------------------
 
 
+0.15.1 --- 2018 . 07 . 03
+- Deep code refactoring, featuring Channels processors
+- Many new unit tests added
+- Simplify mutex mechanism
+- Fix wrong quantizer value on patch/project load
+- Remove the old, buggy and glitchy internal crossfade algorithm
+- Fix many potential plug-in crashes on Linux
+- Properly close plug-in window on plug-in removal 
+- Improve BPM changes while running as JACK client
+
+
 0.15.0 --- 2018 . 04 . 18
 - Refactor audio engine into frame-based processing
 - Refactor channels readers/writers into channelManager namespace
index f007a12bae2b1c71c6218fbedc9b82047d6a2ec7..2ce25d6ae00b4fa9263644eb0bd31e1960a9ee1e 100644 (file)
@@ -4,15 +4,296 @@ AUTOMAKE_OPTIONS = foreign
 # WITH_VST, LINUX, WINDOWS and OSX are varibles defined via AM_CONDITIONAL 
 # inside configure.ac.
 
-extraSources = 
+
 cppFlags =
-cxxFlags = -std=c++11 -Wall -Werror
+cxxFlags = -std=c++11 -Wall
 ldAdd =
 ldFlags =
+sourcesExtra = 
+sourcesMain = src/main.cpp 
+sourcesCore =                            \
+       src/core/const.h                       \
+       src/core/types.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/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/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/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/gui/dialogs/window.h                                 \
+       src/gui/dialogs/window.cpp             \
+       src/gui/dialogs/gd_keyGrabber.h        \
+       src/gui/dialogs/gd_keyGrabber.cpp      \
+       src/gui/dialogs/about.h                \
+       src/gui/dialogs/about.cpp              \
+       src/gui/dialogs/gd_mainWindow.h        \
+       src/gui/dialogs/gd_mainWindow.cpp      \
+       src/gui/dialogs/beatsInput.h           \
+       src/gui/dialogs/beatsInput.cpp         \
+       src/gui/dialogs/gd_warnings.h          \
+       src/gui/dialogs/gd_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/gd_config.h                              \
+       src/gui/dialogs/gd_config.cpp          \
+       src/gui/dialogs/gd_devInfo.h           \
+       src/gui/dialogs/gd_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/gd_actionEditor.h            \
+       src/gui/dialogs/gd_actionEditor.cpp    \
+       src/gui/dialogs/pluginChooser.h        \
+       src/gui/dialogs/pluginChooser.cpp      \
+       src/gui/dialogs/browser/browserBase.h           \
+       src/gui/dialogs/browser/browserBase.cpp         \
+       src/gui/dialogs/browser/browserDir.h            \
+       src/gui/dialogs/browser/browserDir.cpp          \
+       src/gui/dialogs/browser/browserLoad.h           \
+       src/gui/dialogs/browser/browserLoad.cpp         \
+       src/gui/dialogs/browser/browserSave.h           \
+       src/gui/dialogs/browser/browserSave.cpp         \
+       src/gui/dialogs/midiIO/midiOutputBase.h          \
+       src/gui/dialogs/midiIO/midiOutputBase.cpp        \
+       src/gui/dialogs/midiIO/midiOutputSampleCh.h      \
+       src/gui/dialogs/midiIO/midiOutputSampleCh.cpp    \
+       src/gui/dialogs/midiIO/midiOutputMidiCh.h        \
+       src/gui/dialogs/midiIO/midiOutputMidiCh.cpp      \
+       src/gui/dialogs/midiIO/midiInputBase.h           \
+       src/gui/dialogs/midiIO/midiInputBase.cpp         \
+       src/gui/dialogs/midiIO/midiInputChannel.h        \
+       src/gui/dialogs/midiIO/midiInputChannel.cpp      \
+       src/gui/dialogs/midiIO/midiInputMaster.h         \
+       src/gui/dialogs/midiIO/midiInputMaster.cpp       \
+       src/gui/elems/midiLearner.h        \
+       src/gui/elems/midiLearner.cpp      \
+       src/gui/elems/browser.h                  \
+       src/gui/elems/browser.cpp          \
+       src/gui/elems/soundMeter.h                   \
+       src/gui/elems/soundMeter.cpp       \
+       src/gui/elems/plugin/pluginBrowser.h                \
+       src/gui/elems/plugin/pluginBrowser.cpp              \
+       src/gui/elems/plugin/pluginParameter.h              \
+       src/gui/elems/plugin/pluginParameter.cpp            \
+       src/gui/elems/plugin/pluginElement.h                \
+       src/gui/elems/plugin/pluginElement.cpp              \
+       src/gui/elems/sampleEditor/waveform.h               \
+       src/gui/elems/sampleEditor/waveform.cpp             \
+       src/gui/elems/sampleEditor/waveTools.h              \
+       src/gui/elems/sampleEditor/waveTools.cpp            \
+       src/gui/elems/sampleEditor/volumeTool.h             \
+       src/gui/elems/sampleEditor/volumeTool.cpp           \
+       src/gui/elems/sampleEditor/boostTool.h              \
+       src/gui/elems/sampleEditor/boostTool.cpp            \
+       src/gui/elems/sampleEditor/panTool.h                \
+       src/gui/elems/sampleEditor/panTool.cpp              \
+       src/gui/elems/sampleEditor/pitchTool.h              \
+       src/gui/elems/sampleEditor/pitchTool.cpp            \
+       src/gui/elems/sampleEditor/rangeTool.h              \
+       src/gui/elems/sampleEditor/rangeTool.cpp            \
+       src/gui/elems/sampleEditor/shiftTool.h              \
+       src/gui/elems/sampleEditor/shiftTool.cpp            \
+       src/gui/elems/actionEditor/baseActionEditor.h       \
+       src/gui/elems/actionEditor/baseActionEditor.cpp     \
+       src/gui/elems/actionEditor/envelopeEditor.h         \
+       src/gui/elems/actionEditor/envelopeEditor.cpp       \
+       src/gui/elems/actionEditor/pianoRoll.h              \
+       src/gui/elems/actionEditor/pianoRoll.cpp            \
+       src/gui/elems/actionEditor/noteEditor.h                   \
+       src/gui/elems/actionEditor/noteEditor.cpp           \
+       src/gui/elems/actionEditor/basePianoItem.h          \
+       src/gui/elems/actionEditor/basePianoItem.cpp        \
+       src/gui/elems/actionEditor/pianoItem.h              \
+       src/gui/elems/actionEditor/pianoItem.cpp            \
+       src/gui/elems/actionEditor/pianoItemOrphaned.h      \
+       src/gui/elems/actionEditor/pianoItemOrphaned.cpp    \
+       src/gui/elems/actionEditor/muteEditor.h             \
+       src/gui/elems/actionEditor/muteEditor.cpp           \
+       src/gui/elems/actionEditor/actionEditor.h           \
+       src/gui/elems/actionEditor/actionEditor.cpp         \
+       src/gui/elems/actionEditor/action.h                 \
+       src/gui/elems/actionEditor/action.cpp               \
+       src/gui/elems/actionEditor/gridTool.h               \
+       src/gui/elems/actionEditor/gridTool.cpp             \
+       src/gui/elems/mainWindow/mainIO.h          \
+       src/gui/elems/mainWindow/mainIO.cpp        \
+       src/gui/elems/mainWindow/mainMenu.h        \
+       src/gui/elems/mainWindow/mainMenu.cpp      \
+       src/gui/elems/mainWindow/mainTimer.h       \
+       src/gui/elems/mainWindow/mainTimer.cpp     \
+       src/gui/elems/mainWindow/mainTransport.h   \
+       src/gui/elems/mainWindow/mainTransport.cpp \
+       src/gui/elems/mainWindow/beatMeter.h       \
+       src/gui/elems/mainWindow/beatMeter.cpp     \
+       src/gui/elems/mainWindow/keyboard/channelMode.h           \
+       src/gui/elems/mainWindow/keyboard/channelMode.cpp         \
+       src/gui/elems/mainWindow/keyboard/channelButton.h         \
+       src/gui/elems/mainWindow/keyboard/channelButton.cpp       \
+       src/gui/elems/mainWindow/keyboard/channelStatus.h         \
+       src/gui/elems/mainWindow/keyboard/channelStatus.cpp       \
+       src/gui/elems/mainWindow/keyboard/keyboard.h              \
+       src/gui/elems/mainWindow/keyboard/keyboard.cpp            \
+       src/gui/elems/mainWindow/keyboard/column.h                \
+       src/gui/elems/mainWindow/keyboard/column.cpp              \
+       src/gui/elems/mainWindow/keyboard/sampleChannel.h         \
+       src/gui/elems/mainWindow/keyboard/sampleChannel.cpp       \
+       src/gui/elems/mainWindow/keyboard/midiChannel.h           \
+       src/gui/elems/mainWindow/keyboard/midiChannel.cpp         \
+       src/gui/elems/mainWindow/keyboard/channel.h               \
+       src/gui/elems/mainWindow/keyboard/channel.cpp             \
+       src/gui/elems/mainWindow/keyboard/sampleChannelButton.h   \
+       src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \
+       src/gui/elems/mainWindow/keyboard/midiChannelButton.h     \
+       src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp   \
+       src/gui/elems/config/tabMisc.h         \
+       src/gui/elems/config/tabMisc.cpp       \
+       src/gui/elems/config/tabMidi.h         \
+       src/gui/elems/config/tabMidi.cpp       \
+       src/gui/elems/config/tabAudio.h        \
+       src/gui/elems/config/tabAudio.cpp      \
+       src/gui/elems/config/tabBehaviors.h    \
+       src/gui/elems/config/tabBehaviors.cpp  \
+       src/gui/elems/config/tabPlugins.h      \
+       src/gui/elems/config/tabPlugins.cpp    \
+       src/gui/elems/basics/scroll.h          \
+       src/gui/elems/basics/scroll.cpp        \
+       src/gui/elems/basics/boxtypes.h        \
+       src/gui/elems/basics/boxtypes.cpp      \
+       src/gui/elems/basics/baseButton.h      \
+       src/gui/elems/basics/baseButton.cpp    \
+       src/gui/elems/basics/statusButton.h    \
+       src/gui/elems/basics/statusButton.cpp  \
+       src/gui/elems/basics/button.h          \
+       src/gui/elems/basics/button.cpp        \
+       src/gui/elems/basics/idButton.h        \
+       src/gui/elems/basics/idButton.cpp      \
+       src/gui/elems/basics/resizerBar.h      \
+       src/gui/elems/basics/resizerBar.cpp    \
+       src/gui/elems/basics/input.h           \
+       src/gui/elems/basics/input.cpp         \
+       src/gui/elems/basics/liquidScroll.h    \
+       src/gui/elems/basics/liquidScroll.cpp  \
+       src/gui/elems/basics/choice.h          \
+       src/gui/elems/basics/choice.cpp        \
+       src/gui/elems/basics/dial.h            \
+       src/gui/elems/basics/dial.cpp          \
+       src/gui/elems/basics/box.h             \
+       src/gui/elems/basics/box.cpp           \
+       src/gui/elems/basics/slider.h          \
+       src/gui/elems/basics/slider.cpp        \
+       src/gui/elems/basics/progress.h        \
+       src/gui/elems/basics/progress.cpp      \
+       src/gui/elems/basics/check.h           \
+       src/gui/elems/basics/check.cpp         \
+       src/gui/elems/basics/radio.h           \
+       src/gui/elems/basics/radio.cpp         \
+       src/utils/log.h                        \
+       src/utils/log.cpp                      \
+       src/utils/time.h                       \
+       src/utils/time.cpp                     \
+       src/utils/math.h                       \
+       src/utils/math.cpp                     \
+       src/utils/gui.h                        \
+       src/utils/gui.cpp                      \
+       src/utils/gvector.h                    \
+       src/utils/fs.h                         \
+       src/utils/fs.cpp                       \
+       src/utils/ver.h                        \
+       src/utils/ver.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/conf.cpp               \
+       tests/wave.cpp               \
+       tests/waveManager.cpp        \
+       tests/patch.cpp              \
+       tests/midiMapConf.cpp        \
+       tests/pluginHost.cpp         \
+       tests/utils.cpp              \
+       tests/recorder.cpp           \
+       tests/waveFx.cpp             \
+       tests/audioBuffer.cpp        \
+       tests/sampleChannel.cpp      \
+       tests/sampleChannelProc.cpp  \
+       tests/sampleChannelRec.cpp  
 
 if WITH_VST
 
-extraSources += \
+sourcesExtra += \
        src/deps/juce/modules/juce_audio_basics/juce_audio_basics.cpp         \
        src/deps/juce/modules/juce_audio_processors/juce_audio_processors.cpp \
        src/deps/juce/modules/juce_core/juce_core.cpp                         \
@@ -44,7 +325,7 @@ endif
 
 if WINDOWS
 
-extraSources += \
+sourcesExtra += \
        src/deps/rtaudio-mod/include/asio.h                    \
        src/deps/rtaudio-mod/include/asio.cpp                  \
        src/deps/rtaudio-mod/include/asiolist.h                \
@@ -61,8 +342,6 @@ cppFlags += \
        -D__WINDOWS_WASAPI__                         \
        -D__WINDOWS_DS__
 
-cxxFlags += -Wno-error
-
 ldAdd += -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 \
   -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile -lsamplerate -lrtmidi \
   -lwinmm -lsetupapi -lksuser -ljansson -limm32 -lglu32 -lshell32 -lversion \
@@ -78,9 +357,6 @@ if LINUX
 # Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio.
 cppFlags += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
 
-# Don't stop on JUCE's unused functions.
-cxxFlags += -Wno-error=unused-function
-
 ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
   -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
   -lfreetype
@@ -89,13 +365,13 @@ endif
 
 if OSX
 
-extraSources += src/utils/cocoa.mm src/utils/cocoa.h
+sourcesExtra += src/utils/cocoa.mm src/utils/cocoa.h
 
 # Add preprocessor flags to enable CoreAudio in RtAudio.
 cppFlags += -D__MACOSX_CORE__
 
 # -ObjC++: Juce requires to build some Objective C code
-cxxFlags += -ObjC++ -Wno-auto-var-id
+cxxFlags += -ObjC++
 
 ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -ljansson -lm -lpthread \
   -lFLAC -logg -lvorbis -lvorbisenc
@@ -110,264 +386,7 @@ endif
 
 bin_PROGRAMS = giada
 
-giada_SOURCES =                        \
-src/main.cpp                           \
-src/core/const.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/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/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/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/gui/dialogs/window.h                                \
-src/gui/dialogs/window.cpp             \
-src/gui/dialogs/gd_keyGrabber.h        \
-src/gui/dialogs/gd_keyGrabber.cpp      \
-src/gui/dialogs/gd_about.h             \
-src/gui/dialogs/gd_about.cpp           \
-src/gui/dialogs/gd_mainWindow.h        \
-src/gui/dialogs/gd_mainWindow.cpp      \
-src/gui/dialogs/beatsInput.h           \
-src/gui/dialogs/beatsInput.cpp         \
-src/gui/dialogs/gd_warnings.h          \
-src/gui/dialogs/gd_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/gd_config.h                             \
-src/gui/dialogs/gd_config.cpp          \
-src/gui/dialogs/gd_devInfo.h           \
-src/gui/dialogs/gd_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/gd_actionEditor.h           \
-src/gui/dialogs/gd_actionEditor.cpp    \
-src/gui/dialogs/pluginChooser.h        \
-src/gui/dialogs/pluginChooser.cpp      \
-src/gui/dialogs/browser/browserBase.h           \
-src/gui/dialogs/browser/browserBase.cpp         \
-src/gui/dialogs/browser/browserDir.h            \
-src/gui/dialogs/browser/browserDir.cpp          \
-src/gui/dialogs/browser/browserLoad.h           \
-src/gui/dialogs/browser/browserLoad.cpp         \
-src/gui/dialogs/browser/browserSave.h           \
-src/gui/dialogs/browser/browserSave.cpp         \
-src/gui/dialogs/midiIO/midiOutputBase.h          \
-src/gui/dialogs/midiIO/midiOutputBase.cpp        \
-src/gui/dialogs/midiIO/midiOutputSampleCh.h      \
-src/gui/dialogs/midiIO/midiOutputSampleCh.cpp    \
-src/gui/dialogs/midiIO/midiOutputMidiCh.h        \
-src/gui/dialogs/midiIO/midiOutputMidiCh.cpp      \
-src/gui/dialogs/midiIO/midiInputBase.h           \
-src/gui/dialogs/midiIO/midiInputBase.cpp         \
-src/gui/dialogs/midiIO/midiInputChannel.h        \
-src/gui/dialogs/midiIO/midiInputChannel.cpp      \
-src/gui/dialogs/midiIO/midiInputMaster.h         \
-src/gui/dialogs/midiIO/midiInputMaster.cpp       \
-src/gui/elems/midiLearner.h        \
-src/gui/elems/midiLearner.cpp      \
-src/gui/elems/browser.h                         \
-src/gui/elems/browser.cpp          \
-src/gui/elems/soundMeter.h                  \
-src/gui/elems/soundMeter.cpp       \
-src/gui/elems/plugin/pluginBrowser.h                \
-src/gui/elems/plugin/pluginBrowser.cpp              \
-src/gui/elems/plugin/pluginParameter.h              \
-src/gui/elems/plugin/pluginParameter.cpp            \
-src/gui/elems/sampleEditor/waveform.h               \
-src/gui/elems/sampleEditor/waveform.cpp             \
-src/gui/elems/sampleEditor/waveTools.h              \
-src/gui/elems/sampleEditor/waveTools.cpp            \
-src/gui/elems/sampleEditor/volumeTool.h             \
-src/gui/elems/sampleEditor/volumeTool.cpp           \
-src/gui/elems/sampleEditor/boostTool.h              \
-src/gui/elems/sampleEditor/boostTool.cpp            \
-src/gui/elems/sampleEditor/panTool.h                \
-src/gui/elems/sampleEditor/panTool.cpp              \
-src/gui/elems/sampleEditor/pitchTool.h              \
-src/gui/elems/sampleEditor/pitchTool.cpp            \
-src/gui/elems/sampleEditor/rangeTool.h              \
-src/gui/elems/sampleEditor/rangeTool.cpp            \
-src/gui/elems/sampleEditor/shiftTool.h              \
-src/gui/elems/sampleEditor/shiftTool.cpp            \
-src/gui/elems/actionEditor/baseActionEditor.h       \
-src/gui/elems/actionEditor/baseActionEditor.cpp     \
-src/gui/elems/actionEditor/envelopeEditor.h         \
-src/gui/elems/actionEditor/envelopeEditor.cpp       \
-src/gui/elems/actionEditor/pianoRoll.h              \
-src/gui/elems/actionEditor/pianoRoll.cpp            \
-src/gui/elems/actionEditor/noteEditor.h                          \
-src/gui/elems/actionEditor/noteEditor.cpp           \
-src/gui/elems/actionEditor/basePianoItem.h          \
-src/gui/elems/actionEditor/basePianoItem.cpp        \
-src/gui/elems/actionEditor/pianoItem.h              \
-src/gui/elems/actionEditor/pianoItem.cpp            \
-src/gui/elems/actionEditor/pianoItemOrphaned.h      \
-src/gui/elems/actionEditor/pianoItemOrphaned.cpp    \
-src/gui/elems/actionEditor/muteEditor.h             \
-src/gui/elems/actionEditor/muteEditor.cpp           \
-src/gui/elems/actionEditor/actionEditor.h           \
-src/gui/elems/actionEditor/actionEditor.cpp         \
-src/gui/elems/actionEditor/action.h                 \
-src/gui/elems/actionEditor/action.cpp               \
-src/gui/elems/actionEditor/gridTool.h               \
-src/gui/elems/actionEditor/gridTool.cpp             \
-src/gui/elems/mainWindow/mainIO.h          \
-src/gui/elems/mainWindow/mainIO.cpp        \
-src/gui/elems/mainWindow/mainMenu.h        \
-src/gui/elems/mainWindow/mainMenu.cpp      \
-src/gui/elems/mainWindow/mainTimer.h       \
-src/gui/elems/mainWindow/mainTimer.cpp     \
-src/gui/elems/mainWindow/mainTransport.h   \
-src/gui/elems/mainWindow/mainTransport.cpp \
-src/gui/elems/mainWindow/beatMeter.h       \
-src/gui/elems/mainWindow/beatMeter.cpp     \
-src/gui/elems/mainWindow/keyboard/channelMode.h           \
-src/gui/elems/mainWindow/keyboard/channelMode.cpp         \
-src/gui/elems/mainWindow/keyboard/channelButton.h         \
-src/gui/elems/mainWindow/keyboard/channelButton.cpp       \
-src/gui/elems/mainWindow/keyboard/channelStatus.h         \
-src/gui/elems/mainWindow/keyboard/channelStatus.cpp       \
-src/gui/elems/mainWindow/keyboard/keyboard.h              \
-src/gui/elems/mainWindow/keyboard/keyboard.cpp            \
-src/gui/elems/mainWindow/keyboard/column.h                \
-src/gui/elems/mainWindow/keyboard/column.cpp              \
-src/gui/elems/mainWindow/keyboard/sampleChannel.h         \
-src/gui/elems/mainWindow/keyboard/sampleChannel.cpp       \
-src/gui/elems/mainWindow/keyboard/midiChannel.h           \
-src/gui/elems/mainWindow/keyboard/midiChannel.cpp         \
-src/gui/elems/mainWindow/keyboard/channel.h               \
-src/gui/elems/mainWindow/keyboard/channel.cpp             \
-src/gui/elems/mainWindow/keyboard/sampleChannelButton.h   \
-src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \
-src/gui/elems/mainWindow/keyboard/midiChannelButton.h     \
-src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp   \
-src/gui/elems/config/tabMisc.h         \
-src/gui/elems/config/tabMisc.cpp       \
-src/gui/elems/config/tabMidi.h         \
-src/gui/elems/config/tabMidi.cpp       \
-src/gui/elems/config/tabAudio.h        \
-src/gui/elems/config/tabAudio.cpp      \
-src/gui/elems/config/tabBehaviors.h    \
-src/gui/elems/config/tabBehaviors.cpp  \
-src/gui/elems/config/tabPlugins.h      \
-src/gui/elems/config/tabPlugins.cpp    \
-src/gui/elems/basics/scroll.h          \
-src/gui/elems/basics/scroll.cpp        \
-src/gui/elems/basics/boxtypes.h        \
-src/gui/elems/basics/boxtypes.cpp      \
-src/gui/elems/basics/baseButton.h      \
-src/gui/elems/basics/baseButton.cpp    \
-src/gui/elems/basics/statusButton.h    \
-src/gui/elems/basics/statusButton.cpp  \
-src/gui/elems/basics/button.h          \
-src/gui/elems/basics/button.cpp        \
-src/gui/elems/basics/idButton.h        \
-src/gui/elems/basics/idButton.cpp      \
-src/gui/elems/basics/resizerBar.h      \
-src/gui/elems/basics/resizerBar.cpp    \
-src/gui/elems/basics/input.h           \
-src/gui/elems/basics/input.cpp         \
-src/gui/elems/basics/liquidScroll.h    \
-src/gui/elems/basics/liquidScroll.cpp  \
-src/gui/elems/basics/choice.h          \
-src/gui/elems/basics/choice.cpp        \
-src/gui/elems/basics/dial.h            \
-src/gui/elems/basics/dial.cpp          \
-src/gui/elems/basics/box.h             \
-src/gui/elems/basics/box.cpp           \
-src/gui/elems/basics/slider.h          \
-src/gui/elems/basics/slider.cpp        \
-src/gui/elems/basics/progress.h        \
-src/gui/elems/basics/progress.cpp      \
-src/gui/elems/basics/check.h           \
-src/gui/elems/basics/check.cpp         \
-src/gui/elems/basics/radio.h           \
-src/gui/elems/basics/radio.cpp         \
-src/utils/log.h                        \
-src/utils/log.cpp                      \
-src/utils/time.h                       \
-src/utils/time.cpp                     \
-src/utils/math.h                       \
-src/utils/math.cpp                     \
-src/utils/gui.h                        \
-src/utils/gui.cpp                      \
-src/utils/gvector.h                    \
-src/utils/fs.h                         \
-src/utils/fs.cpp                       \
-src/utils/deps.h                       \
-src/utils/deps.cpp                     \
-src/utils/string.h                     \
-src/utils/string.cpp                   \
-src/deps/rtaudio-mod/RtAudio.h         \
-src/deps/rtaudio-mod/RtAudio.cpp
-
-giada_SOURCES += $(extraSources)
+giada_SOURCES = $(sourcesCore) $(sourcesMain) $(sourcesExtra)
 giada_CPPFLAGS = $(cppFlags)
 giada_CXXFLAGS = $(cxxFlags)
 giada_LDADD = $(ldAdd)
@@ -382,35 +401,9 @@ resource.o:
 TESTS = giada_tests
 
 check_PROGRAMS = giada_tests
-giada_tests_SOURCES =        \
-tests/main.cpp               \
-tests/conf.cpp               \
-tests/wave.cpp               \
-tests/waveManager.cpp        \
-tests/patch.cpp              \
-tests/midiMapConf.cpp        \
-tests/pluginHost.cpp         \
-tests/utils.cpp              \
-tests/recorder.cpp           \
-tests/waveFx.cpp             \
-tests/audioBuffer.cpp        \
-src/core/conf.cpp            \
-src/core/wave.cpp            \
-src/core/waveManager.cpp     \
-src/core/waveFx.cpp          \
-src/core/midiMapConf.cpp     \
-src/core/patch.cpp           \
-src/core/plugin.cpp          \
-src/core/storager.cpp        \
-src/core/recorder.cpp        \
-src/core/audioBuffer.cpp     \
-src/utils/fs.cpp             \
-src/utils/string.cpp         \
-src/utils/time.cpp           \
-src/utils/log.cpp
-
-giada_tests_SOURCES += $(extraSources)
-giada_tests_CPPFLAGS = $(cppFlags)
+giada_tests_SOURCES = $(sourcesCore) $(sourcesExtra) $(sourcesTests)
+giada_tests_CPPFLAGS = $(cppFlags) 
+giada_tests_CPPFLAGS += -DTESTS
 giada_tests_CXXFLAGS = $(cxxFlags)
 giada_tests_LDADD = $(ldAdd)
 giada_tests_LDFLAGS = $(ldFlags)
index ac64df9cfd1b9f177c3a6eb8304a23b5b18e8cf8..e54f24c8a22a7a2cc5ee26a0052f96a0416a3941 100644 (file)
@@ -60,14 +60,13 @@ bool AudioBuffer::isAllocd()     const { return m_data != nullptr; }
 /* -------------------------------------------------------------------------- */
 
 
-bool AudioBuffer::alloc(int size, int channels) noexcept
+void AudioBuffer::alloc(int size, int channels)
 {
        free();
        m_size     = size;
        m_channels = channels;
-       m_data     = new (std::nothrow) float[m_size * m_channels];     
+       m_data     = new float[m_size * m_channels];    
        clear(); // does nothing if m_data == nullptr
-       return m_data != nullptr;
 }
 
 
index 702f77acf9c2455cdec7f0cb12f9bf4560fd927e..b8b05198a1c14d139ea814bacebe8ff896128aaa 100644 (file)
@@ -30,7 +30,7 @@ public:
        int countChannels() const;
        bool isAllocd() const;
 
-       bool alloc(int size, int channels) noexcept;
+       void alloc(int size, int channels);
        void free();
 
        /* copyData
index 906b18f8e9aec57b1d60269961c6ac565d303a93..741e1ebea20ff50d9e07eadba42a0fd07d567896 100644 (file)
 
 
 using std::string;
+using namespace giada;
 using namespace giada::m;
 
 
-Channel::Channel(int type, int status, int bufferSize)
-: bufferSize     (bufferSize),
-       volume_i       (1.0f),
-       volume_d       (0.0f),
-       mute_i         (false),
-       guiChannel     (nullptr),
-       previewMode    (G_PREVIEW_NONE),
+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),
-       type           (type),
-       status         (status),
        key            (0),
        mute           (false),
-       mute_s         (false),
        solo           (false),
+       volume_i       (1.0f),
+       volume_d       (0.0f),
+       mute_i         (false),
        hasActions     (false),
        readActions    (false),
-       recStatus      (REC_STOPPED),
        midiIn         (true),
        midiInKeyPress (0x0),
        midiInKeyRel   (0x0),
@@ -83,28 +82,7 @@ Channel::Channel(int type, int status, int bufferSize)
        midiOutLmute   (0x0),
        midiOutLsolo   (0x0)
 {
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Channel::~Channel()
-{
-       status = STATUS_OFF;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Channel::allocBuffers()
-{
-       if (!vChan.alloc(bufferSize, G_MAX_IO_CHANS)) {
-               gu_log("[Channel::allocBuffers] unable to alloc memory for vChan!\n");
-               return false;
-       }
-       return true;
+       buffer.alloc(bufferSize, G_MAX_IO_CHANS);
 }
 
 
@@ -113,13 +91,14 @@ bool Channel::allocBuffers()
 
 void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex)
 {
+       using namespace giada::m;
+
        key             = src->key;
        volume          = src->volume;
        volume_i        = src->volume_i;
        volume_d        = src->volume_d;
        pan             = src->pan;
        mute_i          = src->mute_i;
-       mute_s          = src->mute_s;
        mute            = src->mute;
        solo            = src->solo;
        hasActions      = src->hasActions;
@@ -162,30 +141,9 @@ void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex)
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t& msg)
-{
-       gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
-               learn, msg.channel, msg.value, msg.offset);
-
-       /* 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;
-
-       /* merge the previously prepared channel into final message, and finally
-        * send it. */
-
-       out |= msg.value | (msg.channel << 24);
-       kernelMidi::send(out);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 bool Channel::isPlaying() const
 {
-       return status & (STATUS_PLAY | STATUS_ENDING);
+       return status == ChannelStatus::PLAY || status == ChannelStatus::ENDING;
 }
 
 
@@ -215,9 +173,9 @@ void Channel::sendMidiLmute()
        if (!midiOutL || midiOutLmute == 0x0)
                return;
        if (mute)
-               sendMidiLmessage(midiOutLsolo, midimap::muteOn);
+               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOn);
        else
-               sendMidiLmessage(midiOutLsolo, midimap::muteOff);
+               kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOff);
 }
 
 
@@ -229,31 +187,34 @@ void Channel::sendMidiLsolo()
        if (!midiOutL || midiOutLsolo == 0x0)
                return;
        if (solo)
-               sendMidiLmessage(midiOutLsolo, midimap::soloOn);
+               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOn);
        else
-               sendMidiLmessage(midiOutLsolo, midimap::soloOff);
+               kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOff);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::sendMidiLplay()
+void Channel::sendMidiLstatus()
 {
        if (!midiOutL || midiOutLplaying == 0x0)
                return;
        switch (status) {
-               case STATUS_OFF:
-                       sendMidiLmessage(midiOutLplaying, midimap::stopped);
+               case ChannelStatus::OFF:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopped);
+                       break;
+               case ChannelStatus::PLAY:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing);
+                       break;
+               case ChannelStatus::WAIT:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::waiting);
                        break;
-               case STATUS_PLAY:
-                       sendMidiLmessage(midiOutLplaying, midimap::playing);
+               case ChannelStatus::ENDING:
+                       kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopping);
                        break;
-               case STATUS_WAIT:
-                       sendMidiLmessage(midiOutLplaying, midimap::waiting);
+               default:
                        break;
-               case STATUS_ENDING:
-                       sendMidiLmessage(midiOutLplaying, midimap::stopping);
        }
 }
 
@@ -261,14 +222,6 @@ void Channel::sendMidiLplay()
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::receiveMidi(const MidiEvent& midiEvent)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 bool Channel::isMidiInAllowed(int c) const
 {
        return midiInFilter == -1 || midiInFilter == c;
@@ -299,16 +252,7 @@ float Channel::getPan() const
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::setVolumeI(float v)
-{
-       volume_i = v;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Channel::calcPanning(int ch)
+float Channel::calcPanning(int ch) const
 {
        if (pan == 0.5f) // center: nothing to do
                return 1.0;
@@ -322,15 +266,29 @@ float Channel::calcPanning(int ch)
 /* -------------------------------------------------------------------------- */
 
 
-void Channel::setPreviewMode(int m)
+void Channel::calcVolumeEnvelope()
 {
-       previewMode = m;
+       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 != G_PREVIEW_NONE;
+       return previewMode != PreviewMode::NONE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::isReadingActions() const
+{
+       return hasActions && readActions;
 }
 
 
index 8a78c558c3713ce8e5214319a397a600d34ef156..904bdb44a569c834b05c4ea8811b8287a463d5f6 100644 (file)
@@ -32,6 +32,8 @@
 #include <vector>
 #include <string>
 #include <pthread.h>
+#include "types.h"
+#include "mixer.h"
 #include "midiMapConf.h"
 #include "midiEvent.h"
 #include "recorder.h"
@@ -42,6 +44,9 @@
 #endif
 
 
+#undef Status
+
+
 class Plugin;
 class MidiMapConf;
 class geChannel;
@@ -49,79 +54,32 @@ class geChannel;
 
 class Channel
 {
-protected:
-
-       Channel(int type, int status, int bufferSize);
-
-       /* sendMidiLMessage
-       Composes a MIDI message by merging bytes from MidiMap conf class, and sends it 
-       to KernelMidi. */
-
-       void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t& msg);
-
-       /* calcPanning
-       Given an audio channel (stereo: 0 or 1) computes the current panning value. */
-
-       float calcPanning(int ch);
-
-       /* vChan
-       Virtual channel for internal processing. */
-       
-       giada::m::AudioBuffer vChan;
-
-#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
-
-       /* bufferSize
-       Size of every buffer in this channel (vChan, pChan) */
-
-       int bufferSize;
-
-       /* volume_*
-       Internal volume variables: volume_i for envelopes, volume_d keeps track of
-       the delta during volume changes. */
-       
-       float volume_i;
-       float volume_d;
-
-       bool mute_i;                // internal mute
-       
 public:
 
-       virtual ~Channel();
+       virtual ~Channel() {};
 
        /* copy
-       Makes a shallow copy (no vChan/pChan allocation) of another channel. */
+       Makes a shallow copy (no internal buffers allocation) of another channel. */
 
        virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0;
 
-       /* process
-       Merges vChannels into buffer, plus plugin processing (if any). Warning:
-       inBuffer might be nullptr if no input devices are available for recording. */
+       /* parseEvents
+       Prepares channel for rendering. This is called on each frame. */
 
-       virtual void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) = 0;
+       virtual void parseEvents(giada::m::mixer::FrameEvents fe) = 0;
 
-       /* Preview
-       Makes itself audibile for audio preview, such as Sample Editor or other
-       tools. */
+       /* process
+       Merges working buffers into 'out', plus plugin processing (if any). Warning:
+       inBuffer might be nullptr if no input devices are available for recording. */
 
-       virtual void preview(giada::m::AudioBuffer& in) = 0;
+       virtual void process(giada::m::AudioBuffer& out, const giada::m::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. If isUserGenerated means that
-       the channel has been started by a human key press and not a pre-recorded
-       action. */
+       when Mixer is reading actions from Recorder. */
 
-       virtual void start(int frame, bool doQuantize, int quantize,
-                       bool mixerIsRunning, bool forceStart, bool isUserGenerated) = 0;
+       virtual void start(int localFrame, bool doQuantize, int velocity) = 0;
 
        /* stop
        What to do when channel is stopped normally (via key or MIDI). */
@@ -131,14 +89,12 @@ public:
        /* kill
        What to do when channel stops abruptly. */
 
-       virtual void kill(int frame) = 0;
+       virtual void kill(int localFrame) = 0;
 
-       /* mute
-       What to do when channel is muted. If internal == true, set internal mute 
-       without altering main mute. */
+       /* set
+       What to do when channel is un/muted. */
 
-       virtual void setMute  (bool internal) = 0;
-       virtual void unsetMute(bool internal) = 0;
+       virtual void setMute(bool value, giada::EventType eventType) = 0;
 
        /* empty
        Frees any associated resources (e.g. waveform for SAMPLE). */
@@ -150,44 +106,10 @@ public:
 
        virtual void stopBySeq(bool chansStopOnSeqHalt) = 0;
 
-       /* quantize
-       Starts channel according to quantizer. Index = array index of mixer::channels 
-       used by recorder, localFrame = frame within the current buffer, 
-       globalFrame = frame within the whole sequencer loop.  */
-
-       virtual void quantize(int index, int localFrame, int globalFrame) = 0;
-
-       /* onZero
-       What to do when frame goes to zero, i.e. sequencer restart. */
-
-       virtual void onZero(int frame, bool recsStopOnChanHalt) = 0;
-
-       /* onBar
-       What to do when a bar has passed. */
-
-       virtual void onBar(int frame) = 0;
-
-       /* parseAction
-       Does something on a recorded action. Parameters:
-               - action *a   - action to parse
-         - localFrame  - frame number of the processed buffer
-         - globalFrame - actual frame in Mixer */
-
-        // TODO - quantize is useless!
-
-       virtual void parseAction(giada::m::recorder::action* a, int localFrame,
-    int globalFrame, int quantize, bool mixerIsRunning) = 0;
-
        /* rewind
        Rewinds channel when rewind button is pressed. */
 
-       virtual void rewind() = 0;
-
-       /* clear
-       Clears all memory buffers. This is actually useful to sample channels only. 
-       TODO - please rename it to clearBuffers. */
-
-       virtual void clear() = 0;
+       virtual void rewindBySeq() = 0;
 
        /* canInputRec
        Tells whether a channel can accept and handle input audio. Always false for
@@ -196,48 +118,66 @@ public:
 
        virtual bool canInputRec() = 0;
 
-       /* readPatch
-       Fills channel with data from patch. */
+       virtual bool hasLogicalData() const { return false; };
+       virtual bool hasEditedData()  const { return false; };
+       virtual bool hasData()        const { return false; };
 
-       virtual void readPatch(const std::string& basePath, int i);
+       virtual bool recordStart(bool canQuantize) { return true; };
+       virtual bool recordKill() { return false; };
+       virtual void recordStop() {};
+       virtual void recordMute() {};
+
+       /* prepareBuffer
+       Fill audio buffer with audio data from the internal source. This is actually 
+       useful to sample channels only. */
+
+       virtual void prepareBuffer(bool running) {};
 
-       /* writePatch
-       Fills a patch with channel values. Returns the index of the last 
-       Patch::channel_t added. */
+       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, int i);
        virtual void writePatch(int i, bool isProject);
 
        /* receiveMidi
        Receives and processes midi messages from external devices. */
 
-       virtual void receiveMidi(const giada::m::MidiEvent& midiEvent);
+       virtual void receiveMidi(const giada::m::MidiEvent& midiEvent) {};
 
-       /* allocBuffers
-       Mandatory method to allocate memory for internal buffers. Call it after the
-       object has been constructed. */
+       /* calcPanning
+       Given an audio channel (stereo: 0 or 1) computes the current panning value. */
 
-       virtual bool allocBuffers();
+       float calcPanning(int ch) const;
 
        bool isPlaying() const;
        float getPan() const;
        bool isPreview() const;
 
-       /* isMidiAllowed
-       Given a MIDI channel 'c' tells whether this channel should be allowed to receive
-       and process MIDI events on MIDI channel 'c'. */
+       /* 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*
-        * send MIDI lightning events to a physical device. */
+       Sends MIDI lightning events to a physical device. */
 
        void sendMidiLmute();
        void sendMidiLsolo();
-       void sendMidiLplay();
+       void sendMidiLstatus();
 
        void setPan(float v);
-       void setVolumeI(float v);
-       void setPreviewMode(int m);
+
+       void calcVolumeEnvelope();
 
 #ifdef WITH_VST
 
@@ -251,28 +191,47 @@ public:
 
 #endif
 
-  geChannel* guiChannel;        // pointer to a gChannel object, part of the GUI
+       /* 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. */
        
+       giada::m::AudioBuffer buffer;
+
+       giada::ChannelType   type;
+       giada::ChannelStatus status;
+       giada::ChannelStatus recStatus;
+
        /* previewMode
        Whether the channel is in audio preview mode or not. */
 
-       int previewMode;
+       giada::PreviewMode previewMode;
 
        float       pan;
        float       volume;   // global volume
        bool        armed;
        std::string name;
        int         index;    // unique id
-       int         type;     // midi or sample
-       int         status;   // status: see const.h
        int         key;      // keyboard button
        bool        mute;     // global mute
-       bool        mute_s;   // previous mute status after being solo'd TODO - remove it with mute refactoring
        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). */
+       
+       float volume_i;
+       float volume_d;
+
+       bool mute_i;          // internal mute
+       
   bool hasActions;      // has something recorded
   bool readActions;     // read what's recorded
-       int  recStatus;       // status of recordings (waiting, ending, ...)
   
   bool      midiIn;               // enable midi input
   uint32_t  midiInKeyPress;
@@ -303,6 +262,19 @@ public:
   std::vector <Plugin*> plugins;
 #endif
 
+protected:
+
+       Channel(giada::ChannelType type, giada::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
 };
 
 
index addc5cbdbe44dc715000cb798cc2c73d0a6536d5..0c3e5fdf5d3c7790aa83c221d7674f9568c7db76 100644 (file)
@@ -105,7 +105,7 @@ void readPlugins_(Channel* ch, const patch::channel_t& pch)
 
        for (const patch::plugin_t& ppl : pch.plugins) {
                Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL,
-                       &mixer::mutex_plugins, ch);
+                       &mixer::mutex, ch);
                if (plugin == nullptr)
                        continue;
 
@@ -132,21 +132,12 @@ void readPlugins_(Channel* ch, const patch::channel_t& pch)
 /* -------------------------------------------------------------------------- */
 
 
-int create(int type, int bufferSize, bool inputMonitorOn, Channel** out)
+int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out)
 {
-       Channel* ch;
-       if (type == G_CHANNEL_SAMPLE)
-               ch = new SampleChannel(bufferSize, inputMonitorOn);
+       if (type == ChannelType::SAMPLE)
+               *out = new SampleChannel(inputMonitorOn, bufferSize);
        else
-               ch = new MidiChannel(bufferSize);
-
-       if (!ch->allocBuffers()) {
-               delete ch;
-               return G_RES_ERR_MEMORY;
-       }
-
-       *out = ch;
-
+               *out = new MidiChannel(bufferSize);
        return G_RES_OK;
 }
 
@@ -157,7 +148,7 @@ int create(int type, int bufferSize, bool inputMonitorOn, Channel** out)
 int writePatch(const Channel* ch, bool isProject)
 {
        patch::channel_t pch;
-       pch.type            = ch->type;
+       pch.type            = static_cast<int>(ch->type);
        pch.index           = ch->index;
        pch.size            = ch->guiChannel->getSize();
        pch.name            = ch->name;
@@ -165,7 +156,6 @@ int writePatch(const Channel* ch, bool isProject)
        pch.armed           = ch->armed;
        pch.column          = ch->guiChannel->getColumnIndex();
        pch.mute            = ch->mute;
-       // pch.mute_s          = ch->mute_s;  TODO remove it with mute refactoring
        pch.solo            = ch->solo;
        pch.volume          = ch->volume;
        pch.pan             = ch->pan;
@@ -218,7 +208,7 @@ void writePatch(const SampleChannel* ch, bool isProject, int index)
        else
                pch.samplePath = "";
 
-       pch.mode              = ch->mode;
+       pch.mode              = static_cast<int>(ch->mode);
        pch.begin             = ch->getBegin();
        pch.end               = ch->getEnd();
        pch.boost             = ch->getBoost();
@@ -239,11 +229,10 @@ void readPatch(Channel* ch, int i)
 
        ch->key             = pch.key;
        ch->armed           = pch.armed;
-       ch->type            = pch.type;
+       ch->type            = static_cast<ChannelType>(pch.type);
        ch->name            = pch.name;
        ch->index           = pch.index;
        ch->mute            = pch.mute;
-       //ch->mute_s          = pch.mute_s;
        ch->solo            = pch.solo;
        ch->volume          = pch.volume;
        ch->pan             = pch.pan;
@@ -272,9 +261,9 @@ void readPatch(SampleChannel* ch, const string& basePath, int i)
 {
        const patch::channel_t& pch = patch::channels.at(i);
 
-       ch->mode              = pch.mode;
+       ch->mode              = static_cast<ChannelMode>(pch.mode);
        ch->readActions       = pch.recActive;
-       ch->recStatus         = pch.recActive ? REC_READING : REC_STOPPED;
+       ch->recStatus         = pch.recActive ? ChannelStatus::PLAY : ChannelStatus::OFF;
        ch->midiInVeloAsVol   = pch.midiInVeloAsVol;
        ch->midiInReadActions = pch.midiInReadActions;
        ch->midiInPitch       = pch.midiInPitch;
@@ -292,11 +281,11 @@ void readPatch(SampleChannel* ch, const string& basePath, int i)
        }
        else {
                if (res == G_RES_ERR_NO_DATA)
-                       ch->status = STATUS_EMPTY;
+                       ch->status = ChannelStatus::EMPTY;
                else
                if (res == G_RES_ERR_IO)
-                       ch->status = STATUS_MISSING;
-               ch->sendMidiLplay();  // FIXME - why sending MIDI lightning if sample status is wrong?
+                       ch->status = ChannelStatus::MISSING;
+               ch->sendMidiLstatus();  // FIXME - why sending MIDI lightning if sample status is wrong?
        }
 }
 
index 3e3a11ca4228e08effc9c18f3968eec40ae1e17a..327e27b45722a835b8c728b70fda34c3a17d6b11 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <string>
+#include "types.h"
 
 
 class Channel;
@@ -41,7 +42,7 @@ namespace giada {
 namespace m {
 namespace channelManager
 {
-int create(int type, int bufferSize, bool inputMonitorOn, Channel** out);
+int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out);
 
 int  writePatch(const Channel* ch, bool isProject);
 void writePatch(const SampleChannel* ch, bool isProject, int index);
index 9a38b6a19a19baba02c5bff3c4c1f1bc400defed..66abecc412351e9c1d783b30f5c4d5565c9116a1 100644 (file)
@@ -197,7 +197,7 @@ void setQuantize(int q)
 
 void incrCurrentFrame() {
        currentFrame++;
-       if (currentFrame > framesInLoop) {
+       if (currentFrame >= framesInLoop) {
                currentFrame = 0;
                currentBeat  = 0;
        }
@@ -363,6 +363,15 @@ void recvJackSync()
 /* -------------------------------------------------------------------------- */
 
 
+bool canQuantize()
+{
+       return getQuantize() > 0 && isRunning();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 int getCurrentFrame()
 {
        return currentFrame;
index c7dd35e72674bbce0b0b35cc2177ca5a35c03765..43f225b1933c2c0506d8f4c3002343d91693c8ab 100644 (file)
@@ -71,6 +71,11 @@ Tells whether a quanto unit has passed yet. */
 
 bool quantoHasPassed();
 
+/* quantoHasPassed
+Whether the quantizer value is > 0 and the clock is running. */
+
+bool canQuantize();
+
 /* updateFrameBars
 Updates bpm, frames, beats and so on. */
 
index e93bcd04a95235f6c013a126e06b886e2a244174..6d2e6ba93f1ccba0e5670e6c89bf21995c217bd5 100644 (file)
 
 /* -- version --------------------------------------------------------------- */
 #define G_APP_NAME      "Giada"
-#define G_VERSION_STR   "0.15.0"
+#define G_VERSION_STR   "0.15.1"
 #define G_VERSION_MAJOR 0
 #define G_VERSION_MINOR 15
-#define G_VERSION_PATCH 0
+#define G_VERSION_PATCH 1
 
 #define CONF_FILENAME "giada.conf"
 
 
 /* -- MIN/MAX values -------------------------------------------------------- */
 #define G_MIN_BPM           20.0f
+#define G_MIN_BPM_STR       "20.0"
 #define G_MAX_BPM           999.0f
+#define G_MAX_BPM_STR       "999.0"
 #define G_MAX_BEATS                              32
 #define G_MAX_BARS                               32
 #define G_MAX_QUANTIZE      8
 #define G_MIN_DB_SCALE      60.0f
 #define G_MIN_COLUMN_WIDTH  140
 #define G_MAX_BOOST_DB      20.0f
+#define G_MIN_PITCH         0.1f
 #define G_MAX_PITCH         4.0f
 #define G_MAX_GRID_VAL      64
 #define G_MIN_BUF_SIZE      8
 #define G_DEFAULT_BOOST            1.0f
 #define G_DEFAULT_OUT_VOL          1.0f
 #define G_DEFAULT_IN_VOL           1.0f
-#define G_DEFAULT_CHANMODE         SINGLE_BASIC
 #define G_DEFAULT_BPM              120.0f
 #define G_DEFAULT_BEATS            4
 #define G_DEFAULT_BARS             1
 
 
 
-/* -- mixer statuses and modes ---------------------------------------------- */
-#define LOOP_BASIC                      0x01 // 0000 0001  chanMode
-#define LOOP_ONCE                               0x02 // 0000 0010  chanMode
-#define        SINGLE_BASIC             0x04 // 0000 0100  chanMode
-#define SINGLE_PRESS            0x08 // 0000 1000      chanMode
-#define SINGLE_RETRIG           0x10 // 0001 0000      chanMode
-#define LOOP_REPEAT                     0x20 // 0010 0000  chanMode
-#define SINGLE_ENDLESS   0x40 // 0100 0000  chanMode
-#define LOOP_ONCE_BAR    0x80 // 1000 0000  chanMode
-
-#define LOOP_ANY                                0xA3 // 1010 0011  chanMode - any loop mode
-#define SINGLE_ANY                0x5C // 0101 1100  chanMode - any single mode
-
-#define        STATUS_ENDING            0x01 // 0000 0001  chanStatus - ending            (loop mode only)
-#define        STATUS_WAIT                      0x02 // 0000 0010  chanStatus - waiting for start (loop mode only)
-#define        STATUS_PLAY                      0x04 // 0000 0100  chanStatus - playing
-#define STATUS_OFF                      0x08 // 0000 1000  chanStatus - off
-#define STATUS_EMPTY      0x10 // 0001 0000  chanStatus - not loaded (empty chan)
-#define STATUS_MISSING   0x20 // 0010 0000  chanStatus - not found
-#define STATUS_WRONG     0x40 // 0100 0000  chanStatus - something wrong (freq, bitrate, ...)
-
-#define REC_WAITING                     0x01 // 0000 0001
-#define REC_ENDING       0x02 // 0000 0010
-#define REC_READING      0x04 // 0000 0100
-#define REC_STOPPED      0x08 // 0000 1000
-
-
-
-/* -- preview modes --------------------------------------------------------- */
-#define G_PREVIEW_NONE   0x00
-#define G_PREVIEW_NORMAL 0x01
-#define G_PREVIEW_LOOP   0x02
-
-
-
 /* -- actions --------------------------------------------------------------- */
 #define G_ACTION_KEYPRESS              0x01 // 0000 0001
 #define G_ACTION_KEYREL                        0x02 // 0000 0010
 
 
 
-/* -- channel types --------------------------------------------------------- */
-#define G_CHANNEL_SAMPLE 0x01
-#define G_CHANNEL_MIDI   0x02
-
-
-
 /* -- unique IDs of mainWin's subwindows ------------------------------------ */
 /* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */
 #define WID_BEATS         -1
@@ -361,7 +322,6 @@ const int MIDI_CHANS[16] = {
 #define PATCH_KEY_CHANNEL_NAME                 "name"
 #define PATCH_KEY_CHANNEL_COLUMN               "column"
 #define PATCH_KEY_CHANNEL_MUTE                 "mute"
-#define PATCH_KEY_CHANNEL_MUTE_S               "mute_s"
 #define PATCH_KEY_CHANNEL_SOLO                 "solo"
 #define PATCH_KEY_CHANNEL_VOLUME               "volume"
 #define PATCH_KEY_CHANNEL_PAN                  "pan"
index 1809e50b5fd416dc8c1c9f1d6e2b625ce5e9ccd8..f9cac87fd1381c68165af08b95479f5c026812d8 100644 (file)
@@ -29,6 +29,9 @@
 #ifdef __APPLE__
        #include <pwd.h>
 #endif
+#if defined(__linux__) && defined(WITH_VST)
+       #include <X11/Xlib.h> // For XInitThreads
+#endif
 #include "../utils/log.h"
 #include "../utils/fs.h"
 #include "../utils/gui.h"
@@ -130,6 +133,14 @@ void init_prepareMidiMap()
 
 void init_startGUI(int argc, char** argv)
 {
+       /* 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)
+       XInitThreads();
+#endif
+
        G_MainWin = new gdMainWindow(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv);
        G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW,
     conf::mainWindowH);
@@ -183,27 +194,24 @@ void init_shutdown()
        else
                gu_log("[init] configuration saved\n");
 
-       /* if kernelAudio::getStatus() we close the kernelAudio FIRST, THEN the mixer.
-        * The opposite could cause random segfaults (even now with RtAudio?). */
-
-       if (kernelAudio::getStatus()) {
-               kernelAudio::closeDevice();
-               gu_log("[init] KernelAudio closed\n");
-               mixer::close();
-               gu_log("[init] Mixer closed\n");
-       }
-
        recorder::clearAll();
        gu_log("[init] Recorder cleaned up\n");
 
 #ifdef WITH_VST
 
-       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
+       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex);
   pluginHost::close();
        gu_log("[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");
+       }
+
        gu_log("[init] Giada " G_VERSION_STR " closed\n\n");
        gu_logClose();
 }
index c1fa4e4b72ce9c8f5d37c115d31f94fae67faf53..e30c6f68906d54bdc68ee7f3db0bfc5d48fff6af 100644 (file)
@@ -439,6 +439,9 @@ bool hasAPI(int API)
 }
 
 
+int getAPI() { return api; }
+
+
 /* -------------------------------------------------------------------------- */
 
 
index 88dbc44d8bb59e7580c60666ab18279c9352c541..bd256fef7fa99adbced35b0ceeef64f2362b669c 100644 (file)
@@ -78,6 +78,7 @@ int getDeviceByName(const char* name);
 int getDefaultOut();
 int getDefaultIn();
 bool hasAPI(int API);
+int getAPI();
 
 #ifdef __linux__
 
index 22331e594a712cfde96f26a974d2c18176e212e7..52f760bdd47931b4206b5f410c527bf1ef559f93 100644 (file)
@@ -256,6 +256,25 @@ void send(int b1, int b2, int b3)
 /* -------------------------------------------------------------------------- */
 
 
+void sendMidiLightning(uint32_t learn, const midimap::message_t& msg)
+{
+       gu_log("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, msg.channel, 
+               msg.value, msg.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;
+
+       /* Merge the previously prepared channel into final message, and finally send 
+       it. */
+       out |= msg.value | (msg.channel << 24);
+       send(out);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 unsigned countInPorts()
 {
        return numInPorts;
index d1c8e866b0387cd81988377e7accdbde30158753..450854c41b2b60ab9da91f50dab67b7a020f511b 100644 (file)
 #define G_KERNELMIDI_H
 
 
-#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 "midiMapConf.h"
 
 
 namespace giada {
@@ -54,14 +51,15 @@ channel. */
 uint32_t setChannel(uint32_t iValue, int channel);
 
 /* send
- * send a MIDI message 's' (uint32_t). */
+Sends a MIDI message 's' as uint32_t or as separate bytes. */
 
 void send(uint32_t s);
+void send(int b1, int b2=-1, int b3=-1);
 
-/* send (2)
- * send separate bytes of MIDI message. */
+/* sendMidiLightning
+Sends a MIDI lightning message defined by 'msg'. */
 
-void send(int b1, int b2=-1, int b3=-1);
+void sendMidiLightning(uint32_t learn, const midimap::message_t& msg);
 
 /* setApi
  * set the Api in use for both in & out messages. */
index 4445d7533e5961a584db6a8785aa9e1efdfbc150..4c83b02bcdfaee5d73a4d93282291a29ff596136 100644 (file)
@@ -26,7 +26,7 @@
 
 
 #include "../utils/log.h"
-#include "midiChannel.h"
+#include "midiChannelProc.h"
 #include "channelManager.h"
 #include "channel.h"
 #include "patch.h"
 #include "mixer.h"
 #include "pluginHost.h"
 #include "kernelMidi.h"
+#include "midiChannel.h"
 
 
 using std::string;
+using namespace giada;
 using namespace giada::m;
 
 
 MidiChannel::MidiChannel(int bufferSize)
-       : Channel    (G_CHANNEL_MIDI, STATUS_OFF, bufferSize),
+       : Channel    (ChannelType::MIDI, ChannelStatus::OFF, bufferSize),
                midiOut    (false),
                midiOutChan(MIDI_CHANS[0])
 {
@@ -53,12 +55,6 @@ MidiChannel::MidiChannel(int bufferSize)
 /* -------------------------------------------------------------------------- */
 
 
-MidiChannel::~MidiChannel() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
 {
        Channel::copy(src_, pluginMutex);
@@ -71,179 +67,109 @@ void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
 /* -------------------------------------------------------------------------- */
 
 
-#ifdef WITH_VST
-
-void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame)
+void MidiChannel::parseEvents(mixer::FrameEvents fe)
 {
-       juce::MidiMessage message = juce::MidiMessage(
-               kernelMidi::getB1(msg),
-               kernelMidi::getB2(msg),
-               kernelMidi::getB3(msg));
-       midiBuffer.addEvent(message, localFrame);
+       midiChannelProc::parseEvents(this, fe);
 }
 
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::onBar(int frame) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::stop() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::empty() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::quantize(int index, int localFrame, int globalFrame) {}
-
-
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::parseAction(recorder::action* a, int localFrame,
-               int globalFrame, int quantize, bool mixerIsRunning)
+void MidiChannel::process(giada::m::AudioBuffer& out, 
+       const giada::m::AudioBuffer& in, bool audible, bool running)
 {
-       if (a->type == G_ACTION_MIDI)
-               sendMidi(a, localFrame);
+       midiChannelProc::process(this, out, in, audible);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::onZero(int frame, bool recsStopOnChanHalt)
+void MidiChannel::stopBySeq(bool chansStopOnSeqHalt)
 {
-       if (status == STATUS_ENDING) {
-               status = STATUS_OFF;
-               sendMidiLplay();
-       }
-       else
-       if (status == STATUS_WAIT) {
-               status = STATUS_PLAY;
-               sendMidiLplay();
-       }
+       midiChannelProc::stopBySeq(this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::setMute(bool internal)
+void MidiChannel::start(int frame, bool doQuantize, int velocity)
 {
-       mute = true;    // internal mute does not exist for midi (for now)
-       if (midiOut)
-               kernelMidi::send(MIDI_ALL_NOTES_OFF);
-#ifdef WITH_VST
-               addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-#endif
-       sendMidiLmute();
+       midiChannelProc::start(this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::unsetMute(bool internal)
+void MidiChannel::kill(int localFrame)
 {
-       mute = false;   // internal mute does not exist for midi (for now)
-       sendMidiLmute();
+       midiChannelProc::kill(this, localFrame);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in)
+void MidiChannel::rewindBySeq()
 {
-#ifdef WITH_VST
-       pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
-#endif
-
-       /* TODO - isn't this useful only if WITH_VST ? */
-       for (int i=0; i<out.countFrames(); i++)
-               for (int j=0; j<out.countChannels(); j++)
-                       out[i][j] += vChan[i][j] * volume;
+       midiChannelProc::rewindBySeq(this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::preview(giada::m::AudioBuffer& out)
+void MidiChannel::setMute(bool value, EventType eventType)
 {
-       // No preview for MIDI channels (for now).
+       midiChannelProc::setMute(this, value);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::start(int frame, bool doQuantize, int quantize,
-               bool mixerIsRunning, bool forceStart, bool isUserGenerated)
+void MidiChannel::readPatch(const string& basePath, int i)
 {
-       switch (status) {
-               case STATUS_PLAY:
-                       status = STATUS_ENDING;
-                       sendMidiLplay();
-                       break;
-               case STATUS_ENDING:
-               case STATUS_WAIT:
-                       status = STATUS_OFF;
-                       sendMidiLplay();
-                       break;
-               case STATUS_OFF:
-                       status = STATUS_WAIT;
-                       sendMidiLplay();
-                       break;
-       }
+       Channel::readPatch("", i);
+       channelManager::readPatch(this, i);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::stopBySeq(bool chansStopOnSeqHalt)
+void MidiChannel::writePatch(int i, bool isProject)
 {
-       kill(0);
+       Channel::writePatch(i, isProject);
+       channelManager::writePatch(this, isProject, i);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::kill(int frame)
-{
-       if (status & (STATUS_PLAY | STATUS_ENDING)) {
-               if (midiOut)
-                       kernelMidi::send(MIDI_ALL_NOTES_OFF);
 #ifdef WITH_VST
-               addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-#endif
-       }
-       status = STATUS_OFF;
-       sendMidiLplay();
+
+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::readPatch(const string& basePath, int i)
+void MidiChannel::empty()
 {
-       Channel::readPatch("", i);
-       channelManager::readPatch(this, i);
+       hasActions = false;
 }
 
 
@@ -252,7 +178,7 @@ void MidiChannel::readPatch(const string& basePath, int i)
 
 void MidiChannel::sendMidi(recorder::action* a, int localFrame)
 {
-       if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
+       if (isPlaying() && !mute) {
                if (midiOut)
                        kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]);
 
@@ -265,7 +191,7 @@ void MidiChannel::sendMidi(recorder::action* a, int localFrame)
 
 void MidiChannel::sendMidi(uint32_t data)
 {
-       if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
+       if (isPlaying() && !mute) {
                if (midiOut)
                        kernelMidi::send(data | MIDI_CHANS[midiOutChan]);
 #ifdef WITH_VST
@@ -278,29 +204,6 @@ void MidiChannel::sendMidi(uint32_t data)
 /* -------------------------------------------------------------------------- */
 
 
-void MidiChannel::rewind()
-{
-       if (midiOut)
-               kernelMidi::send(MIDI_ALL_NOTES_OFF);
-#ifdef WITH_VST
-               addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::writePatch(int i, bool isProject)
-{
-       Channel::writePatch(i, isProject);
-       channelManager::writePatch(this, isProject, i);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
 {
        if (!armed)
@@ -339,10 +242,4 @@ void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
 bool MidiChannel::canInputRec()
 {
        return false; // midi channels don't handle input audio
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::clear() {}
+}
\ No newline at end of file
index 3ecad62aecb401652e1dc437d6d99bd8031f7edd..08f272935f15686b30dab313ddb6949387b243e5 100644 (file)
@@ -44,28 +44,20 @@ class MidiChannel : public Channel
 public:
 
        MidiChannel(int bufferSize);
-       ~MidiChannel();
 
        void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
-       void clear() override;
-       void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override;
-       void preview(giada::m::AudioBuffer& out) override;
-       void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
-               bool forceStart, bool isUserGenerated) override;
-       void kill(int frame) override;
+       void parseEvents(giada::m::mixer::FrameEvents fe) override;
+       void process(giada::m::AudioBuffer& out, const giada::m::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 rewind() override;
-       void setMute(bool internal) override;
-       void unsetMute(bool internal) override;
+       void stop() override {};
+       void rewindBySeq() override;
+       void setMute(bool value, giada::EventType eventType) override;
        void readPatch(const std::string& basePath, int i) override;
        void writePatch(int i, bool isProject) override;
-       void quantize(int index, int localFrame, int globalFrame) override;
-       void onZero(int frame, bool recsStopOnChanHalt) override;
-       void onBar(int frame) override;
-       void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame,
-               int quantize, bool mixerIsRunning) override;
        void receiveMidi(const giada::m::MidiEvent& midiEvent) override;
        bool canInputRec() override;
 
diff --git a/src/core/midiChannelProc.cpp b/src/core/midiChannelProc.cpp
new file mode 100644 (file)
index 0000000..965d440
--- /dev/null
@@ -0,0 +1,163 @@
+#include "midiChannel.h"
+#include "pluginHost.h"
+#include "kernelMidi.h"
+#include "const.h"
+#include "midiChannelProc.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();
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parseAction_(MidiChannel* ch, const recorder::action* a, int localFrame)
+{
+       if (ch->isPlaying() && !ch->mute) {
+               if (ch->midiOut)
+                       kernelMidi::send(a->iValue | MIDI_CHANS[ch->midiOutChan]);
+#ifdef WITH_VST
+               ch->addVstMidiEvent(a->iValue, localFrame);
+#endif
+       }
+}
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents(MidiChannel* ch, mixer::FrameEvents fe)
+{
+       if (fe.onFirstBeat)
+               onFirstBeat_(ch);
+       for (const recorder::action* action : fe.actions)
+               if (action->chan == ch->index && action->type == G_ACTION_MIDI)
+                       parseAction_(ch, action, fe.frameLocal);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void process(MidiChannel* ch, giada::m::AudioBuffer& out, 
+       const giada::m::AudioBuffer& in, bool audible)
+{
+       #ifdef WITH_VST
+               pluginHost::processStack(ch->buffer, pluginHost::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          
+               }
+       ch->sendMidiLmute();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopBySeq(MidiChannel* ch)
+{
+       kill(ch, 0);
+}
+}}};
\ No newline at end of file
diff --git a/src/core/midiChannelProc.h b/src/core/midiChannelProc.h
new file mode 100644 (file)
index 0000000..ffd4929
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef G_MIDI_CHANNEL_PROC_H
+#define G_MIDI_CHANNEL_PROC_H
+
+
+#include "mixer.h"
+#include "audioBuffer.h"
+
+
+class MidiChannel;
+
+
+namespace giada {
+namespace m {
+namespace midiChannelProc
+{
+/* parseEvents
+Parses events gathered by Mixer::masterPlay(). */
+
+void parseEvents(MidiChannel* ch, mixer::FrameEvents ev);
+
+/**/
+void process(MidiChannel* ch, giada::m::AudioBuffer& out, 
+       const giada::m::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);
+}}};
+
+
+#endif
\ No newline at end of file
index 1d49fc12f95f98d0fd5b3665952ba74961b35e6d..e5d93ea15942be2e3291acd973e4c984f3ed1341 100644 (file)
@@ -153,7 +153,7 @@ void processChannels(const MidiEvent& midiEvent)
                        else 
                        if (pure == sch->midiInReadActions) {
                                gu_log("  >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure);
-                               c::channel::toggleReadingRecs(sch, false);
+                               c::channel::toggleReadingActions(sch, false);
                        }
                }
 
index 7592927ab1fff5e609c45a98e455ebde734a9451..1931ed73457a128d41f343dd9e5f8e45d278fe1a 100644 (file)
@@ -87,15 +87,6 @@ Sample position while recording. */
 int inputTracker = 0;
 
 
-/* -------------------------------------------------------------------------- */
-
-
-bool isChannelAudible(Channel* ch)
-{
-       return !hasSolos || (hasSolos && ch->solo);
-}
-
-
 /* -------------------------------------------------------------------------- */
 
 /* computePeak */
@@ -159,40 +150,15 @@ void processLineIn(const AudioBuffer& inBuf, unsigned frame)
 
 /* -------------------------------------------------------------------------- */
 
-/* clearAllBuffers
+/* prepareBuffers
 Cleans up every buffer, both in Mixer and in channels. */
 
-void clearAllBuffers(AudioBuffer& outBuf)
+void prepareBuffers(AudioBuffer& outBuf)
 {
        outBuf.clear();
        vChanInToOut.clear();
-
-       pthread_mutex_lock(&mutex_chans);
        for (Channel* channel : channels)
-               channel->clear();
-       pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* readActions
-Reads all recorded actions. */
-
-void readActions(unsigned frame)
-{
-       pthread_mutex_lock(&mutex_recs);
-       for (unsigned i=0; i<recorder::frames.size(); i++) {
-               if (recorder::frames.at(i) != clock::getCurrentFrame())
-                       continue;
-               for (recorder::action* action : recorder::global.at(i)) {
-                       Channel* ch = mh::getChannelByIndex(action->chan);
-                       ch->parseAction(action, frame, clock::getCurrentFrame(), 
-                               clock::getQuantize(), clock::isRunning());
-               }
-               break;
-       }
-       pthread_mutex_unlock(&mutex_recs);
+               channel->prepareBuffer(clock::isRunning());
 }
 
 
@@ -212,27 +178,6 @@ void doQuantize(unsigned frame)
                rewindWait = false;
                rewind();
        }
-
-       pthread_mutex_lock(&mutex_chans);
-       for (unsigned i=0; i<channels.size(); i++)
-               channels.at(i)->quantize(i, frame, clock::getCurrentFrame());
-       pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* sumChannels
-Sums channels, i.e. lets them add sample frames to their virtual channels.
-This is required for G_CHANNEL_SAMPLE only */
-
-void sumChannels(unsigned frame)
-{
-       pthread_mutex_lock(&mutex_chans);
-       for (Channel* ch : channels)
-               if (ch->type == G_CHANNEL_SAMPLE)
-                       static_cast<SampleChannel*>(ch)->sum(frame, clock::isRunning());
-       pthread_mutex_unlock(&mutex_chans);
 }
 
 
@@ -272,19 +217,12 @@ content to the output buffer). Process plugins too, if any. */
 
 void renderIO(AudioBuffer& outBuf, const AudioBuffer& inBuf)
 {
-       pthread_mutex_lock(&mutex_chans);
-       for (Channel* ch : channels) {
-               if (isChannelAudible(ch))
-                       ch->process(outBuf, inBuf);
-               ch->preview(outBuf);
-       }
-       pthread_mutex_unlock(&mutex_chans);
+       for (Channel* channel : channels)
+               channel->process(outBuf, inBuf, isChannelAudible(channel), clock::isRunning());
 
 #ifdef WITH_VST
-       pthread_mutex_lock(&mutex_plugins);
        pluginHost::processStack(outBuf, pluginHost::MASTER_OUT);
        pluginHost::processStack(vChanInToOut, pluginHost::MASTER_IN);
-       pthread_mutex_unlock(&mutex_plugins);
 #endif
 }
 
@@ -330,30 +268,8 @@ last frame). */
 
 void testBar(unsigned frame)
 {
-       if (!clock::isOnBar())
-               return;
-
-       if (metronome)
+       if (clock::isOnBar() && metronome)
                tickPlay = true;
-
-       pthread_mutex_lock(&mutex_chans);
-       for (Channel* ch : channels)
-               ch->onBar(frame);
-       pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void testFirstBeat(unsigned frame)
-{
-       if (!clock::isOnFirstBeat())
-               return;
-       pthread_mutex_lock(&mutex_chans);
-       for (Channel* ch : channels)
-               ch->onZero(frame, conf::recsStopOnChanHalt);
-       pthread_mutex_unlock(&mutex_chans);
 }
 
 
@@ -362,11 +278,9 @@ void testFirstBeat(unsigned frame)
 
 void testLastBeat()
 {
-       if (clock::isOnBeat())
-               if (metronome && !tickPlay)
-                       tockPlay = true;
+       if (clock::isOnBeat() && metronome && !tickPlay)
+               tockPlay = true;
 }
-
 }; // {anonymous}
 
 
@@ -389,9 +303,7 @@ bool   rewindWait   = false;
 bool   hasSolos     = false;
 bool   inToOut      = false;
 
-pthread_mutex_t mutex_recs;
-pthread_mutex_t mutex_chans;
-pthread_mutex_t mutex_plugins;
+pthread_mutex_t mutex;
 
 
 /* -------------------------------------------------------------------------- */
@@ -401,23 +313,16 @@ void init(int framesInSeq, int framesInBuffer)
 {
        /* Allocate virtual input channels. vChanInput has variable size: it depends
        on how many frames there are in sequencer. */
-       if (!allocVirtualInput(framesInSeq)) {
-               gu_log("[Mixer::init] vChanInput alloc error!\n");      
-               return;
-       }
-       if (!vChanInToOut.alloc(framesInBuffer, G_MAX_IO_CHANS)) {
-               gu_log("[Mixer::init] vChanInToOut alloc error!\n");    
-               return;
-       }
+       
+       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", 
                framesInSeq, framesInBuffer);   
 
        hasSolos = false;
 
-       pthread_mutex_init(&mutex_recs, nullptr);
-       pthread_mutex_init(&mutex_chans, nullptr);
-       pthread_mutex_init(&mutex_plugins, nullptr);
+       pthread_mutex_init(&mutex, nullptr);
 
        rewind();
 }
@@ -426,9 +331,9 @@ void init(int framesInSeq, int framesInBuffer)
 /* -------------------------------------------------------------------------- */
 
 
-bool allocVirtualInput(int frames)
+void allocVirtualInput(int frames)
 {
-       return vChanInput.alloc(frames, G_MAX_IO_CHANS);
+       vChanInput.alloc(frames, G_MAX_IO_CHANS);
 }
 
 
@@ -441,6 +346,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        if (!ready)
                return 0;
 
+       pthread_mutex_lock(&mutex);
+
 #ifdef __linux__
        clock::recvJackSync();
 #endif
@@ -453,23 +360,33 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        peakOut = 0.0f;  // reset peak calculator
        peakIn  = 0.0f;  // reset peak calculator
 
-       clearAllBuffers(out);
+       prepareBuffers(out);
 
        for (unsigned j=0; j<bufferSize; j++) {
-               processLineIn(in, j);
+               processLineIn(in, j);   // TODO - can go outside this loop
+
                if (clock::isRunning()) {
-                       lineInRec(in, j);
+                       FrameEvents fe;
+                       fe.frameLocal   = j;
+                       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);
+
+                       lineInRec(in, j);   // TODO - can go outside this loop
                        doQuantize(j);
                        testBar(j);
-                       testFirstBeat(j);
-                       readActions(j);
+                       testLastBeat();
                        clock::incrCurrentFrame();
-                       testLastBeat();  // this test must be the last one
                        clock::sendMIDIsync();
                }
-               sumChannels(j);
        }
-
+       
        renderIO(out, in);
 
        /* Post processing. */
@@ -486,6 +403,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        out.setData(nullptr, 0, 0);
        in.setData(nullptr, 0, 0);
 
+       pthread_mutex_unlock(&mutex);
+
        return 0;
 }
 
@@ -498,6 +417,7 @@ void close()
        clock::stop();
        while (channels.size() > 0)
                mh::deleteChannel(channels.at(0));
+       pthread_mutex_destroy(&mutex);
 }
 
 
@@ -507,12 +427,22 @@ void close()
 bool isSilent()
 {
        for (const Channel* ch : channels)
-               if (ch->status == STATUS_PLAY)
+               if (ch->isPlaying())
                        return false;
        return true;
 }
 
 
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isChannelAudible(Channel* ch)
+{
+       return !hasSolos || (hasSolos && ch->solo);
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -521,7 +451,7 @@ void rewind()
        clock::rewind();
        if (clock::isRunning())
                for (Channel* ch : channels)
-                       ch->rewind();
+                       ch->rewindBySeq();
 }
 
 
@@ -541,7 +471,8 @@ void startInputRec()
 void mergeVirtualInput()
 {
        for (Channel* ch : channels) {
-               if (ch->type == G_CHANNEL_MIDI)
+               /* TODO - move this to audioProc::*/
+               if (ch->type == ChannelType::MIDI)
                        continue;
                SampleChannel* sch = static_cast<SampleChannel*>(ch);
                if (sch->armed)
@@ -549,6 +480,4 @@ void mergeVirtualInput()
        }
        vChanInput.clear();
 }
-
-
 }}}; // giada::m::mixer::
index 36c566b7b9482e2dd67b0c8172c193be3caf03b9..4f1f341887f4fbd5a8f2065fd2aae659a8ad06bb 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <pthread.h>
 #include <vector>
+#include "recorder.h"
 #include "../deps/rtaudio-mod/RtAudio.h"
 
 
@@ -41,13 +42,56 @@ namespace giada {
 namespace m {
 namespace mixer
 {
+struct FrameEvents
+{
+       int   frameLocal;
+       int   frameGlobal;
+       bool  doQuantize;
+       bool  onBar;
+       bool  onFirstBeat;
+       bool  quantoPassed;
+       std::vector<recorder::action*> actions;
+};
+
+enum {    // const - what to do when a fadeout ends
+       DO_STOP   = 0x01,
+       DO_MUTE   = 0x02,
+       DO_MUTE_I = 0x04
+};
+
+enum {    // const - fade types
+       FADEOUT = 0x01,
+       XFADE   = 0x02
+};
+
+extern std::vector<Channel*> channels;
+
+extern bool   recording;         // is recording something?
+extern bool   ready;
+extern float  outVol;
+extern float  inVol;
+extern float  peakOut;
+extern float  peakIn;
+extern bool      metronome;
+extern int    waitRec;       // delayComp guard
+extern bool   rewindWait;         // rewind guard, if quantized
+extern bool   hasSolos;      // more than 0 channels soloed
+
+/* inToOut
+Copy, process and paste the input into the output, in order to obtain a "hear 
+what you're playing" feature. */
+
+extern bool inToOut;
+
+extern pthread_mutex_t mutex;
+
 void init(int framesInSeq, int framesInBuffer);
 
 /* allocVirtualInput
 Allocates new memory for the virtual input channel. Call this whenever you 
 shrink or resize the sequencer. */
 
-bool allocVirtualInput(int frames);
+void allocVirtualInput(int frames);
 
 void close();
 
@@ -55,13 +99,15 @@ void close();
 Core method (callback) */
 
 int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime,
-  RtAudioStreamStatus status, void* userData);
+       RtAudioStreamStatus status, void* userData);
 
 /* isSilent
 Is mixer silent? */
 
 bool isSilent();
 
+bool isChannelAudible(Channel* ch);
+
 /* rewind
 Rewinds sequencer to frame 0. */
 
@@ -77,41 +123,6 @@ Copies the virtual channel input in the channels designed for input recording.
 Called by mixerHandler on stopInputRec(). */
 
 void mergeVirtualInput();
-
-enum {    // const - what to do when a fadeout ends
-       DO_STOP   = 0x01,
-       DO_MUTE   = 0x02,
-       DO_MUTE_I = 0x04
-};
-
-enum {    // const - fade types
-       FADEOUT = 0x01,
-       XFADE   = 0x02
-};
-
-extern std::vector<Channel*> channels;
-
-extern bool   recording;         // is recording something?
-extern bool   ready;
-extern float  outVol;
-extern float  inVol;
-extern float  peakOut;
-extern float  peakIn;
-extern bool      metronome;
-extern int    waitRec;       // delayComp guard
-extern bool   rewindWait;         // rewind guard, if quantized
-extern bool   hasSolos;      // more than 0 channels soloed
-
-/* inToOut
-Copy, process and paste the input into the output, in order to obtain a "hear 
-what you're playing" feature. */
-
-extern bool inToOut;
-
-extern pthread_mutex_t mutex_recs;
-extern pthread_mutex_t mutex_chans;
-extern pthread_mutex_t mutex_plugins;
-
 }}} // giada::m::mixer::;
 
 
index ecbf42f9a51de8a806b59bb2f11a9a4b4c01b3ea..66ecc810e90a58adc6770b6721227c7610292790 100644 (file)
@@ -72,8 +72,8 @@ int readPatchPlugins(vector<patch::plugin_t>* list, int type)
        for (unsigned i=0; i<list->size(); i++) {
                patch::plugin_t *ppl = &list->at(i);
                // TODO use glue_addPlugin()
-               Plugin *plugin = pluginHost::addPlugin(ppl->path.c_str(), type,
-                               &mixer::mutex_plugins, nullptr);
+               Plugin *plugin = pluginHost::addPlugin(ppl->path.c_str(), type, 
+                       &mixer::mutex, nullptr);
                if (plugin != nullptr) {
                        plugin->setBypass(ppl->bypass);
                        for (unsigned j=0; j<ppl->params.size(); j++)
@@ -120,7 +120,7 @@ int getNewChanIndex()
 bool uniqueSamplePath(const SampleChannel* skip, const string& path)
 {
        for (const Channel* ch : mixer::channels) {
-               if (skip == ch || ch->type != G_CHANNEL_SAMPLE) // skip itself and MIDI channels
+               if (skip == ch || ch->type != ChannelType::SAMPLE) // skip itself and MIDI channels
                        continue;
                const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
                if (sch->wave != nullptr && path == sch->wave->getPath())
@@ -133,7 +133,7 @@ bool uniqueSamplePath(const SampleChannel* skip, const string& path)
 /* -------------------------------------------------------------------------- */
 
 
-Channel* addChannel(int type)
+Channel* addChannel(ChannelType type)
 {
        Channel* ch = nullptr;
        channelManager::create(type, kernelAudio::getRealBufSize(), 
@@ -142,10 +142,10 @@ Channel* addChannel(int type)
                return nullptr;
 
        while (true) {
-               if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+               if (pthread_mutex_trylock(&mixer::mutex) != 0)
                        continue;
                mixer::channels.push_back(ch);
-               pthread_mutex_unlock(&mixer::mutex_chans);
+               pthread_mutex_unlock(&mixer::mutex);
                break;
        }
 
@@ -162,12 +162,12 @@ Channel* addChannel(int type)
 void deleteChannel(Channel* target)
 {
        while (true) {
-               if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+               if (pthread_mutex_trylock(&mixer::mutex) != 0)
                        continue;
                auto it = std::find(mixer::channels.begin(), mixer::channels.end(), target);
                if (it != mixer::channels.end()) 
                        mixer::channels.erase(it);
-               pthread_mutex_unlock(&mixer::mutex_chans);
+               pthread_mutex_unlock(&mixer::mutex);
                return;
        }
 }
@@ -191,13 +191,9 @@ Channel* getChannelByIndex(int index)
 
 bool hasLogicalSamples()
 {
-       for (const Channel* ch : mixer::channels) {
-               if (ch->type != G_CHANNEL_SAMPLE)
-                       continue;
-               const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
-               if (sch->wave != nullptr && sch->wave->isLogical())
+       for (const Channel* ch : mixer::channels)
+               if (ch->hasLogicalData())
                        return true;
-       }
        return false;
 }
 
@@ -207,13 +203,9 @@ bool hasLogicalSamples()
 
 bool hasEditedSamples()
 {
-       for (const Channel* ch : mixer::channels) {
-               if (ch->type != G_CHANNEL_SAMPLE)
-                       continue;
-               const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
-               if (sch->wave != nullptr && sch->wave->isEdited())
+       for (const Channel* ch : mixer::channels)
+               if (ch->hasEditedData())
                        return true;
-       }
        return false;
 }
 
@@ -234,7 +226,7 @@ void stopSequencer()
 
 void updateSoloCount()
 {
-       for (Channel* ch : mixer::channels)
+       for (const Channel* ch : mixer::channels)
                if (ch->solo) {
                        mixer::hasSolos = true;
                        return;
@@ -307,10 +299,8 @@ bool startInputRec()
                Wave*  wave = nullptr;
                string name = string("TAKE-" + gu_iToString(patch::lastTakeId++)); // Increase lastTakeId 
 
-               int result = waveManager::createEmpty(clock::getFramesInLoop(), G_MAX_IO_CHANS,
+               waveManager::createEmpty(clock::getFramesInLoop(), G_MAX_IO_CHANS, 
                        conf::samplerate, name + ".wav", &wave); 
-               if (result != G_RES_OK)
-                       continue;
 
                sch->pushWave(wave);
                sch->name = name; 
@@ -338,6 +328,10 @@ void stopInputRec()
        mixer::mergeVirtualInput();
        mixer::recording = false;
        mixer::waitRec = 0; // in case delay compensation is in use
+
+       for (Channel* ch : mixer::channels)
+               ch->stopInputRec(clock::getCurrentFrame());
+
        gu_log("[mh] stop input recs\n");
 }
 
@@ -348,7 +342,7 @@ void stopInputRec()
 bool hasArmedSampleChannels()
 {
        for (const Channel* ch : mixer::channels)
-               if (ch->type == G_CHANNEL_SAMPLE && ch->armed)
+               if (ch->type == ChannelType::SAMPLE && ch->armed)
                        return true;
        return false;
 }
index ae160a9d8e4369463d32052fa8c6b6d34090810f..b37a14c4c7969024d44d8241d38c2d4ffa13520c 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <string>
+#include "types.h"
 
 
 class Channel;
@@ -43,7 +44,7 @@ namespace mh
 /* addChannel
 Adds a new channel of type 'type' into mixer's stack. */
 
-Channel* addChannel(int type);
+Channel* addChannel(ChannelType type);
 
 /* deleteChannel
 Completely removes a channel from the stack. */
index a834775b83708b8d1058c859d9d4eacb01d1415e..6691b6d3c03c0f3116d48ef3ec9e4ef4dccea07e 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "../utils/log.h"
 #include "../utils/string.h"
+#include "../utils/ver.h"
 #include "const.h"
+#include "types.h"
 #include "storager.h"
 #include "conf.h"
 #include "mixer.h"
@@ -82,11 +84,23 @@ Makes sure an older patch is compatible with the current version. */
 void modernize()
 {
        /* Starting from 0.15.0 actions are recorded on frames, not samples. */
-       if (versionMajor <= 0 && versionMinor < 15) {
+       if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 0)) {
                for (channel_t& ch : channels)
                        for (action_t& a : ch.actions)
                                a.frame /= 2;
        }
+
+       /* 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);
+               }
+       }
 }
 
 
@@ -227,7 +241,6 @@ bool readChannels(json_t* jContainer)
                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_MUTE_S,               channel.mute_s)) return 0;
                if (!storager::setInt   (jChannel, PATCH_KEY_CHANNEL_SOLO,                 channel.solo)) return 0;
                if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME,               channel.volume)) return 0;
                if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN,                  channel.pan)) return 0;
@@ -406,7 +419,6 @@ void writeChannels(json_t* jContainer, vector<channel_t>* channels)
                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_MUTE_S,               json_integer(channel.mute_s));
                json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO,                 json_integer(channel.solo));
                json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME,               json_real(channel.volume));
                json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN,                  json_real(channel.pan));
index 651c2af5ed78f425402552179fdf573c81ccaf88..77d48d7a28ddaed8b80c02b7541d7821c0259564 100644 (file)
@@ -64,7 +64,6 @@ struct channel_t
        std::string name;
        int         column;
        int         mute;
-       int         mute_s;
        int         solo;
        float       volume;
        float       pan;
index d6b32fa1a4a3fd8bd995ba36a18426ea41420187..afcb4c9b20cedb807000cd104278b4429880366a 100644 (file)
@@ -45,7 +45,7 @@ int Plugin::idGenerator = 1;
 /* -------------------------------------------------------------------------- */
 
 
-Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate,
+Plugin::Plugin(juce::AudioPluginInstanceplugin, double samplerate,
        int buffersize)
        : ui    (nullptr),
                plugin(plugin),
index 153b149670760c5afe9a425a859ef641deb3dc1b..6ba04653398dd768b2ee2376de1cbaa85622f98b 100644 (file)
@@ -141,6 +141,7 @@ pthread_mutex_t mutex_midi;
 void close()
 {
        messageManager->deleteInstance();
+       pthread_mutex_destroy(&mutex_midi);
 }
 
 
@@ -413,7 +414,7 @@ void processStack(AudioBuffer& outBuf, int stackType, Channel* ch)
        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 == G_CHANNEL_MIDI) 
+       if (ch != nullptr && ch->type == ChannelType::MIDI) 
                audioBuffer.clear();
        else
                for (int i=0; i<outBuf.countFrames(); i++)
index 7dddda1f512d91b452b87187216d4a0824ba04f8..7e068f4217442a87bd8b3bb0a3bbe32a23a787af 100644 (file)
@@ -102,19 +102,12 @@ void init()
 
 bool canRec(Channel* ch, bool clockRunning, bool mixerRecording)
 {
-       /* NO recording if:
-        * recorder is inactive
-        * mixer is not running
-        * mixer is recording a take somewhere
-        * channel is empty */
-
-       if (!active         ||
-                 !clockRunning   ||
-                        mixerRecording ||
-                       (ch->type == G_CHANNEL_SAMPLE && static_cast<SampleChannel*>(ch)->wave == nullptr)
-               )
-               return false;
-       return true;
+       /* Can record on a channel if:
+         - recorder is on
+         - mixer is running
+         - mixer is not recording a take somewhere
+         - channel is SAMPLE type and has data in it  */
+       return active && clockRunning && !mixerRecording && ch->type == ChannelType::SAMPLE && ch->hasData();
 }
 
 
@@ -125,7 +118,7 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue)
 {
        /* allocating the action */
 
-       action* a = (action*) malloc(sizeof(action));
+       action* a = (action*) malloc(sizeof(action)); /* TODO - AAARRRGGHHHHHH!!!! */
        a->chan   = index;
        a->type   = type;
        a->frame  = frame;
@@ -661,6 +654,20 @@ void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex)
 /* -------------------------------------------------------------------------- */
 
 
+vector<action*> getActionsOnFrame(int frame)
+{
+       for (size_t i=0; i<frames.size(); i++) {
+               if (recorder::frames.at(i) != frame)
+                       continue;
+               return global.at(i);
+       }
+       return vector<action*>();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void forEachAction(std::function<void(const action*)> f)
 {
        for (const vector<action*> actions : recorder::global)
index 0f0b0e579f1557a3e532c8b565ab4701d668a323..cdbcc2c5706d8dd1cb084b7f0338be93aa18d925 100644 (file)
@@ -79,6 +79,8 @@ Contains the actual actions. E.g.:
   global[1] = <actions> */
 
 extern std::vector<std::vector<action*>> global;
+/* TODO - this frames vs global madness must be replaced with a map:
+std::map<int, vector<actions>> */
 
 extern bool active;
 extern bool sortedActions;   // are actions sorted via sortActions()?
@@ -174,12 +176,17 @@ Returns a pointer to action in chan 'chan' of type 'action' at frame 'frame'. */
 
 int getAction(int chan, char action, int frame, struct action** out);
 
+/* getActionsOnFrame
+Returns a vector of actions that occur on frame 'frame'. */
+
+std::vector<action*> getActionsOnFrame(int frame);
+
 /* start/stopOverdub
 These functions are used when you overwrite existing actions. For example:
 pressing Mute button on a channel with some existing mute actions. */
 
 void startOverdub(int chan, char action, int frame, unsigned bufferSize);
-void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex);
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_tmixerMutex);
 
 /* forEachAction
 Applies a read-only callback on each action recorded. */
index 0a696323be7f47cec894015ebaa908771f167f0d..0ce44f6ea253d22806d1dc784ee863511d03c154 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include <cmath>
-#include <cstring>
-#include <cassert>
 #include "../utils/log.h"
-#include "../utils/fs.h"
-#include "../utils/string.h"
-#include "patch.h"
+#include "sampleChannelProc.h"
+#include "sampleChannelRec.h"
 #include "channelManager.h"
 #include "const.h"
-#include "conf.h"
-#include "clock.h"
-#include "mixer.h"
 #include "wave.h"
-#include "pluginHost.h"
-#include "waveFx.h"
-#include "waveManager.h"
-#include "mixerHandler.h"
-#include "kernelMidi.h"
-#include "kernelAudio.h"
 #include "sampleChannel.h"
 
 
 using std::string;
+using namespace giada;
 using namespace giada::m;
 
 
-SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
-       : Channel          (G_CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
-               rsmp_state       (nullptr),
-               frameRewind      (-1),
-               begin            (0),
-               end              (0),
-               pitch            (G_DEFAULT_PITCH),
-               boost            (G_DEFAULT_BOOST),
-               fadeinOn         (false),
-               fadeinVol        (1.0f),
-               fadeoutOn        (false),
-               fadeoutVol       (1.0f),
-               fadeoutTracker   (0),
-               fadeoutStep      (G_DEFAULT_FADEOUT_STEP),
+SampleChannel::SampleChannel(bool inputMonitor, int bufferSize)
+       : Channel          (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize),
+               mode             (ChannelMode::SINGLE_BASIC),
                wave             (nullptr),
                tracker          (0),
                trackerPreview   (0),
                shift            (0),
-               mode             (G_DEFAULT_CHANMODE),
                qWait              (false),
                inputMonitor     (inputMonitor),
+               boost            (G_DEFAULT_BOOST),
+               pitch            (G_DEFAULT_PITCH),
+               begin            (0),
+               end              (0),
                midiInReadActions(0x0),
-               midiInPitch      (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);
 }
 
 
@@ -83,8 +70,7 @@ SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
 
 SampleChannel::~SampleChannel()
 {
-       if (wave != nullptr)
-               delete wave;
+       delete wave;
        if (rsmp_state != nullptr)
                src_delete(rsmp_state);
 }
@@ -93,34 +79,6 @@ SampleChannel::~SampleChannel()
 /* -------------------------------------------------------------------------- */
 
 
-bool SampleChannel::allocBuffers()
-{
-       if (!Channel::allocBuffers())
-               return false;
-
-       rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr);
-       if (rsmp_state == nullptr) {
-               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for SRC_STATE!\n");
-               return false;
-       }
-
-       if (!pChan.alloc(bufferSize, G_MAX_IO_CHANS)) {
-               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for pChan!\n");
-               return false;
-       }
-
-       if (!vChanPreview.alloc(bufferSize, G_MAX_IO_CHANS)) {
-               gu_log("[SampleChannel::allocBuffers] unable to alloc memory for vChanPreview!\n");
-               return false;
-       }
-
-       return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
 void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
 {
        Channel::copy(src_, pluginMutex);
@@ -131,14 +89,6 @@ void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
        boost           = src->boost;
        mode            = src->mode;
        qWait           = src->qWait;
-       fadeinOn        = src->fadeinOn;
-       fadeinVol       = src->fadeinVol;
-       fadeoutOn       = src->fadeoutOn;
-       fadeoutVol      = src->fadeoutVol;
-       fadeoutTracker  = src->fadeoutTracker;
-       fadeoutStep     = src->fadeoutStep;
-       fadeoutType     = src->fadeoutType;
-       fadeoutEnd      = src->fadeoutEnd;
        setPitch(src->pitch);
 
        if (src->wave)
@@ -149,596 +99,291 @@ void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::clear()
+void SampleChannel::parseEvents(m::mixer::FrameEvents fe)
 {
-       /** TODO - these clear() may be done only if status PLAY | ENDING (if below),
-        * but it would require extra clearPChan calls when samples stop */
-
-       vChan.clear();
-       pChan.clear();
-
-       if (status & (STATUS_PLAY | STATUS_ENDING)) {
-               tracker = fillChan(vChan, tracker, 0);
-               if (fadeoutOn && fadeoutType == XFADE) {
-                       gu_log("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker);
-                       fadeoutTracker = fillChan(pChan, fadeoutTracker, 0);
-               }
-       }
+       sampleChannelProc::parseEvents(this, fe);
+       sampleChannelRec::parseEvents(this, fe);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::calcVolumeEnv(int frame)
+void SampleChannel::prepareBuffer(bool running)
 {
-       /* method: check this frame && next frame, then calculate delta */
-
-       recorder::action* a0 = nullptr;
-       recorder::action* a1 = nullptr;
-       int res;
-
-       /* get this action on frame 'frame'. It's unlikely that the action
-        * is not found. */
+       sampleChannelProc::prepareBuffer(this, running);
+}
 
-       res = recorder::getAction(index, G_ACTION_VOLUME, frame, &a0);
-       if (res == 0)
-               return;
 
-       /* get the action next to this one.
-        * res == -1: a1 not found, this is the last one. Rewind the search
-        * and use action at frame number 0 (actions[0]).
-        * res == -2 G_ACTION_VOLUME not found. This should never happen */
 
-       res = recorder::getNextAction(index, G_ACTION_VOLUME, frame, &a1);
+/* -------------------------------------------------------------------------- */
 
-       if (res == -1)
-               res = recorder::getAction(index, G_ACTION_VOLUME, 0, &a1);
 
-       volume_i = a0->fValue;
-       volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f;
+void SampleChannel::rewindBySeq()
+{
+       sampleChannelProc::rewindBySeq(this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::hardStop(int frame)
+void SampleChannel::start(int localFrame, bool doQuantize, int velocity)
 {
-       if (frame != 0)        
-               vChan.clear(frame); // clear data in range [frame, [end]]
-       status = STATUS_OFF;
-       sendMidiLplay();
-       reset(frame);
+       sampleChannelProc::start(this, localFrame, doQuantize, velocity);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::onBar(int frame)
+void SampleChannel::stop()
 {
-       ///if (mode == LOOP_REPEAT && status == STATUS_PLAY)
-       ///     //setXFade(frame * 2);
-       ///     reset(frame * 2);
-
-       if (mode == LOOP_REPEAT) {
-               if (status == STATUS_PLAY)
-                       //setXFade(frame * 2);
-                       reset(frame);
-       }
-       else
-       if (mode == LOOP_ONCE_BAR) {
-               if (status == STATUS_WAIT) {
-                       status  = STATUS_PLAY;
-                       tracker = fillChan(vChan, tracker, frame);
-                       sendMidiLplay();
-               }
-       }
+       sampleChannelProc::stop(this);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setBegin(int f)
+void SampleChannel::stopBySeq(bool chansStopOnSeqHalt)
 {
-       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;
+       sampleChannelProc::stopBySeq(this, chansStopOnSeqHalt);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setEnd(int f)
+void SampleChannel::kill(int localFrame)
 {
-       if (f < 0)
-               end = begin + wave->getChannels();
-       else
-       if (f >= wave->getSize())
-               end = wave->getSize() - 1;
-       else
-       if (f <= begin)
-               end = begin + 1;
-       else
-               end = f;
+       sampleChannelProc::kill(this, localFrame);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::getBegin() const { return begin; }
-int SampleChannel::getEnd() const   { return end; }
-
-
-/* -------------------------------------------------------------------------- */
+bool SampleChannel::recordStart(bool canQuantize) 
+{
+       return sampleChannelRec::recordStart(this, canQuantize);
+}
 
 
-void SampleChannel::setPitch(float v)
+bool SampleChannel::recordKill()
 {
-       if (v > G_MAX_PITCH)
-               pitch = G_MAX_PITCH;
-       else
-       if (v < 0.1f)
-               pitch = 0.1000f;
-       else 
-               pitch = v;
-
-       rsmp_data.src_ratio = 1/pitch;
+       return sampleChannelRec::recordKill(this);
+}
 
-       /* if status is off don't slide between frequencies */
 
-       if (status & (STATUS_OFF | STATUS_WAIT))
-               src_set_ratio(rsmp_state, 1/pitch);
+void SampleChannel::recordStop()
+{
+       sampleChannelRec::recordStop(this);
 }
 
 
-float SampleChannel::getPitch() const { return pitch; }
+void SampleChannel::recordMute()
+{
+       sampleChannelRec::recordMute(this);
+}
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::rewind()
+void SampleChannel::startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt)
 {
-       /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */
-
-       if (wave != nullptr) {
-               if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY)))
-                       reset(0);  // rewind is user-generated events, always on frame 0
-       }
+       sampleChannelRec::startReadingActions(this, treatRecsAsLoops, recsStopOnChanHalt);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::parseAction(recorder::action* a, int localFrame,
-               int globalFrame, int quantize, bool mixerIsRunning)
+void SampleChannel::stopReadingActions(bool running, bool treatRecsAsLoops, 
+               bool recsStopOnChanHalt)
 {
-       if (readActions == false)
-               return;
-
-       switch (a->type) {
-               case G_ACTION_KEYPRESS:
-                       if (mode & SINGLE_ANY)
-                               start(localFrame, false, quantize, mixerIsRunning, false, false);
-                       break;
-               case G_ACTION_KEYREL:
-                       if (mode & SINGLE_ANY)
-                               stop();
-                       break;
-               case G_ACTION_KILL:
-                       if (mode & SINGLE_ANY)
-                               kill(localFrame);
-                       break;
-               case G_ACTION_MUTEON:
-                       setMute(true);   // internal mute
-                       break;
-               case G_ACTION_MUTEOFF:
-                       unsetMute(true); // internal mute
-                       break;
-               case G_ACTION_VOLUME:
-                       calcVolumeEnv(globalFrame);
-                       break;
-       }
+       sampleChannelRec::stopReadingActions(this, running, treatRecsAsLoops, 
+               recsStopOnChanHalt);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::sum(int frame, bool running)
+void SampleChannel::stopInputRec(int globalFrame)
 {
-       if (wave == nullptr || status & ~(STATUS_PLAY | STATUS_ENDING))
-               return;
-
-       if (frame != frameRewind) {
-
-               /* volume envelope, only if seq is running */
-
-               if (running) {
-                       volume_i += volume_d;
-                       if (volume_i < 0.0f)
-                               volume_i = 0.0f;
-                       else
-                       if (volume_i > 1.0f)
-                               volume_i = 1.0f;
-               }
-
-               /* fadein or fadeout processes. If mute, delete any signal. */
-
-               /** TODO - big issue: fade[in/out]Vol * internal_volume might be a
-                * bad choice: it causes glitches when muting on and off during a
-                * volume envelope. */
-
-               if (mute || mute_i) {
-                       for (int i=0; i<vChan.countChannels(); i++)
-                               vChan[frame][i] = 0.0f;
-               }
-               else
-               if (fadeinOn) {
-                       if (fadeinVol < 1.0f) {
-                               for (int i=0; i<vChan.countChannels(); i++)
-                                       vChan[frame][i] *= fadeinVol * volume_i;
-                               fadeinVol += 0.01f;
-                       }
-                       else {
-                               fadeinOn  = false;
-                               fadeinVol = 0.0f;
-                       }
-               }
-               else
-               if (fadeoutOn) {
-                       if (fadeoutVol > 0.0f) { // fadeout ongoing
-                               if (fadeoutType == XFADE) {
-                                       for (int i=0; i<vChan.countChannels(); i++)
-                                               vChan[frame][i] = pChan[frame][i] * fadeoutVol * volume_i;
-                               }
-                               else {
-                                       for (int i=0; i<vChan.countChannels(); i++)
-                                               vChan[frame][i] *= fadeoutVol * volume_i;
-                               }
-                               fadeoutVol -= fadeoutStep;
-                       }
-                       else {  // fadeout end
-                               fadeoutOn  = false;
-                               fadeoutVol = 1.0f;
-
-                               /* QWait ends with the end of the xfade */
-
-                               if (fadeoutType == XFADE) {
-                                       qWait = false;
-                               }
-                               else {
-                                       if (fadeoutEnd == DO_MUTE)
-                                               mute = true;
-                                       else
-                                       if (fadeoutEnd == DO_MUTE_I)
-                                               mute_i = true;
-                                       else             // DO_STOP
-                                               hardStop(frame);
-                               }
-                       }
-               }
-               else {
-                       for (int i=0; i<vChan.countChannels(); i++)
-                               vChan[frame][i] *= volume_i;
-               }
-       }
-       else { // at this point the sample has reached the end */
-
-               if (mode & (SINGLE_BASIC | SINGLE_PRESS | SINGLE_RETRIG) ||
-                        (mode == SINGLE_ENDLESS && status == STATUS_ENDING)   ||
-                        (mode & LOOP_ANY && !running))     // stop loops when the seq is off
-               {
-                       status = STATUS_OFF;
-                       sendMidiLplay();
-               }
-
-               /* LOOP_ONCE or LOOP_ONCE_BAR: if ending (i.e. the user requested their
-                * termination), kill 'em. Let them wait otherwise. But don't put back in
-                * wait mode those already stopped by the conditionals above. */
-
-               if (mode & (LOOP_ONCE | LOOP_ONCE_BAR)) {
-                       if (status == STATUS_ENDING)
-                               status = STATUS_OFF;
-                       else
-                       if (status != STATUS_OFF)
-                               status = STATUS_WAIT;
-               }
-
-               /* Check for end of samples. SINGLE_ENDLESS runs forever unless it's in 
-               ENDING mode. */
-
-               reset(frame);
-       }
+       sampleChannelProc::stopInputRec(this, globalFrame);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::onZero(int frame, bool recsStopOnChanHalt)
+void SampleChannel::setMute(bool value, EventType eventType)
 {
-       if (wave == nullptr)
-               return;
-
-       if (mode & LOOP_ANY) {
-
-               /* do a crossfade if the sample is playing. Regular chanReset
-                * instead if it's muted, otherwise a click occurs */
-
-               if (status == STATUS_PLAY) {
-                       /*
-                       if (mute || mute_i)
-                               reset(frame * 2);
-                       else
-                               setXFade(frame * 2);
-                       */
-                       reset(frame);
-               }
-               else
-               if (status == STATUS_ENDING)
-                       hardStop(frame);
-       }
-
-       if (status == STATUS_WAIT) { /// FIXME - should be inside previous if!
-               status  = STATUS_PLAY;
-               sendMidiLplay();
-               tracker = fillChan(vChan, tracker, frame);
-       }
-
-       if (recStatus == REC_ENDING) {
-               recStatus = REC_STOPPED;
-               setReadActions(false, recsStopOnChanHalt);  // rec stop
-       }
-       else
-       if (recStatus == REC_WAITING) {
-               recStatus = REC_READING;
-               setReadActions(true, recsStopOnChanHalt);   // rec start
-       }
+       sampleChannelProc::setMute(this, value, eventType);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::quantize(int index, int localFrame, int globalFrame)
+void SampleChannel::process(giada::m::AudioBuffer& out, 
+       const giada::m::AudioBuffer& in, bool audible, bool running)
 {
-       /* skip if LOOP_ANY or not in quantizer-wait mode */
-
-       if ((mode & LOOP_ANY) || !qWait)
-               return;
-
-       /* no fadeout if the sample starts for the first time (from a
-        * STATUS_OFF), it would be meaningless. */
-
-       if (status == STATUS_OFF) {
-               status  = STATUS_PLAY;
-               sendMidiLplay();
-               qWait   = false;
-               tracker = fillChan(vChan, tracker, localFrame); /// FIXME: ???
-       }
-       else
-               //setXFade(localFrame * 2);
-               reset(localFrame);
-
-       /* this is the moment in which we record the keypress, if the
-        * quantizer is on. SINGLE_PRESS needs overdub */
-
-       if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
-               if (mode == SINGLE_PRESS) {
-                       recorder::startOverdub(index, G_ACTION_KEYS, globalFrame, 
-                               kernelAudio::getRealBufSize());
-      readActions = false;   // don't read actions while overdubbing
-    }
-               else
-                       recorder::rec(index, G_ACTION_KEYPRESS, globalFrame);
-    hasActions = true;
-       }
+       sampleChannelProc::process(this, out, in, audible, running);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::getPosition()
+void SampleChannel::readPatch(const string& basePath, int i)
 {
-       if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...)
-               return tracker - begin;
-       else
-               return -1;
+       Channel::readPatch("", i);
+       channelManager::readPatch(this, basePath, i);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setMute(bool internal)
+void SampleChannel::writePatch(int i, bool isProject)
 {
-       if (internal) {
-
-               /* global mute is on? don't waste time with fadeout, just mute it
-                * internally */
-
-               if (mute)
-                       mute_i = true;
-               else {
-                       if (isPlaying())
-                               setFadeOut(DO_MUTE_I);
-                       else
-                               mute_i = true;
-               }
-       }
-       else {
-
-               /* internal mute is on? don't waste time with fadeout, just mute it
-                * globally */
-
-               if (mute_i)
-                       mute = true;
-               else {
-
-                       /* sample in play? fadeout needed. Else, just mute it globally */
-
-                       if (isPlaying())
-                               setFadeOut(DO_MUTE);
-                       else
-                               mute = true;
-               }
-       }
-
-       sendMidiLmute();
+       Channel::writePatch(i, isProject);
+       channelManager::writePatch(this, isProject, i);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::unsetMute(bool internal)
+void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
 {
-       if (internal) {
-               if (mute)
-                       mute_i = false;
-               else {
-                       if (isPlaying())
-                               setFadeIn(internal);
-                       else
-                               mute_i = false;
-               }
-       }
-       else {
-               if (mute_i)
-                       mute = false;
-               else {
-                       if (isPlaying())
-                               setFadeIn(internal);
-                       else
-                               mute = false;
-               }
-       }
-
-       sendMidiLmute();
+       sampleChannelRec::setReadActions(this, v, recsStopOnChanHalt);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-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;
-}
+bool SampleChannel::hasLogicalData() const
+{ 
+       return wave != nullptr && wave->isLogical();
+};
 
 
-float SampleChannel::getBoost() const
+bool SampleChannel::hasEditedData() const
+{ 
+       return wave != nullptr && wave->isEdited();
+};
+
+
+bool SampleChannel::hasData() const
 {
-       return boost;
+       return wave != nullptr;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::calcFadeoutStep()
+void SampleChannel::setBegin(int f)
 {
-       if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP))
-               fadeoutStep = ceil((end - tracker) / volume); /// or volume_i ???
+       if (f < 0)
+               begin = 0;
+       else
+       if (f > wave->getSize())
+               begin = wave->getSize();
        else
-               fadeoutStep = G_DEFAULT_FADEOUT_STEP;
+       if (f >= end)
+               begin = end - 1;
+       else
+               begin = f;
+
+       tracker = begin;
+       trackerPreview = begin;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setReadActions(bool v, bool killOnFalse)
+void SampleChannel::setEnd(int f)
 {
-       readActions = v;
-       if (!readActions && killOnFalse)
-               kill(0);  /// FIXME - wrong frame value
+       if (f >= wave->getSize())
+               end = wave->getSize() - 1;
+       else
+       if (f <= begin)
+               end = begin + 1;
+       else
+               end = f;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setFadeIn(bool internal)
-{
-       if (internal) mute_i = false;  // remove mute before fading in
-       else          mute   = false;
-       fadeinOn  = true;
-       fadeinVol = 0.0f;
-}
+int SampleChannel::getBegin() const { return begin; }
+int SampleChannel::getEnd() const   { return end; }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setFadeOut(int actionPostFadeout)
+void SampleChannel::setPitch(float v)
 {
-       calcFadeoutStep();
-       fadeoutOn   = true;
-       fadeoutVol  = 1.0f;
-       fadeoutType = FADEOUT;
-       fadeoutEnd      = actionPostFadeout;
+       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; }
+
+
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::setXFade(int frame)
+int SampleChannel::getPosition() const
 {
-       gu_log("[xFade] frame=%d tracker=%d\n", frame, tracker);
-
-       calcFadeoutStep();
-       fadeoutOn      = true;
-       fadeoutVol     = 1.0f;
-       fadeoutType    = XFADE;
-       fadeoutTracker = fillChan(pChan, tracker, 0, false);
-       reset(frame);
+       if (status != ChannelStatus::EMPTY   && 
+           status != ChannelStatus::MISSING && 
+           status != ChannelStatus::OFF)
+               return tracker - begin;
+       else
+               return -1;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::reset(int frame)
+void SampleChannel::setBoost(float v)
 {
-       //fadeoutTracker = tracker;   // store old frame number for xfade
-       tracker = begin;
-       mute_i  = false;
-       qWait   = false;  // Was in qWait mode? Reset occured, no more qWait now.
-
-       /* On reset, if frame > 0 and in play, fill again pChan to create something 
-       like this:
+       if (v > G_MAX_BOOST_DB)
+               boost = G_MAX_BOOST_DB;
+       else 
+       if (v < 0.0f)
+               boost = 0.0f;
+       else
+               boost = v;
+}
 
-               |abcdefabcdefab*abcdefabcde|
-               [old data-----]*[new data--] */
 
-       if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING))
-               tracker = fillChan(vChan, tracker, frame);
+float SampleChannel::getBoost() const
+{
+       return boost;
 }
 
 
@@ -747,18 +392,16 @@ void SampleChannel::reset(int frame)
 
 void SampleChannel::empty()
 {
-       status = STATUS_OFF;
-       if (wave != nullptr) {
-               delete wave;
-               wave = nullptr;
-       }
-  begin   = 0;
-  end     = 0;
-  tracker = 0;
-       status  = STATUS_EMPTY;
-  volume  = G_DEFAULT_VOL;
-  boost   = G_DEFAULT_BOOST;
-       sendMidiLplay();
+       status     = ChannelStatus::EMPTY;
+  begin      = 0;
+  end        = 0;
+  tracker    = 0;
+  volume     = G_DEFAULT_VOL;
+  boost      = G_DEFAULT_BOOST;
+  hasActions = false;
+       delete wave;
+       wave = nullptr;
+       sendMidiLstatus();
 }
 
 
@@ -767,290 +410,86 @@ void SampleChannel::empty()
 
 void SampleChannel::pushWave(Wave* w)
 {
-       sendMidiLplay();     // FIXME - why here?!?!
+       status = ChannelStatus::OFF;
        wave   = w;
-       status = STATUS_OFF;
        begin  = 0;
        end    = wave->getSize() - 1;
        name   = wave->getBasename();
+       sendMidiLstatus();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in)
-{
-       assert(out.countSamples() == vChan.countSamples());
-       assert(in.countSamples()  == vChan.countSamples());
-
-       /* If armed and inbuffer is not nullptr (i.e. input device available) and
-  input monitor is on, copy input buffer to vChan: this enables the input
-  monitoring. The vChan will be overwritten later by pluginHost::processStack,
-  so that you would record "clean" audio (i.e. not plugin-processed). */
-
-       if (armed && in.isAllocd() && inputMonitor)
-               for (int i=0; i<vChan.countFrames(); i++)
-                       for (int j=0; j<vChan.countChannels(); j++)
-                               vChan[i][j] += in[i][j];   // add, don't overwrite
-
-#ifdef WITH_VST
-       pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
-#endif
-
-               for (int i=0; i<out.countFrames(); i++)
-                       for (int j=0; j<out.countChannels(); j++)
-                               out[i][j] += vChan[i][j] * volume * calcPanning(j) * boost;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::preview(giada::m::AudioBuffer& out)
+bool SampleChannel::canInputRec()
 {
-       if (previewMode == G_PREVIEW_NONE)
-               return;
-
-       vChanPreview.clear();
-
-       /* If the tracker exceedes the end point and preview is looped, split the 
-       rendering as in SampleChannel::reset(). */
-
-       if (trackerPreview + bufferSize >= end) {
-               int offset = end - trackerPreview;
-               trackerPreview = fillChan(vChanPreview, trackerPreview, 0, false);
-               trackerPreview = begin;
-               if (previewMode == G_PREVIEW_LOOP)
-                       trackerPreview = fillChan(vChanPreview, begin, offset, false);
-               else
-               if (previewMode == G_PREVIEW_NORMAL) {
-                       previewMode = G_PREVIEW_NONE;
-                       if (onPreviewEnd)
-                               onPreviewEnd();
-               }
-       }
-       else
-               trackerPreview = fillChan(vChanPreview, trackerPreview, 0, false);
-
-       for (int i=0; i<out.countFrames(); i++)
-               for (int j=0; j<out.countChannels(); j++)
-                       out[i][j] += vChanPreview[i][j] * volume * calcPanning(j) * boost;
+       return wave == nullptr && armed;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::kill(int frame)
+int SampleChannel::fillBuffer(giada::m::AudioBuffer& dest, int start, int offset)
 {
-       if (wave != nullptr && status != STATUS_OFF) {
-               if (mute || mute_i || (status == STATUS_WAIT && mode & LOOP_ANY))
-                       hardStop(frame);
-               else
-                       setFadeOut(DO_STOP);
-       }
+       if (pitch == 1.0) return fillBufferCopy(dest, start, offset);
+       else              return fillBufferResampled(dest, start, offset);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::stopBySeq(bool chansStopOnSeqHalt)
+int SampleChannel::fillBufferResampled(giada::m::AudioBuffer& dest, int start, int offset)
 {
-  /* Loop-mode samples in wait status get stopped right away. */
-
-       if (mode & LOOP_ANY && status == STATUS_WAIT) {
-               status = STATUS_OFF;
-    return;
-  }
-
-  /* When to kill samples on StopSeq:
-   *  - when chansStopOnSeqHalt == true (run the sample to end otherwise)
-   *  - when a channel has recs in play (1)
-   *
-   * Always kill at frame=0, this is a user-generated event.
-   *
-   * (1) a channel has recs in play when:
-   *  - Recorder has events for that channel
-   *  - G_Mixer has at least one sample in play
-   *  - Recorder's channel is active (altrimenti può capitare che si stoppino i
-   *    sample suonati manualmente in un canale con rec disattivate) */
-
-       if (chansStopOnSeqHalt) {
-    if ((mode & LOOP_ANY) || (hasActions && readActions && status == STATUS_PLAY))
-      kill(0);
-  }
-}
-
-
-/* -------------------------------------------------------------------------- */
+       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);
 
-void SampleChannel::stop()
-{
-       if (mode == SINGLE_PRESS && status == STATUS_PLAY) {
-               if (mute || mute_i)
-                       hardStop(0);  /// FIXME - wrong frame value
-               else
-                       setFadeOut(DO_STOP);
-       }
-       else  // stop a SINGLE_PRESS immediately, if the quantizer is on
-       if (mode == SINGLE_PRESS && qWait == true)
-               qWait = false;
+       return rsmp_data.input_frames_used; // Returns used frames
 }
 
-
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::readPatch(const string& basePath, int i)
+int SampleChannel::fillBufferCopy(giada::m::AudioBuffer& dest, int start, int offset)
 {
-       Channel::readPatch("", i);
-       channelManager::readPatch(this, basePath, i);
-}
-
-
-/* -------------------------------------------------------------------------- */
+       int used = dest.countFrames() - offset;
+       if (used + start > wave->getSize())
+               used = wave->getSize() - start;
 
+       dest.copyData(wave->getFrame(start), used, offset);
 
-bool SampleChannel::canInputRec()
-{
-       return wave == nullptr && armed;
+       return used;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void SampleChannel::start(int frame, bool doQuantize, int quantize,
-               bool mixerIsRunning, bool forceStart, bool isUserGenerated)
+bool SampleChannel::isAnyLoopMode() const
 {
-       switch (status) {
-               case STATUS_EMPTY:
-               case STATUS_MISSING:
-               case STATUS_WRONG:
-               {
-                       return;
-               }
-               case STATUS_OFF:
-               {
-                       if (mode & LOOP_ANY) {
-        if (forceStart) {
-          status = STATUS_PLAY;
-          tracker = frame;
-        }
-        else
-                                   status = STATUS_WAIT;
-                               sendMidiLplay();
-                       }
-                       else {
-                               if (quantize > 0 && mixerIsRunning && doQuantize)
-                                       qWait = true;
-                               else {
-                                       status = STATUS_PLAY;
-                                       sendMidiLplay();
-
-                                       /* Do fillChan only if this is not a user-generated event (i.e. is an
-                                       action read by Mixer). Otherwise clear() will take take of calling
-                                       fillChan on the next cycle. */
-
-                                       if (!isUserGenerated)
-                                               tracker = fillChan(vChan, tracker, frame);
-                               }
-                       }
-                       break;
-               }
-               case STATUS_PLAY:
-               {
-                       if (mode == SINGLE_BASIC)
-                               setFadeOut(DO_STOP);
-                       else
-                       if (mode == SINGLE_RETRIG) {
-                               if (quantize > 0 && mixerIsRunning && doQuantize)
-                                       qWait = true;
-                               else
-                                       reset(frame);
-                       }
-                       else
-                       if (mode & (LOOP_ANY | SINGLE_ENDLESS)) {
-                               status = STATUS_ENDING;
-                               sendMidiLplay();
-                       }
-                       break;
-               }
-               case STATUS_WAIT:
-               {
-                       status = STATUS_OFF;
-                       sendMidiLplay();
-                       break;
-               }
-               case STATUS_ENDING:
-               {
-                       status = STATUS_PLAY;
-                       sendMidiLplay();
-                       break;
-               }
-       }
+       return mode == ChannelMode::LOOP_BASIC || mode == ChannelMode::LOOP_ONCE || 
+              mode == ChannelMode::LOOP_REPEAT || mode == ChannelMode::LOOP_ONCE_BAR;
 }
 
 
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::writePatch(int i, bool isProject)
+bool SampleChannel::isAnySingleMode() const
 {
-       Channel::writePatch(i, isProject);
-       channelManager::writePatch(this, isProject, i);
+       return !isAnyLoopMode();
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int SampleChannel::fillChan(giada::m::AudioBuffer& dest, int start, int offset, bool rewind)
+bool SampleChannel::isOnLastFrame() const
 {
-       int position;  // return value: the new position
-
-       if (pitch == 1.0f) {
-
-               /* Fill 'dest' buffer with a chunk of data of size 'buffersize - offset'. 
-               Case 1: there is enough data in Wave. Case2: not enough data or Wave is 
-               smaller than the buffer. */
-
-               int chunkSize = bufferSize - offset;
-
-               if (start + chunkSize <= end) {
-                       position = start + chunkSize;
-                       if (rewind)
-                               frameRewind = -1;
-               }
-               else {
-                       chunkSize = end - start;
-                       position  = end;
-                       if (rewind)
-                               frameRewind = chunkSize + offset;
-               }
-               dest.copyData(wave->getFrame(start), chunkSize, offset);
-       }
-       else {
-               rsmp_data.data_in       = wave->getFrame(start);    // source data
-               rsmp_data.input_frames  = end - start;              // how many readable bytes
-               rsmp_data.data_out      = dest[offset];             // destination (processed data)
-               rsmp_data.output_frames = bufferSize - offset;      // how many bytes to process
-               rsmp_data.end_of_input  = false;
-
-               src_process(rsmp_state, &rsmp_data);
-
-               position = start + rsmp_data.input_frames_used;     // position goes forward of frames_used (i.e. read from wave)
-
-               if (rewind) {
-                       int gen = rsmp_data.output_frames_gen;            // frames generated by this call
-                       if (gen == bufferSize - offset)
-                               frameRewind = -1;
-                       else
-                               frameRewind = gen + offset;
-               }
-       }
-       return position;
-}
+       return tracker >= end;
+}
\ No newline at end of file
index 11af92e91db3c0072293b26260f90924fb01d00c..87aca99d73bacde4962a14f364b4bd7a8889da86 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <functional>
 #include <samplerate.h>
+#include "types.h"
 #include "channel.h"
 
 
@@ -40,172 +41,114 @@ class Wave;
 
 class SampleChannel : public Channel
 {
-private:
-
-       /* fillChan
-       Fills 'dest' buffer at point 'offset' with wave data taken from 'start'. If 
-       rewind=false don't rewind internal tracker. Returns new sample position, 
-       in frames. It resamples data if pitch != 1.0f. */
-
-       int fillChan(giada::m::AudioBuffer& dest, int start, int offset, bool rewind=true);
-
-       /* calcFadeoutStep
-       How many frames are left before the end of the sample? Is there enough room 
-       for a complete fadeout? Should we shorten it? */
-
-       void calcFadeoutStep();
-
-       /* calcVolumeEnv
-       Computes any changes in volume done via envelope tool. */
-
-       void calcVolumeEnv(int frame);
-
-       /* reset
-       Rewinds tracker to the beginning of the sample. */
-
-       void reset(int frame);
-
-       /* fade methods
-       Prepare channel for fade, mixer will take care of the process during master 
-       play. */
-
-       void setFadeIn(bool internal);
-       void setFadeOut(int actionPostFadeout);
-       void setXFade(int frame);
-
-       /* rsmp_state, rsmp_data
-       Structs from libsamplerate. */
-
-       SRC_STATE* rsmp_state;
-       SRC_DATA   rsmp_data;
-
-       /* pChan, vChanPreview
-       Extra virtual channel for processing resampled data and for audio preview. */
-
-       giada::m::AudioBuffer pChan;
-       giada::m::AudioBuffer vChanPreview;
-
-       /* frameRewind
-       Exact frame in which a rewind occurs. */
-
-       int frameRewind;
-
-       /* begin, end
-       Begin/end point to read wave data from/to. */
-
-       int begin;
-       int end;
-       
-       float pitch;
-       float boost;
-
-       bool  fadeinOn;
-       float fadeinVol;
-       bool  fadeoutOn;
-       float fadeoutVol;      // fadeout volume
-       int   fadeoutTracker;  // tracker fadeout, xfade only
-       float fadeoutStep;     // fadeout decrease
-  int   fadeoutType;     // xfade or fadeout
-  int          fadeoutEnd;      // what to do when fadeout ends
-
 public:
 
-       SampleChannel(int bufferSize, bool inputMonitor);
+       SampleChannel(bool inputMonitor, int bufferSize);
        ~SampleChannel();
 
        void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
-       void clear() override;
-       void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override;
-       void preview(giada::m::AudioBuffer& out) override;
-       void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
-               bool forceStart, bool isUserGenerated) override;
+       void prepareBuffer(bool running) override;
+       void parseEvents(giada::m::mixer::FrameEvents fe) override;
+       void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in,
+               bool audible, bool running) override;
+       void readPatch(const std::string& basePath, int i) 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 recordMute() override;
+       void setMute(bool value, giada::EventType eventType) 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 stop() override;
-       void rewind() override;
-       void setMute(bool internal) override;
-       void unsetMute(bool internal) override;
-  void readPatch(const std::string& basePath, int i) override;
-       void writePatch(int i, bool isProject) override;
-       void quantize(int index, int localFrame, int globalFrame) override;
-       void onZero(int frame, bool recsStopOnChanHalt) override;
-       void onBar(int frame) override;
-       void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame,
-                       int quantize, bool mixerIsRunning) override;
+       void rewindBySeq() override;
        bool canInputRec() override;
-       bool allocBuffers() override;
+       void stopInputRec(int globalFrame) 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;
-
-       /* pushWave
-       Adds a new wave to an existing channel. */
-
-       void pushWave(Wave* w);
+       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();
+       int getPosition() const;
 
-       /* sum
-       Adds sample frames to virtual channel. Frame = processed frame in Mixer. 
-       Running == is Mixer in play? */
+       /* 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. */
 
-       void sum(int frame, bool running);
+       int fillBuffer(giada::m::AudioBuffer& dest, int start, int offset);
+
+       /* pushWave
+       Adds a new wave to an existing channel. */
+
+       void pushWave(Wave* w);
 
        void setPitch(float v);
        void setBegin(int f);
        void setEnd(int f);
        void setBoost(float v);
 
-       /* hardStop
-       Stops the channel immediately, no further checks. */
-
-       void hardStop(int frame);
-
-       /* setReadActions
-       If enabled (v == true), recorder will read actions from this channel. If 
-       killOnFalse == true and disabled, will also kill the channel. */
-
-       void setReadActions(bool v, bool killOnFalse);
+       void setReadActions(bool v, bool recsStopOnChanHalt);
 
        /* onPreviewEnd
        A callback fired when audio preview ends. */
 
        std::function<void()> onPreviewEnd;
 
+       /* bufferPreview
+       Extra buffer for audio preview. */
+
+       giada::m::AudioBuffer bufferPreview;
+       
+       giada::ChannelMode mode;
+       
        Wave* wave;
        int   tracker;         // chan position
        int   trackerPreview;  // chan position for audio preview
        int   shift;
-       int   mode;            // mode: see const.h
        bool  qWait;           // quantizer wait
-  bool  inputMonitor;
+       bool  inputMonitor;  
+       float boost;
+       float pitch;
 
-       /* midi stuff */
+       /* begin, end
+       Begin/end point to read wave data from/to. */
 
-  bool     midiInVeloAsVol;
-  uint32_t midiInReadActions;
-  uint32_t midiInPitch;
+       int begin;
+       int end;
 
-       /* const - what to do when a fadeout ends */
+       /* midi stuff */
+
+       bool     midiInVeloAsVol;
+       uint32_t midiInReadActions;
+       uint32_t midiInPitch;
+       
+private:
 
-       enum {
-               DO_STOP   = 0x01,
-               DO_MUTE   = 0x02,
-               DO_MUTE_I = 0x04
-       };
+       /* rsmp_state, rsmp_data
+       Structs from libsamplerate. */
 
-       /*  const - fade types */
+       SRC_STATE* rsmp_state;
+       SRC_DATA   rsmp_data;
 
-       enum {
-               FADEOUT = 0x01,
-               XFADE   = 0x02
-       };
+       int fillBufferResampled(giada::m::AudioBuffer& dest, int start, int offset);
+       int fillBufferCopy     (giada::m::AudioBuffer& dest, int start, int offset);
 };
 
 #endif
diff --git a/src/core/sampleChannelProc.cpp b/src/core/sampleChannelProc.cpp
new file mode 100644 (file)
index 0000000..546adf7
--- /dev/null
@@ -0,0 +1,468 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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 "pluginHost.h"
+#include "sampleChannel.h"
+#include "sampleChannelProc.h"
+#include "clock.h"
+
+
+namespace giada {
+namespace m {
+namespace sampleChannelProc
+{
+namespace
+{
+void rewind_(SampleChannel* ch, int localFrame)
+{
+       ch->tracker = ch->begin;
+       ch->mute_i  = false;
+       ch->qWait   = false;  // Was in qWait mode? Reset occured, no more qWait 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->qWait || !quantoPassed)
+               return;
+
+       switch (ch->status) {
+               case ChannelStatus::OFF:
+                       ch->status   = ChannelStatus::PLAY;
+                       ch->qWait    = false;
+                       ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame);
+                       ch->sendMidiLstatus();
+                       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());
+       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::CHANNEL, ch);
+#endif
+
+       for (int i=0; i<out.countFrames(); i++) {
+               if (running)
+                       ch->calcVolumeEnvelope();
+               if (!ch->mute && !ch->mute_i)
+                       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->qWait)
+                               ch->qWait = 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, EventType eventType)
+{
+       eventType == EventType::MANUAL ? ch->mute = value : ch->mute_i = value;
+       ch->sendMidiLmute();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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<float>(velocity, 0.0f, 127.0f, 0.0f, 1.0f);         
+       }
+
+       switch (ch->status)     {
+               case ChannelStatus::OFF:
+                       if (ch->isAnyLoopMode()) {
+                               ch->status = ChannelStatus::WAIT;
+                               ch->sendMidiLstatus();
+                       }
+                       else {
+                               if (doQuantize)
+                                       ch->qWait = true;
+                               else {
+                                       ch->status = ChannelStatus::PLAY;
+                                       ch->sendMidiLstatus();
+                               }
+                       }
+                       break;
+
+               case ChannelStatus::PLAY:
+                       if (ch->mode == ChannelMode::SINGLE_RETRIG) {
+                               if (doQuantize)
+                                       ch->qWait = 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)
+{
+       if (!ch->hasData())
+               return;
+       ch->buffer.clear();
+       if (ch->isPlaying()) {
+               int framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, 0);
+               ch->tracker += framesUsed;
+               if (ch->isOnLastFrame())
+                       onLastFrame_(ch, framesUsed * (1 / ch->pitch), 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);
+}
+}}};
\ No newline at end of file
diff --git a/src/core/sampleChannelProc.h b/src/core/sampleChannelProc.h
new file mode 100644 (file)
index 0000000..341cbfd
--- /dev/null
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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"
+
+
+class SampleChannel;
+
+
+namespace giada {
+namespace m {
+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, giada::m::AudioBuffer& out, 
+       const giada::m::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, EventType eventType);
+}}};
+
+
+#endif
\ No newline at end of file
diff --git a/src/core/sampleChannelRec.cpp b/src/core/sampleChannelRec.cpp
new file mode 100644 (file)
index 0000000..ec28bb9
--- /dev/null
@@ -0,0 +1,327 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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 "const.h"
+#include "conf.h"
+#include "clock.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)
+{
+       return recorder::canRec(ch, clock::isRunning(), mixer::recording);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* calcVolumeEnv
+Computes any changes in volume done via envelope tool. */
+
+void calcVolumeEnv_(SampleChannel* ch, int globalFrame)
+{
+       /* method: check this frame && next frame, then calculate delta */
+
+       recorder::action* a0 = nullptr;
+       recorder::action* a1 = nullptr;
+       int res;
+
+       /* get this action on frame 'frame'. It's unlikely that the action
+        * is not found. */
+
+       res = recorder::getAction(ch->index, G_ACTION_VOLUME, globalFrame, &a0);
+       if (res == 0)
+               return;
+
+       /* get the action next to this one.
+        * res == -1: a1 not found, this is the last one. Rewind the search
+        * and use action at frame number 0 (actions[0]).
+        * res == -2 G_ACTION_VOLUME not found. This should never happen */
+
+       res = recorder::getNextAction(ch->index, G_ACTION_VOLUME, globalFrame, &a1);
+
+       if (res == -1)
+               res = recorder::getAction(ch->index, G_ACTION_VOLUME, 0, &a1);
+
+       ch->volume_i = a0->fValue;
+       ch->volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parseAction_(SampleChannel* ch, const recorder::action* a, int localFrame, 
+       int globalFrame)
+{
+       if (!ch->readActions)
+               return;
+
+       switch (a->type) {
+               case G_ACTION_KEYPRESS:
+                       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 G_ACTION_KEYREL:
+                       if (ch->isAnySingleMode())
+                               ch->stop();
+                       break;
+               case G_ACTION_KILL:
+                       if (ch->isAnySingleMode())
+                               ch->kill(localFrame);
+                       break;
+               case G_ACTION_MUTEON:
+                       ch->setMute(true, EventType::AUTO);
+                       break;
+               case G_ACTION_MUTEOFF:
+                       ch->setMute(false, EventType::AUTO);
+                       break;
+               case G_ACTION_VOLUME:
+                       calcVolumeEnv_(ch, globalFrame);
+                       break;
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordKeyPressAction_(SampleChannel* ch)
+{
+       if (!recorderCanRec_(ch))
+               return;
+
+       /* SINGLE_PRESS mode needs overdub. Also, disable reading actions while 
+       overdubbing. */
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
+               recorder::startOverdub(ch->index, G_ACTION_KEYS, clock::getCurrentFrame(), 
+                       kernelAudio::getRealBufSize());
+               ch->readActions = false;
+       }
+       else
+               recorder::rec(ch->index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
+       ch->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void quantize_(SampleChannel* ch, bool quantoPassed)
+{
+       /* Skip if LOOP_ANY or not in quantizer-wait mode. Otherwise the quantize wait 
+       has expired: record the keypress.  */
+
+       if (ch->isAnyLoopMode() || !ch->qWait || !quantoPassed)
+               return;
+       recordKeyPressAction_(ch);
+}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
+{
+       quantize_(ch, fe.quantoPassed);
+       if (fe.onFirstBeat)
+               onFirstBeat_(ch, conf::recsStopOnChanHalt);
+       for (const recorder::action* action : fe.actions)
+               if (action->chan == ch->index)
+                       parseAction_(ch, action, fe.frameLocal, fe.frameGlobal);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordMute(SampleChannel* ch)
+{
+       if (recorderCanRec_(ch)) {
+               if (!ch->mute) {
+                       recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(),
+                               kernelAudio::getRealBufSize());
+                       ch->readActions = false;   // don't read actions while overdubbing
+               }
+               else
+                recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
+                       &mixer::mutex);
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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);
+
+               /* Why return here? You record an action and then you call ch->start: 
+               Mixer, which is on another thread, reads your newly recorded action if you 
+               have readActions == true, and then ch->start kicks in right after it.
+               The result: Mixer plays the channel (due to the new action) but the code
+               in the switch in start() kills it right away (because the sample is playing). 
+               Fix: start channel only if you are not recording anything, i.e. let 
+               Mixer play it. */
+
+               if (ch->readActions)
+                       return false;
+       }
+       return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool recordKill(SampleChannel* ch)
+{
+       /* Don't record G_ACTION_KILL actions for LOOP channels. */
+       if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) {
+               recorder::rec(ch->index, G_ACTION_KILL, clock::getCurrentFrame());
+               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) {
+               recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
+                       &mixer::mutex);
+       }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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
new file mode 100644 (file)
index 0000000..6fe5d48
--- /dev/null
@@ -0,0 +1,73 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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
+
+
+class SampleChannel;
+
+
+namespace giada {
+namespace m {
+namespace sampleChannelRec
+{
+void parseEvents(SampleChannel* ch, mixer::FrameEvents fe);
+
+/* recordStart
+Records a G_ACTION_KEYPRESS if capable of. Returns true if a start() call can
+be performed. */
+
+bool recordStart(SampleChannel* ch, bool doQuantize);
+
+/* recordKill
+Records a G_ACTION_KILL 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);
+
+void recordMute(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/types.h b/src/core/types.h
new file mode 100644 (file)
index 0000000..d83fd3c
--- /dev/null
@@ -0,0 +1,58 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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_TYPES_H
+#define G_TYPES_H
+
+
+namespace giada
+{
+enum class ChannelType : int 
+{
+       SAMPLE = 1, MIDI
+};
+
+
+enum class ChannelStatus : int
+{
+       ENDING = 1, WAIT, PLAY, OFF, EMPTY, MISSING, WRONG  
+};
+
+
+enum class ChannelMode : int
+{
+       LOOP_BASIC = 1, LOOP_ONCE, LOOP_REPEAT, LOOP_ONCE_BAR,
+       SINGLE_BASIC, SINGLE_PRESS, SINGLE_RETRIG, SINGLE_ENDLESS
+};
+
+
+enum class PreviewMode : int { NONE = 0, NORMAL, LOOP };
+enum class EventType : int { AUTO = 0, MANUAL };
+};
+
+
+#endif
\ No newline at end of file
index 5c9519559bc6ab7c3052e9e34a67ef3ea8a5f340..95123080fc2f5722f1ddbac513d867b9f9e21c21 100644 (file)
@@ -75,14 +75,12 @@ Wave::Wave(const Wave& other)
 /* -------------------------------------------------------------------------- */
 
 
-bool Wave::alloc(int size, int channels, int rate, int bits, const std::string& path)
+void Wave::alloc(int size, int channels, int rate, int bits, const std::string& path)
 {
-       if (!buffer.alloc(size, channels))
-               return false;
+       buffer.alloc(size, channels);
        m_rate = rate;
        m_bits = bits;
        m_path = path;
-       return true;
 }
 
 
index ecdcc4ab8236f057c4e3a2f7429108573c3670a2..5ea0e71bf92f673aba52b7e359d11429e0200d83 100644 (file)
@@ -84,7 +84,7 @@ public:
 
        void copyData(float* data, int frames, int offset=0);
 
-       bool alloc(int size, int channels, int rate, int bits, const std::string& path);
+       void alloc(int size, int channels, int rate, int bits, const std::string& path);
 
 private:
 
index 91549393c19f0a174644b2fb489e3d4a6048ad88..d3f147eaafaf4da5ceb010f227db7030e369204a 100644 (file)
@@ -109,10 +109,7 @@ int monoToStereo(Wave& w)
                return G_RES_OK;
 
        AudioBuffer newData;
-       if (!newData.alloc(w.getSize(), G_MAX_IO_CHANS)) {
-               gu_log("[wfx::monoToStereo] unable to allocate memory!\n");
-               return G_RES_ERR_MEMORY;
-       }
+       newData.alloc(w.getSize(), G_MAX_IO_CHANS);
 
        for (int i=0; i<newData.countFrames(); i++)
                for (int j=0; j<newData.countChannels(); j++)
@@ -154,10 +151,7 @@ int cut(Wave& w, int a, int b)
        int newSize = w.getSize() - (b - a);
 
        AudioBuffer newData;
-       if (!newData.alloc(newSize, w.getChannels())) {
-               gu_log("[wfx::cut] unable to allocate memory!\n");
-               return G_RES_ERR_MEMORY;
-       }
+       newData.alloc(newSize, w.getChannels());
 
        gu_log("[wfx::cut] cutting from %d to %d\n", a, b);
 
@@ -187,10 +181,7 @@ int trim(Wave& w, int a, int b)
        int newSize = b - a;
 
        AudioBuffer newData;
-       if (!newData.alloc(newSize, w.getChannels())) {
-               gu_log("[wfx::trim] unable to allocate memory!\n");
-               return G_RES_ERR_MEMORY;
-       }
+       newData.alloc(newSize, w.getChannels());
 
        gu_log("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
 
@@ -213,10 +204,7 @@ int paste(const Wave& src, Wave& des, int a)
        assert(src.getChannels() == des.getChannels());
 
        AudioBuffer newData;
-       if (!newData.alloc(src.getSize() + des.getSize(), des.getChannels())) {
-               gu_log("[wfx::paste] unable to allocate memory!\n");
-               return G_RES_ERR_MEMORY;
-       }
+       newData.alloc(src.getSize() + des.getSize(), des.getChannels());
 
        /* |---original data---|///paste data///|---original data---|
                 des[0, a)      src[0, src.size)   des[a, des.size)     */
index b7905e65c8bf9c8ba1593a3067b003d9ec961fa3..c7e48e0b2f07ec7b4105d7c0d257ba7ba5e8096c 100644 (file)
@@ -95,11 +95,7 @@ int create(const string& path, Wave** out)
        }
 
        Wave* wave = new Wave();
-       if (!wave->alloc(header.frames, header.channels, header.samplerate, getBits(header), path)) {
-               gu_log("[waveManager::create] unable to allocate memory\n");
-               delete wave;
-               return G_RES_ERR_MEMORY;
-       }
+       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");
@@ -122,15 +118,11 @@ int create(const string& path, Wave** out)
 /* -------------------------------------------------------------------------- */
 
 
-int createEmpty(int frames, int channels, int samplerate, const string& name, 
+void createEmpty(int frames, int channels, int samplerate, const string& name, 
        Wave** out)
 {
        Wave* wave = new Wave();
-       if (!wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name)) {
-               gu_log("[waveManager::createEmpty] unable to allocate memory\n");
-               delete wave;
-               return G_RES_ERR_MEMORY;
-       }
+       wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name);
 
        wave->setLogical(true);
 
@@ -138,25 +130,19 @@ int createEmpty(int frames, int channels, int samplerate, const string& name,
 
        gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n", 
                wave->getSize());
-
-       return G_RES_OK;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-int createFromWave(const Wave* src, int a, int b, Wave** out)
+void createFromWave(const Wave* src, int a, int b, Wave** out)
 {
        int channels = src->getChannels();
        int frames   = b - a;
 
        Wave* wave = new Wave();
-       if (!wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath())) {
-               gu_log("[waveManager::createFromWave] unable to allocate memory\n");
-               delete wave;
-               return G_RES_ERR_MEMORY;
-       }
+       wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath());
 
        wave->copyData(src->getFrame(a), frames);
        wave->setLogical(true);
@@ -164,8 +150,6 @@ int createFromWave(const Wave* src, int a, int b, Wave** out)
        *out = wave;
 
        gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
-
-       return G_RES_OK;
 }
 
 
@@ -178,10 +162,7 @@ int resample(Wave* w, int quality, int samplerate)
        int newSizeFrames = ceil(w->getSize() * ratio);
 
        AudioBuffer newData;
-       if (!newData.alloc(newSizeFrames, w->getChannels())) {
-               gu_log("[waveManager::resample] unable to allocate memory\n");
-               return G_RES_ERR_MEMORY;
-       }
+       newData.alloc(newSizeFrames, w->getChannels());
 
        SRC_DATA src_data;
        src_data.data_in       = w->getFrame(0);
index b350695bdea81a3f451bbc5e3c265d4b35b0a480..0f0b7640afed718534733677bf2d24af6ac337af 100644 (file)
@@ -47,13 +47,13 @@ int create(const std::string& path, Wave** out);
 /* createEmpty
 Creates a new silent Wave object. */
 
-int createEmpty(int frames, int channels, int samplerate, const std::string& name, 
+void createEmpty(int frames, int channels, int samplerate, const std::string& name, 
        Wave** out);
 
 /* createFromWave
 Creates a new Wave from an existing one, copying the data in range a - b. */
 
-int createFromWave(const Wave* src, int a, int b, Wave** out);
+void createFromWave(const Wave* src, int a, int b, Wave** out);
 
 int resample(Wave* w, int quality, int samplerate); 
 int save(Wave* w, const std::string& path);
index 4dd0171299f9f078a88419f8643c8f099d4425b5..cdfe12d690fa0d512636131f31d2fda5fa094450 100644 (file)
@@ -79,8 +79,8 @@ int loadChannel(SampleChannel* ch, const string& fname)
        /* 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->status & (STATUS_PLAY | STATUS_ENDING))
-               ch->hardStop(0);
+       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. */
@@ -113,7 +113,7 @@ int loadChannel(SampleChannel* ch, const string& fname)
 /* -------------------------------------------------------------------------- */
 
 
-Channel* addChannel(int column, int type, int size)
+Channel* addChannel(int column, ChannelType type, int size)
 {
        Channel* ch    = m::mh::addChannel(type);
        geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size);
@@ -134,7 +134,7 @@ void deleteChannel(Channel* ch)
        recorder::clearChan(ch->index);
        ch->hasActions = false;
 #ifdef WITH_VST
-       pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex_plugins, ch);
+       pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex, ch);
 #endif
        Fl::lock();
        G_MainWin->keyboard->deleteChannel(ch->guiChannel);
@@ -149,7 +149,7 @@ void deleteChannel(Channel* ch)
 
 void freeChannel(Channel* ch)
 {
-       if (ch->status == STATUS_PLAY) {
+       if (ch->isPlaying()) {
                if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
                        return;
        }
@@ -159,7 +159,6 @@ void freeChannel(Channel* ch)
 
        G_MainWin->keyboard->freeChannel(ch->guiChannel);
        m::recorder::clearChan(ch->index);
-       ch->hasActions = false;
        ch->empty();
 
        /* delete any related subwindow */
@@ -204,7 +203,7 @@ int cloneChannel(Channel* src)
                ch, src->guiChannel->getSize());
 
        ch->guiChannel = gch;
-       ch->copy(src, &mixer::mutex_plugins);
+       ch->copy(src, &mixer::mutex);
 
        G_MainWin->keyboard->updateChannel(ch->guiChannel);
        return true;
@@ -272,21 +271,7 @@ void setPanning(SampleChannel* ch, float val)
 
 void toggleMute(Channel* ch, bool gui)
 {
-       using namespace giada::m;
-
-       if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) {
-               if (!ch->mute) {
-                       recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(),
-                               kernelAudio::getRealBufSize());
-                       ch->readActions = false;   // don't read actions while overdubbing
-               }
-               else
-                recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
-                       &mixer::mutex_recs);
-       }
-
-       ch->mute ? ch->unsetMute(false) : ch->setMute(false);
-
+       ch->setMute(!ch->mute, EventType::MANUAL);
        if (!gui) {
                Fl::lock();
                ch->guiChannel->mute->value(ch->mute);
@@ -300,11 +285,8 @@ void toggleMute(Channel* ch, bool gui)
 
 void toggleSolo(Channel* ch, bool gui)
 {
-       using namespace giada::m;
-       
        ch->solo = !ch->solo;
-       mh::updateSoloCount();  
-       
+       m::mh::updateSoloCount();       
        if (!gui) {
                Fl::lock();
                ch->guiChannel->solo->value(ch->solo);
@@ -350,7 +332,7 @@ void setName(Channel* ch, const string& name)
 /* -------------------------------------------------------------------------- */
 
 
-void toggleReadingRecs(SampleChannel* ch, bool gui)
+void toggleReadingActions(Channel* ch, bool gui)
 {
 
        /* When you call startReadingRecs with conf::treatRecsAsLoops, the
@@ -361,24 +343,22 @@ void toggleReadingRecs(SampleChannel* 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 == REC_WAITING))
-               stopReadingRecs(ch, gui);
+       if (ch->readActions || (!ch->readActions && ch->recStatus == ChannelStatus::WAIT))
+               stopReadingActions(ch, gui);
        else
-               startReadingRecs(ch, gui);
+               startReadingActions(ch, gui);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void startReadingRecs(SampleChannel* ch, bool gui)
+void startReadingActions(Channel* ch, bool gui)
 {
        using namespace giada::m;
 
-       if (conf::treatRecsAsLoops)
-               ch->recStatus = REC_WAITING;
-       else
-               ch->setReadActions(true, conf::recsStopOnChanHalt);
+       ch->startReadingActions(conf::treatRecsAsLoops, conf::recsStopOnChanHalt); 
+
        if (!gui) {
                Fl::lock();
                static_cast<geSampleChannel*>(ch->guiChannel)->readActions->value(1);
@@ -390,29 +370,12 @@ void startReadingRecs(SampleChannel* ch, bool gui)
 /* -------------------------------------------------------------------------- */
 
 
-void stopReadingRecs(SampleChannel* ch, bool gui)
+void stopReadingActions(Channel* ch, bool gui)
 {
        using namespace giada::m;
 
-       /* 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 (!clock::isRunning()) {
-               ch->recStatus = REC_STOPPED;
-               ch->setReadActions(false, false);
-       }
-       else
-       if (ch->recStatus == REC_WAITING)
-               ch->recStatus = REC_STOPPED;
-       else
-       if (ch->recStatus == REC_ENDING)
-               ch->recStatus = REC_READING;
-       else
-       if (conf::treatRecsAsLoops)
-               ch->recStatus = REC_ENDING;
-       else
-               ch->setReadActions(false, conf::recsStopOnChanHalt);
+       ch->stopReadingActions(clock::isRunning(), conf::treatRecsAsLoops, 
+               conf::recsStopOnChanHalt);
 
        if (!gui) {
                Fl::lock();
index e5212bb72ef16628b93ddafb66a4c9b46a67f1a2..8f6c1f9b359b7790a7daa49b87d9197afea65e14 100644 (file)
 
 
 #include <string>
+#include "../core/types.h"
 
 
 class Channel;
 class SampleChannel;
 class gdSampleEditor;
 
+
 namespace giada {
 namespace c     {
 namespace channel 
@@ -43,7 +45,7 @@ namespace channel
 /* addChannel
 Adds an empty new channel to the stack. Returns the new channel. */
 
-Channel* addChannel(int column, int type, int size);
+Channel* addChannel(int column, ChannelType type, int size);
 
 /* loadChannel
 Fills an existing channel with a wave. */
@@ -84,9 +86,9 @@ void setBoost(SampleChannel* ch, float val);
 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 toggleReadingRecs(SampleChannel* ch, bool gui=true);
-void startReadingRecs(SampleChannel* ch, bool gui=true);
-void stopReadingRecs(SampleChannel* ch, bool gui=true);
+void toggleReadingActions(Channel* ch, bool gui=true);
+void startReadingActions(Channel* ch, bool gui=true);
+void stopReadingActions(Channel* ch, bool gui=true);
 
 }}}; // giada::c::channel::
 
index 2da007dfa3757637076dead4439dd5bf75b33c4d..d9788030f2260002585da2fd21511b3c5011d7c5 100644 (file)
@@ -58,108 +58,22 @@ namespace giada {
 namespace c     {
 namespace io 
 {
-namespace
-{
-void ctrlPress(SampleChannel* ch)
-{
-       c::channel::toggleMute(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void shiftPress(SampleChannel* ch)
+void keyPress(Channel* ch, bool ctrl, bool shift, int velocity)
 {
-       /* action recording on:
-                       if sequencer is running, rec a killchan
-                action recording off:
-                       if chan has recorded events:
-                       |        if seq is playing OR channel 'c' is stopped, de/activate recs
-                       |        else kill chan
-                       else kill chan. */
-
-       if (m::recorder::active) {
-               if (!m::clock::isRunning()) 
-                       return;
-               ch->kill(0); // on frame 0: user-generated event
-               if (m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording) &&
-                               !(ch->mode & LOOP_ANY))
-               {   // don't record killChan actions for LOOP channels
-                       m::recorder::rec(ch->index, G_ACTION_KILL, m::clock::getCurrentFrame());
-                       ch->hasActions = true;
-               }
+       /* Everything occurs on frame 0 here: they are all user-generated events. */
+       if (ctrl) {
+               ch->recordMute();
+               c::channel::toggleMute(ch);
        }
-       else {
-               if (ch->hasActions) {
-                       if (m::clock::isRunning() || ch->status == STATUS_OFF)
-                               ch->readActions ? c::channel::stopReadingRecs(ch) : c::channel::startReadingRecs(ch);
-                       else
-                               ch->kill(0);  // on frame 0: user-generated event
-               }
-               else
-                       ch->kill(0);    // on frame 0: user-generated event
+       else
+       if (shift) {
+               if (ch->recordKill())
+                       ch->kill(0);
        }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void cleanPress(SampleChannel* ch, int velocity)
-{
-       /* Record now if the quantizer is off, otherwise let mixer to handle it when a
-       quantoWait has passed. Moreover, KEYPRESS and KEYREL are meaningless for loop 
-       modes. */
-
-       if (m::clock::getQuantize() == 0 &&
-                       m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording) &&
-                       !(ch->mode & LOOP_ANY))
-       {
-               if (ch->mode == SINGLE_PRESS) {
-                       m::recorder::startOverdub(ch->index, G_ACTION_KEYS, m::clock::getCurrentFrame(),
-                               m::kernelAudio::getRealBufSize());
-                       ch->readActions = false;   // don't read actions while overdubbing
-               }
-               else {
-                       m::recorder::rec(ch->index, G_ACTION_KEYPRESS, m::clock::getCurrentFrame());
-                       ch->hasActions = true;
-
-                       /* Why return here? You record an action and then you call ch->start: 
-                       Mixer, which is on another thread, reads your newly recorded action if you 
-                       have readActions == true, and then ch->start kicks in right after it.
-                       The result: Mixer plays the channel (due to the new action) but ch->start
-                       kills it right away (because the sample is playing). Fix: call ch->start
-                       only if you are not recording anything, i.e. let Mixer play it. */
-
-                       if (ch->readActions)
-                               return;
-               }
+       else {
+               if (ch->recordStart(m::clock::canQuantize()))
+                       ch->start(0, m::clock::canQuantize(), 0);
        }
-
-       /* This is a user-generated event, so it's on frame 0. For one-shot modes,
-       velocity drives the internal volume. */
-
-       if (ch->mode & SINGLE_ANY && ch->midiInVeloAsVol)
-               ch->setVolumeI(u::math::map((float)velocity, 0.0f, 127.0f, 0.0f, 1.0f));
-
-       ch->start(0, true, m::clock::getQuantize(), m::clock::isRunning(), false, true);
-}
-
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void keyPress(Channel* ch, bool ctrl, bool shift, int velocity)
-{
-       if (ch->type == G_CHANNEL_SAMPLE)
-               keyPress(static_cast<SampleChannel*>(ch), ctrl, shift, velocity);
-       else
-               keyPress(static_cast<MidiChannel*>(ch), ctrl, shift);
 }
 
 
@@ -168,61 +82,10 @@ void keyPress(Channel* ch, bool ctrl, bool shift, int velocity)
 
 void keyRelease(Channel* ch, bool ctrl, bool shift)
 {
-       if (ch->type == G_CHANNEL_SAMPLE)
-               keyRelease(static_cast<SampleChannel*>(ch), ctrl, shift);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void keyPress(MidiChannel* ch, bool ctrl, bool shift)
-{
-       if (ctrl)
-               c::channel::toggleMute(ch);
-       else
-       if (shift)
-               ch->kill(0);        // on frame 0: user-generated event
-       else
-               ch->start(0, true, m::clock::getQuantize(), m::clock::isRunning(), false, true); // on frame 0: user-generated event
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void keyPress(SampleChannel* ch, bool ctrl, bool shift, int velocity)
-{
-       if (ctrl)
-               ctrlPress(ch);
-       else if (shift)
-               shiftPress(ch);
-       else
-               cleanPress(ch, velocity);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void keyRelease(SampleChannel* ch, bool ctrl, bool shift)
-{
-       using namespace giada::m;
-
-       if (ctrl || shift)
-               return;
-
-       ch->stop();
-
-       /* record a key release only if channel is single_press. For any
-        * other mode the KEY REL is meaningless. */
-
-       if (ch->mode == SINGLE_PRESS && recorder::canRec(ch, clock::isRunning(), mixer::recording))
-               recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
-                       &mixer::mutex_recs);
-
-       /* the GUI update is done by gui_refresh() */
-
+       if (!ctrl && !shift) {
+               ch->recordStop();
+               ch->stop();
+       }
 }
 
 
@@ -248,7 +111,7 @@ void startActionRec(bool gui)
        recorder::active = true;
 
        if (!clock::isRunning())
-               glue_startSeq(false);  // update gui ayway
+               glue_startSeq(false);  // update gui
 
        if (!gui) {
                Fl::lock();
@@ -263,19 +126,18 @@ void startActionRec(bool gui)
 
 void stopActionRec(bool gui)
 {
-       /* stop the recorder and sort new actions */
+       /* Stop the recorder and sort newly recorder actions. */
 
        m::recorder::active = false;
        m::recorder::sortActions();
 
        for (Channel* ch : m::mixer::channels)
        {
-               if (ch->type == G_CHANNEL_MIDI)
+               if (ch->type == ChannelType::MIDI)
                        continue;
-               SampleChannel* sch = static_cast<SampleChannel*>(ch);
-               G_MainWin->keyboard->setChannelWithActions(static_cast<geSampleChannel*>(sch->guiChannel));
-               if (!sch->readActions && sch->hasActions)
-                       c::channel::startReadingRecs(sch, false);
+               G_MainWin->keyboard->setChannelWithActions(static_cast<geSampleChannel*>(ch->guiChannel));
+               if (!ch->readActions && ch->hasActions)
+                       c::channel::startReadingActions(ch, false);
        }
 
        if (!gui) {
@@ -346,20 +208,6 @@ int stopInputRec(bool gui)
        
        mh::stopInputRec();
 
-       /* Start all sample channels in loop mode that were armed, i.e. that were
-       recording stuff and not yet in play. They are also started in force mode, i.e.
-       they must start playing right away at the current frame, not at the next first
-       beat. */
-
-       for (Channel* ch : mixer::channels) {
-               if (ch->type == G_CHANNEL_MIDI)
-                       continue;
-               SampleChannel* sch = static_cast<SampleChannel*>(ch);
-               if (sch->mode & (LOOP_ANY) && sch->status == STATUS_OFF && sch->armed)
-                       sch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
-                               clock::isRunning(), true, true);
-       }
-
        Fl::lock();
                if (!gui)
                        G_MainWin->mainTransport->updateRecInput(0);
index 0dccf9e258bd416d1ce1898d68b6decdc2f3a2c1..799a56ef3169a3e2ff55a9ef8e95177fa1af63d9 100644 (file)
@@ -45,15 +45,12 @@ namespace c     {
 namespace io 
 {
 /* keyPress / keyRelease
- * handle the key pressure, either via mouse/keyboard or MIDI. If gui
- * is true it means that the event comes from the main window (mouse,
- * keyb or MIDI), otherwise the event comes from the action recorder. */
+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  (Channel*       ch, bool ctrl, bool shift, int velocity);
-void keyPress  (SampleChannel* ch, bool ctrl, bool shift, int velocity);
-void keyPress  (MidiChannel*   ch, bool ctrl, bool shift);
-void keyRelease(Channel*       ch, bool ctrl, bool shift);
-void keyRelease(SampleChannel* ch, bool ctrl, bool shift);
+void keyPress  (Channel* ch, bool ctrl, bool shift, int velocity);
+void keyRelease(Channel* ch, bool ctrl, bool shift);
 
 /* start/stopActionRec
 Handles the action recording. If gui == true the signal comes from an user
index 4fc529d726543736df6ce8a5dbc64dc349bbf3fa..95d1138e6189c0737ec2ed0963ee01e508e172ee 100644 (file)
@@ -42,6 +42,7 @@
 #include "../core/kernelMidi.h"
 #include "../core/kernelAudio.h"
 #include "../core/conf.h"
+#include "../core/const.h"
 #ifdef WITH_VST
 #include "../core/pluginHost.h"
 #endif
 extern gdMainWindow *G_MainWin;
 
 
+using std::string;
 using namespace giada::m;
 
 
-void glue_setBpm(const char *v1, const char *v2)
+namespace
+{
+void setBpm_(float f, string s)
+{
+       if (f < G_MIN_BPM) {
+               f = G_MIN_BPM;
+               s = G_MIN_BPM_STR;
+       }
+       else
+       if (f > G_MAX_BPM) {
+               f = G_MAX_BPM;
+               s = G_MAX_BPM_STR;              
+       }
+
+       float vPre = clock::getBpm();
+       clock::setBpm(f);
+       recorder::updateBpm(vPre, f, clock::getQuanto());
+       mixer::allocVirtualInput(clock::getFramesInLoop());
+
+       gu_refreshActionEditor();
+       G_MainWin->mainTimer->setBpm(s.c_str());
+
+       gu_log("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s.c_str(), clock::getBpm());
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBpm(const char* v1, const char* v2)
 {
        /* Never change this stuff while recording audio */
 
        if (mixer::recording)
                return;
 
-       char  bpmS[6];
-       float bpmF = atof(v1) + (atof(v2)/10);
-       if (bpmF < 20.0f) {
-               bpmF = 20.0f;
-               sprintf(bpmS, "20.0");
-       }
-       else
-               sprintf(bpmS, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
-
-       /* a value such as atof("120.1") will never be 120.1 but 120.0999999,
-        * because of the rounding error. So we pass the real "wrong" value to
-        * G_Mixer and we show the nice looking (but fake) one to the GUI. */
+       /* 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 
+       the nice looking (but fake) one to the GUI. 
+       On Linux, let Jack handle the bpm change if its on. */
 
-       float oldBpmF = clock::getBpm();
-       clock::setBpm(bpmF);
-       recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto());
-       mixer::allocVirtualInput(clock::getFramesInLoop());
+       float  f = atof(v1) + (atof(v2)/10);
+       string s = string(v1) + "." + string(v2);
 
-#ifdef __linux__
-       kernelAudio::jackSetBpm(clock::getBpm());
+#ifdef G_OS_LINUX
+       if (kernelAudio::getAPI() == G_SYS_API_JACK)
+               kernelAudio::jackSetBpm(f);
+       else
 #endif
-
-       gu_refreshActionEditor();
-       G_MainWin->mainTimer->setBpm(bpmS);
-
-       gu_log("[glue] Bpm changed to %s (real=%f)\n", bpmS, clock::getBpm());
+       setBpm_(f, s);
 }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void glue_setBpm(float v)
+void glue_setBpm(float f)
 {
-       if (v < G_MIN_BPM || v > G_MAX_BPM)
-               v = G_DEFAULT_BPM;
-       double fIpart;
-       double fPpart = modf(v, &fIpart);
-       int iIpart = fIpart;
-       int iPpart = ceilf(fPpart);
-       glue_setBpm(gu_iToString(iIpart).c_str(), gu_iToString(iPpart).c_str());
+       /* Never change this stuff while recording audio */
+
+       if (mixer::recording)
+               return;
+
+       float intpart;
+       float fracpart = std::round(std::modf(f, &intpart) * 10);
+       string s = std::to_string((int) intpart) + "." + std::to_string((int)fracpart);
+
+       setBpm_(f, s);
 }
 
 
@@ -232,7 +257,7 @@ void glue_resetToInitState(bool resetGui, bool createColumns)
        mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
        recorder::init();
 #ifdef WITH_VST
-       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
+       pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex);
 #endif
 
        G_MainWin->keyboard->clear();
index c684b2207b802252ed0e987f313f6b1481f30894..5dc304d9a0c9d67bd5d0024a7ae9c23c390a3c2c 100644 (file)
 #define G_GLUE_MAIN_H
 
 
+/* glue_setBpm (1)
+Sets bpm value from string to float. */
+
 void glue_setBpm(const char* v1, const char* v2);
+
+/* glue_setBpm (2)
+Sets bpm value. Usually called from the Jack callback or non-UI components. */
+
 void glue_setBpm(float v);
+
 void glue_setBeats(int beats, int bars, bool expand);
 void glue_quantize(int val);
 void glue_setOutVol(float v, bool gui=true);
@@ -46,13 +54,13 @@ void glue_clearAllSamples();
 void glue_clearAllActions();
 
 /* resetToInitState
- * reset Giada to init state. If resetGui also refresh all widgets. If
- * createColumns also build initial empty columns. */
+Resets Giada to init state. If resetGui also refresh all widgets. If 
+createColumns also build initial empty columns. */
 
 void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
 
 /* beatsDivide/Multiply
- * shrinks or enlarges the number of beats by 2. */
+Shrinks or enlarges the number of beats by 2. */
 
 void glue_beatsMultiply();
 void glue_beatsDivide();
index 20a0f8bad9d591d70f77fb0937e3784fa7bee444..c5282ac427311b4d196e37813ea879b171417752 100644 (file)
@@ -28,6 +28,7 @@
 #ifdef WITH_VST
 
 
+#include <FL/Fl.H>
 #include "../core/pluginHost.h"
 #include "../core/mixer.h"
 #include "../core/plugin.h"
@@ -82,7 +83,7 @@ Plugin* addPlugin(Channel* ch, int index, int stackType)
 {
   if (index >= pluginHost::countAvailablePlugins())
     return nullptr;
-  return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch);
+  return pluginHost::addPlugin(index, stackType, &mixer::mutex, ch);
 }
 
 
@@ -91,7 +92,7 @@ Plugin* addPlugin(Channel* ch, int index, int stackType)
 
 void swapPlugins(Channel* ch, int index1, int index2, int stackType)
 {
-  pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex_plugins,
+  pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex,
     ch);
 }
 
@@ -101,7 +102,7 @@ void swapPlugins(Channel* ch, int index1, int index2, int stackType)
 
 void freePlugin(Channel* ch, int index, int stackType)
 {
-  pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch);
+  pluginHost::freePlugin(index, stackType, &mixer::mutex, ch);
 }
 
 
index 53670c3b8684f65fb89c653578ed01f2f334c65f..f193285a3230aa35cc143c196a7b02e41bccde28 100644 (file)
@@ -51,7 +51,7 @@ namespace
 void updateChannel(geChannel* gch)
 {
        gch->ch->hasActions = m::recorder::hasActions(gch->ch->index);
-       if (gch->ch->type == G_CHANNEL_SAMPLE && !gch->ch->hasActions)
+       if (gch->ch->type == ChannelType::SAMPLE && !gch->ch->hasActions)
                static_cast<geSampleChannel*>(gch)->hideActionButton();
        /* TODO - set mute=false */
        gu_refreshActionEditor(); // refresh a.editor window, it could be open
index 568e508a587fad78bfa95220ef42a52ae264401b..1793a92d5e57364f4155f808605c013a5f65fde8 100644 (file)
@@ -121,12 +121,7 @@ void copy(SampleChannel* ch, int a, int b)
 {
        if (m_waveBuffer != nullptr)
                delete m_waveBuffer;
-
-       int result = m::waveManager::createFromWave(ch->wave, a, b, &m_waveBuffer);
-       if (result != G_RES_OK) {
-               gu_log("[sampleEditor::copy] unable to create wave buffer!\n");
-               return;
-       }
+       m::waveManager::createFromWave(ch->wave, a, b, &m_waveBuffer);
 }
 
 
@@ -243,9 +238,9 @@ void setPlayHead(SampleChannel* ch, int f)
 /* -------------------------------------------------------------------------- */
 
 
-void setPreview(SampleChannel* ch, int mode)
+void setPreview(SampleChannel* ch, PreviewMode mode)
 {
-       ch->setPreviewMode(mode);
+       ch->previewMode = mode;
        gdSampleEditor* gdEditor = getSampleEditorWindow();
        gdEditor->play->value(!gdEditor->play->value());
 }
@@ -270,14 +265,10 @@ void rewindPreview(SampleChannel* ch)
 void toNewChannel(SampleChannel* ch, int a, int b)
 {
        SampleChannel* newCh = static_cast<SampleChannel*>(c::channel::addChannel(
-               ch->guiChannel->getColumnIndex(), G_CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
+               ch->guiChannel->getColumnIndex(), ChannelType::SAMPLE, G_GUI_CHANNEL_H_1));
 
        Wave* wave = nullptr;
-       int result = m::waveManager::createFromWave(ch->wave, a, b, &wave);
-       if (result != G_RES_OK) {
-               gdAlert("Unable to copy to new channel!");
-               return;
-       }
+       m::waveManager::createFromWave(ch->wave, a, b, &wave);
 
        newCh->pushWave(wave);
        newCh->guiChannel->update();
index b38cbb2a6ae0a7318f5616cd545622516e9061ca..4933100486f7fe8081a3b1b5b0e1c3056b0d32d4 100644 (file)
@@ -29,6 +29,9 @@
 #define G_GLUE_SAMPLE_EDITOR_H
 
 
+#include "../core/types.h"
+
+
 class SampleChannel;
 class geWaveform;
 
@@ -66,7 +69,7 @@ Changes playhead's position. Used in preview. */
 
 void setPlayHead(SampleChannel* ch, int f);
 
-void setPreview(SampleChannel* ch, int mode);
+void setPreview(SampleChannel* ch, PreviewMode mode);
 void rewindPreview(SampleChannel* ch);
 
 /* toNewChannel
index 4f99a0080b5ed7087e490422c730c4753184ca26..2a79efc797f99ee25f07c8976bdd605d3ce12a63 100644 (file)
@@ -283,7 +283,7 @@ void glue_loadPatch(void* data)
                unsigned k = 0;
                for (const patch::channel_t& pch : patch::channels) {
                        if (pch.column == col.index) {
-                               Channel* ch = c::channel::addChannel(pch.column, pch.type, pch.size);
+                               Channel* ch = c::channel::addChannel(pch.column, static_cast<ChannelType>(pch.type), pch.size);
                                ch->readPatch(basePath, k);
                        }
                        browser->setStatusBar(steps);
@@ -360,7 +360,7 @@ void glue_saveProject(void* data)
 
        for (const Channel* ch : mixer::channels) {
 
-               if (ch->type == G_CHANNEL_MIDI)
+               if (ch->type == ChannelType::MIDI)
                        continue;
 
                const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
diff --git a/src/gui/dialogs/about.cpp b/src/gui/dialogs/about.cpp
new file mode 100644 (file)
index 0000000..79f7996
--- /dev/null
@@ -0,0 +1,144 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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_Pixmap.H>
+#include <FL/fl_draw.H>
+#include <jansson.h>
+#include "../../core/conf.h"
+#include "../../core/const.h"
+#include "../../core/graphics.h"
+#ifdef WITH_VST
+  #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 "about.h"
+
+
+using std::string;
+using namespace giada::m;
+using namespace giada::u;
+
+
+gdAbout::gdAbout()
+#ifdef WITH_VST
+: gdWindow(340, 435, "About Giada")
+#else
+: gdWindow(340, 350, "About Giada")
+#endif
+{
+       if (conf::aboutX)
+               resize(conf::aboutX, conf::aboutY, w(), h());
+
+       set_modal();
+
+       logo  = new geBox(8, 10, 324, 86);
+       text  = new geBox(8, 120, 324, 145);
+       close = new geButton(252, h()-28, 80, 20, "Close");
+#ifdef WITH_VST
+       vstLogo = new geBox(8, 265, 324, 50);
+       vstText = new geBox(8, 315, 324, 46);
+#endif
+       end();
+
+       logo->image(new Fl_Pixmap(giada_logo_xpm));
+       text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
+
+       string message = gu_format(
+         "Version " G_VERSION_STR " (" BUILD_DATE ")\n\n"
+               "Developed by Monocasual Laboratories\n"
+               "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
+               "RtMidi (%s), Libsamplerate, Jansson (%s),\n"
+               "Libsndfile (%s)"
+#ifdef WITH_VST
+               ", JUCE (%d.%d.%d)\n\n"
+#else
+               "\n\n"
+#endif
+               "Released under the terms of the GNU General\n"
+               "Public License (GPL v3)\n\n"
+               "News, infos, contacts and documentation:\n"
+               "www.giadamusic.com",
+               FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
+               ver::getRtAudioVersion().c_str(),
+               ver::getRtMidiVersion().c_str(),
+               JANSSON_VERSION, ver::getLibsndfileVersion().c_str()
+#ifdef WITH_VST
+               , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER
+#endif
+       );
+
+       int tw = 0;
+       int th = 0;
+       fl_measure(message.c_str(), tw, th);
+       text->copy_label(message.c_str());
+       text->size(text->w(), th);
+
+#ifdef WITH_VST
+       vstLogo->image(new Fl_Pixmap(vstLogo_xpm));
+       vstLogo->position(vstLogo->x(), text->y()+text->h()+8);
+       vstText->label(
+               "VST Plug-In Technology by Steinberg\n"
+               "VST is a trademark of Steinberg\nMedia Technologies GmbH"
+       );
+       vstText->position(vstText->x(), vstLogo->y()+vstLogo->h());
+
+#endif
+
+       close->callback(cb_close, (void*)this);
+       gu_setFavicon(this);
+       setId(WID_ABOUT);
+       show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdAbout::~gdAbout()
+{
+       conf::aboutX = x();
+       conf::aboutY = y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdAbout::cb_close(Fl_Widget* w, void* p) { ((gdAbout*)p)->cb_close(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdAbout::cb_close()
+{
+       do_callback();
+}
diff --git a/src/gui/dialogs/about.h b/src/gui/dialogs/about.h
new file mode 100644 (file)
index 0000000..8d37cda
--- /dev/null
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_ABOUT_H
+#define GD_ABOUT_H
+
+
+#include "window.h"
+
+
+class geBox;
+class geButton;
+
+
+class gdAbout : public gdWindow
+{
+private:
+
+       geBox*    logo;
+       geBox*    text;
+       geButton* close;
+
+#ifdef WITH_VST
+       geBox* vstText;
+       geBox* vstLogo;
+#endif
+
+public:
+
+       gdAbout();
+       ~gdAbout();
+
+       static void cb_close(Fl_Widget* w, void* p);
+       inline void cb_close();
+};
+
+#endif
diff --git a/src/gui/dialogs/gd_about.cpp b/src/gui/dialogs/gd_about.cpp
deleted file mode 100644 (file)
index eb9228e..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 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_Pixmap.H>
-#include <FL/fl_draw.H>
-#include <jansson.h>
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/graphics.h"
-#ifdef WITH_VST
-  #include "../../deps/juce-config.h"
-#endif
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../../utils/deps.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/box.h"
-#include "gd_about.h"
-
-
-using std::string;
-using namespace giada::m;
-using namespace giada::u;
-
-
-gdAbout::gdAbout()
-#ifdef WITH_VST
-: gdWindow(340, 435, "About Giada")
-#else
-: gdWindow(340, 350, "About Giada")
-#endif
-{
-       if (conf::aboutX)
-               resize(conf::aboutX, conf::aboutY, w(), h());
-
-       set_modal();
-
-       logo  = new geBox(8, 10, 324, 86);
-       text  = new geBox(8, 120, 324, 145);
-       close = new geButton(252, h()-28, 80, 20, "Close");
-#ifdef WITH_VST
-       vstLogo = new geBox(8, 265, 324, 50);
-       vstText = new geBox(8, 315, 324, 46);
-#endif
-       end();
-
-       logo->image(new Fl_Pixmap(giada_logo_xpm));
-       text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
-
-       string message = gu_format(
-         "Version " G_VERSION_STR " (" BUILD_DATE ")\n\n"
-               "Developed by Monocasual Laboratories\n"
-               "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
-               "RtMidi (%s), Libsamplerate, Jansson (%s),\n"
-               "Libsndfile (%s)"
-#ifdef WITH_VST
-               ", JUCE (%d.%d.%d)\n\n"
-#else
-               "\n\n"
-#endif
-               "Released under the terms of the GNU General\n"
-               "Public License (GPL v3)\n\n"
-               "News, infos, contacts and documentation:\n"
-               "www.giadamusic.com",
-               FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
-               deps::getRtAudioVersion().c_str(),
-               deps::getRtMidiVersion().c_str(),
-               JANSSON_VERSION, deps::getLibsndfileVersion().c_str()
-#ifdef WITH_VST
-               , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER
-#endif
-       );
-
-       int tw = 0;
-       int th = 0;
-       fl_measure(message.c_str(), tw, th);
-       text->copy_label(message.c_str());
-       text->size(text->w(), th);
-
-#ifdef WITH_VST
-       vstLogo->image(new Fl_Pixmap(vstLogo_xpm));
-       vstLogo->position(vstLogo->x(), text->y()+text->h()+8);
-       vstText->label(
-               "VST Plug-In Technology by Steinberg\n"
-               "VST is a trademark of Steinberg\nMedia Technologies GmbH"
-       );
-       vstText->position(vstText->x(), vstLogo->y()+vstLogo->h());
-
-#endif
-
-       close->callback(cb_close, (void*)this);
-       gu_setFavicon(this);
-       setId(WID_ABOUT);
-       show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdAbout::~gdAbout()
-{
-       conf::aboutX = x();
-       conf::aboutY = y();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdAbout::__cb_close()
-{
-       do_callback();
-}
diff --git a/src/gui/dialogs/gd_about.h b/src/gui/dialogs/gd_about.h
deleted file mode 100644 (file)
index aa93094..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GD_ABOUT_H
-#define GD_ABOUT_H
-
-
-#include "window.h"
-
-
-class geBox;
-class geButton;
-
-
-class gdAbout : public gdWindow
-{
-private:
-
-       geBox      *logo;
-       geBox      *text;
-       geButton *close;
-
-#ifdef WITH_VST
-       geBox  *vstText;
-       geBox  *vstLogo;
-#endif
-
-public:
-
-       gdAbout();
-       ~gdAbout();
-
-       static void cb_close(Fl_Widget *w, void *p);
-       inline void __cb_close();
-};
-
-#endif
index 885f549fbcd02bf1536d57f8e6034c53b29c6686..499f9a331347a3e867ba1480b35ad28529aeb402 100644 (file)
 
 
 using std::string;
+using namespace giada;
 using namespace giada::m;
 
 
-gdActionEditor::gdActionEditor(Channel *chan)
+gdActionEditor::gdActionEditor(Channelchan)
        :       gdWindow(640, 284),
                chan   (chan),
                zoom   (100),
@@ -70,7 +71,7 @@ gdActionEditor::gdActionEditor(Channel *chan)
 
        upperArea->begin();
 
-       if (chan->type == G_CHANNEL_SAMPLE) {
+       if (chan->type == ChannelType::SAMPLE) {
          actionType = new geChoice(8, 8, 80, 20);
          gridTool   = new geGridTool(actionType->x()+actionType->w()+4, 8, this);
                actionType->add("key press");
@@ -78,8 +79,8 @@ gdActionEditor::gdActionEditor(Channel *chan)
                actionType->add("kill chan");
                actionType->value(0);
 
-               SampleChannel *ch = (SampleChannel*) chan;
-               if (ch->mode == SINGLE_PRESS || ch->mode & LOOP_ANY)
+               SampleChannel *ch = static_cast<SampleChannel*>(chan);
+               if (ch->mode == ChannelMode::SINGLE_PRESS || ch->isAnyLoopMode())
                actionType->deactivate();
        }
        else {
@@ -99,9 +100,9 @@ gdActionEditor::gdActionEditor(Channel *chan)
 
        scroller = new geScroll(8, 36, w()-16, h()-44);
 
-       if (chan->type == G_CHANNEL_SAMPLE) {
+       if (chan->type == ChannelType::SAMPLE) {
 
-               SampleChannel *ch = (SampleChannel*) chan;
+               SampleChannel *ch = static_cast<SampleChannel*>(chan);
 
                ac = new geActionEditor  (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
                mc = new geMuteEditor    (scroller->x(), ac->y()+ac->h()+8, this);
@@ -120,7 +121,7 @@ gdActionEditor::gdActionEditor(Channel *chan)
                /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot
                 * hold keypress/keyrelease actions */
 
-               if (ch->mode & LOOP_ANY)
+               if (ch->isAnyLoopMode())
                        ac->deactivate();
        }
        else {
@@ -188,7 +189,7 @@ void gdActionEditor::__cb_zoomIn()
 
        update();
 
-       if (chan->type == G_CHANNEL_SAMPLE) {
+       if (chan->type == ChannelType::SAMPLE) {
                ac->size(totalWidth, ac->h());
                mc->size(totalWidth, mc->h());
                vc->size(totalWidth, vc->h());
@@ -222,7 +223,7 @@ void gdActionEditor::__cb_zoomOut()
 
        update();
 
-       if (chan->type == G_CHANNEL_SAMPLE) {
+       if (chan->type == ChannelType::SAMPLE) {
                ac->size(totalWidth, ac->h());
                mc->size(totalWidth, mc->h());
                vc->size(totalWidth, vc->h());
index 3fec31779c612080610926df0ac6de7407c55b8f..8656fdb4de617ce03040a8b4d5296144c91eb586 100644 (file)
@@ -47,6 +47,7 @@
 
 using std::string;
 using std::vector;
+using namespace giada;
 using namespace giada::m;
 
 
@@ -59,7 +60,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch)
        label(title.c_str());
        size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
 
-       int extra = ch->type == G_CHANNEL_SAMPLE ? 28 : 0;
+       int extra = ch->type == ChannelType::SAMPLE ? 28 : 0;
 
        Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20 + extra);
        groupHeader->begin();
@@ -98,7 +99,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch)
        enable->value(ch->midiIn);
        enable->callback(cb_enable, (void*)this);
 
-       if (ch->type == G_CHANNEL_SAMPLE) {
+       if (ch->type == ChannelType::SAMPLE) {
                veloAsVol->value(static_cast<SampleChannel*>(ch)->midiInVeloAsVol);
                veloAsVol->callback(cb_veloAsVol, (void*)this); 
        }
@@ -165,7 +166,7 @@ void gdMidiInputChannel::addChannelLearners()
                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 == G_CHANNEL_SAMPLE) {
+               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, 
index 9ba36c230f5a322c43dfc23b3d5a8a0de8d46a2e..ab0f3fe49b0fa15400080fe8d20be057c2570965 100644 (file)
 #ifdef WITH_VST
 
 
+#include <string>
+#include <FL/Fl_Scroll.H>
 #include "../../utils/gui.h"
-#include "../../utils/fs.h"
 #include "../../core/conf.h"
 #include "../../core/const.h"
-#include "../../core/graphics.h"
 #include "../../core/pluginHost.h"
-#include "../../core/plugin.h"
-#include "../../core/mixer.h"
 #include "../../core/channel.h"
-#include "../../glue/plugin.h"
-#include "../../utils/log.h"
 #include "../../utils/string.h"
 #include "../elems/basics/boxtypes.h"
-#include "../elems/basics/idButton.h"
+#include "../elems/basics/button.h"
 #include "../elems/basics/statusButton.h"
-#include "../elems/basics/choice.h"
 #include "../elems/mainWindow/mainIO.h"
 #include "../elems/mainWindow/keyboard/channel.h"
+#include "../elems/plugin/pluginElement.h"
 #include "pluginChooser.h"
-#include "pluginWindow.h"
-#include "pluginWindowGUI.h"
 #include "gd_mainWindow.h"
 #include "pluginList.h"
 
@@ -57,13 +51,14 @@ extern gdMainWindow* G_MainWin;
 
 
 using std::string;
-using namespace giada::m;
-using namespace giada::c;
+using namespace giada;
 
 
 gdPluginList::gdPluginList(int stackType, Channel* ch)
        : gdWindow(468, 204), ch(ch), stackType(stackType)
 {
+       using namespace giada::m;
+
        if (conf::pluginListX)
                resize(conf::pluginListX, conf::pluginListY, w(), h());
 
@@ -104,8 +99,8 @@ gdPluginList::gdPluginList(int stackType, Channel* ch)
 
 gdPluginList::~gdPluginList()
 {
-       conf::pluginListX = x();
-       conf::pluginListY = y();
+       m::conf::pluginListX = x();
+       m::conf::pluginListY = y();
 }
 
 
@@ -120,17 +115,17 @@ void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_
 
 void gdPluginList::cb_refreshList(Fl_Widget* v, void* p)
 {
-       /* 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). */
+       /* 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). */
 
-       gdWindow *child = (gdWindow*) v;
+       gdWindow* child = static_cast<gdWindow*>(v);
        if (child->getParent() != nullptr)
                (child->getParent())->delSubWindow(child);
 
-       /* finally refresh plugin list: void *p is a pointer to gdPluginList.
-        * This callback works even when you click 'x' to close the window...
-        * well, who cares */
+       /* 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. */
 
        ((gdPluginList*)p)->refreshList();
        ((gdPluginList*)p)->redraw();
@@ -142,10 +137,12 @@ void gdPluginList::cb_refreshList(Fl_Widget* v, void* p)
 
 void gdPluginList::cb_addPlugin()
 {
-       /* 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. */
+       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,
@@ -160,6 +157,8 @@ void gdPluginList::cb_addPlugin()
 
 void gdPluginList::refreshList()
 {
+       using namespace giada::m;
+
        /* delete the previous list */
 
        list->clear();
@@ -173,9 +172,10 @@ void gdPluginList::refreshList()
        int i = 0;
 
        while (i<numPlugins) {
-               Plugin   *pPlugin = pluginHost::getPluginByIndex(i, stackType, ch);
-               gdPlugin *gdp     = new gdPlugin(this, pPlugin, list->x(), list->y()-list->yposition()+(i*24), 800);
-               list->add(gdp);
+               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++;
        }
 
@@ -200,13 +200,11 @@ void gdPluginList::refreshList()
        gdPluginListMaster */
 
        if (stackType == pluginHost::MASTER_OUT) {
-               G_MainWin->mainIO->setMasterFxOutFull(
-                       pluginHost::countPlugins(stackType, ch) > 0);
+               G_MainWin->mainIO->setMasterFxOutFull(pluginHost::countPlugins(stackType, ch) > 0);
        }
        else
        if (stackType == pluginHost::MASTER_IN) {
-               G_MainWin->mainIO->setMasterFxInFull(
-                       pluginHost::countPlugins(stackType, ch) > 0);
+               G_MainWin->mainIO->setMasterFxInFull(pluginHost::countPlugins(stackType, ch) > 0);
        }
        else {
                ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0;
@@ -215,163 +213,4 @@ void gdPluginList::refreshList()
 }
 
 
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gdPlugin::gdPlugin(gdPluginList* gdp, Plugin* p, int X, int Y, int W)
-       : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p)
-{
-       begin();
-       button    = new geIdButton(8, y(), 220, 20);
-       program   = new geChoice(button->x()+button->w()+4, y(), 132, 20);
-       bypass    = new geIdButton(program->x()+program->w()+4, y(), 20, 20);
-       shiftUp   = new geIdButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
-       shiftDown = new geIdButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
-       remove    = new geIdButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
-       end();
-
-       button->copy_label(pPlugin->getName().c_str());
-       button->callback(cb_openPluginWindow, (void*)this);
-
-       program->callback(cb_setProgram, (void*)this);
-
-       for (int i=0; i<pPlugin->getNumPrograms(); i++)
-               program->add(gu_removeFltkChars(pPlugin->getProgramName(i)).c_str());
-
-       if (program->size() == 0) {
-               program->add("-- no programs --\0");
-               program->deactivate();
-       }
-       else
-               program->value(pPlugin->getCurrentProgram());
-
-       bypass->callback(cb_setBypass, (void*)this);
-       bypass->type(FL_TOGGLE_BUTTON);
-       bypass->value(pPlugin->isBypassed() ? 0 : 1);
-
-       shiftUp->callback(cb_shiftUp, (void*)this);
-       shiftDown->callback(cb_shiftDown, (void*)this);
-       remove->callback(cb_removePlugin, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_removePlugin    (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_removePlugin(); }
-void gdPlugin::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_openPluginWindow(); }
-void gdPlugin::cb_setBypass       (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_setBypass(); }
-void gdPlugin::cb_shiftUp         (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_shiftUp(); }
-void gdPlugin::cb_shiftDown       (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_shiftDown(); }
-void gdPlugin::cb_setProgram      (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_setProgram(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_shiftUp()
-{
-       /*nothing to do if there's only one plugin */
-
-       if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
-               return;
-
-       int pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(),
-               pParent->stackType, pParent->ch);
-
-       if (pluginIndex == 0)  // first of the stack, do nothing
-               return;
-
-       plugin::swapPlugins(pParent->ch, pluginIndex, pluginIndex-1, pParent->stackType);
-       pParent->refreshList();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_shiftDown()
-{
-       /*nothing to do if there's only one plugin */
-
-       if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
-               return;
-
-       unsigned pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
-       unsigned stackSize   = (pluginHost::getStack(pParent->stackType, pParent->ch))->size();
-
-       if (pluginIndex == stackSize-1)  // last one in the stack, do nothing
-               return;
-
-       plugin::swapPlugins(pParent->ch, pluginIndex, pluginIndex+1, pParent->stackType);
-       pParent->refreshList();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_removePlugin()
-{
-       /* any subwindow linked to the plugin must be destroyed first */
-
-       pParent->delSubWindow(pPlugin->getId());
-       plugin::freePlugin(pParent->ch, pPlugin->getId(), pParent->stackType);
-       pParent->refreshList();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_openPluginWindow()
-{
-       /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved
-       * for the parent window 'add plugin'. */
-
-       int pwid = pPlugin->getId() + 1;
-       
-       gdWindow* w;
-       if (pPlugin->hasEditor()) {
-               if (pPlugin->isEditorOpen()) {
-                       gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has editor but it's already visible\n");
-                       pParent->getChild(pwid)->show();  // Raise it to top
-                       return;
-               }
-               gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has editor, window id=%d\n", pwid);
-               w = new gdPluginWindowGUI(pPlugin);
-       }
-       else {
-               w = new gdPluginWindow(pPlugin);
-               gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid);
-       }
-       
-       if (pParent->hasWindow(pwid))
-               pParent->delSubWindow(pwid);
-       w->setId(pwid);
-       pParent->addSubWindow(w);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_setBypass()
-{
-       pPlugin->toggleBypass();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdPlugin::cb_setProgram()
-{
-       //pPlugin->setCurrentProgram(program->value());
-       plugin::setProgram(pPlugin, program->value());
-}
-
-
 #endif // #ifdef WITH_VST
index 94e49cd039bdc310138c42003c6eca2f95c15e86..d63ed05d49679532961f33abbe8272d35ccf5a56 100644 (file)
 #ifndef GD_PLUGINLIST_H
 #define GD_PLUGINLIST_H
 
-#include <FL/Fl.H>
-#include <FL/Fl_Scroll.H>
+
 #include "window.h"
 
 
-class Plugin;
+class Fl_Scroll;
 class Channel;
 class geButton;
-class gdPluginList;
-class geIdButton;
-class geChoice;
 
 
 class gdPluginList : public gdWindow
 {
 private:
 
-  geButton  *addPlugin;
-       Fl_Scroll *list;
+       geButton*  addPlugin;
+       Fl_Scrolllist;
 
-       static void cb_addPlugin(Fl_Widget *v, void *p);
+       static void cb_addPlugin(Fl_Widget* v, void* p);
        void cb_addPlugin();
 
 public:
 
-       Channel *ch;      // ch == nullptr ? masterOut
+       Channelch;      // ch == nullptr ? masterOut
        int stackType;
 
-       gdPluginList(int stackType, Channel *ch=nullptr);
+       gdPluginList(int stackType, Channelch=nullptr);
        ~gdPluginList();
 
        /* special callback, passed to browser. When closed (i.e. plugin
@@ -70,41 +66,6 @@ public:
 };
 
 
-/* -------------------------------------------------------------------------- */
-
-
-class gdPlugin : public Fl_Group
-{
-private:
-
-  gdPluginList *pParent;
-  Plugin       *pPlugin;
-
-       static void cb_removePlugin(Fl_Widget *v, void *p);
-       static void cb_openPluginWindow(Fl_Widget *v, void *p);
-       static void cb_setBypass(Fl_Widget *v, void *p);
-       static void cb_shiftUp(Fl_Widget *v, void *p);
-       static void cb_shiftDown(Fl_Widget *v, void *p);
-       static void cb_setProgram(Fl_Widget *v, void *p);
-       void cb_removePlugin();
-       void cb_openPluginWindow();
-       void cb_setBypass();
-       void cb_shiftUp();
-       void cb_shiftDown();
-       void cb_setProgram();
-
-public:
-
-       geIdButton *button;
-       geChoice    *program;
-       geIdButton *bypass;
-       geIdButton *shiftUp;
-       geIdButton *shiftDown;
-       geIdButton *remove;
-
-       gdPlugin(gdPluginList *gdp, Plugin *p, int x, int y, int w);
-};
-
 #endif
 
 #endif // #ifdef WITH_VST
index 60b560c8f06c17de8c0855c3673fae676f4af632..35c9ec6ccfac554c6527fae4a36a6fdd73c741a6 100644 (file)
@@ -36,7 +36,7 @@
 #include "../../core/const.h"
 #include "pluginWindowGUI.h"
 
-#ifdef __APPLE__
+#ifdef G_OS_MAC
 #import "../../utils/cocoa.h" // objective-c
 #endif
 
 using namespace giada::m;
 
 
-gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
- : gdWindow(450, 300), pPlugin(pPlugin)
+gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin)
+ : gdWindow(450, 300), m_plugin(plugin)
 {
   show();
 
-#ifndef __APPLE__
+#ifdef G_OS_LINUX
+
+  /*  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
+  
+  NOTE - we should try to macOS as well. */
+
+  wait_for_expose();
+  Fl::flush();
+
+#endif
+
+#ifndef G_OS_MAC
 
   Fl::check();
 
@@ -58,26 +76,26 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
   gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
     (void*) this, (void*) fl_xid(this));
 
-#if defined(__APPLE__)
+#ifdef G_OS_MAC
 
-  void *cocoaWindow = (void*) fl_xid(this);
-  cocoa_setWindowSize(cocoaWindow, pPlugin->getEditorW(), pPlugin->getEditorH());
-  pPlugin->showEditor(cocoa_getViewFromWindow(cocoaWindow));
+  voidcocoaWindow = (void*) fl_xid(this);
+  cocoa_setWindowSize(cocoaWindow, m_plugin->getEditorW(), m_plugin->getEditorH());
+  m_plugin->showEditor(cocoa_getViewFromWindow(cocoaWindow));
 
 #else
 
-  pPlugin->showEditor((void*) fl_xid(this));
+  m_plugin->showEditor((void*) fl_xid(this));
 
 #endif
 
-  int pluginW = pPlugin->getEditorW();
-  int pluginH = pPlugin->getEditorH();
+  int pluginW = m_plugin->getEditorW();
+  int pluginH = m_plugin->getEditorH();
 
   resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
 
   Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
 
-  copy_label(pPlugin->getName().c_str());
+  copy_label(m_plugin->getName().c_str());
 
 }
 
@@ -85,17 +103,17 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
 /* -------------------------------------------------------------------------- */
 
 
-void gdPluginWindowGUI::cb_close(Fl_Widget *v, void *p)   { ((gdPluginWindowGUI*)p)->__cb_close(); }
-void gdPluginWindowGUI::cb_refresh(void *data) { ((gdPluginWindowGUI*)data)->__cb_refresh(); }
+void gdPluginWindowGUI::cb_close(Fl_Widget* v, void* p) { ((gdPluginWindowGUI*)p)->cb_close(); }
+void gdPluginWindowGUI::cb_refresh(void* data) { ((gdPluginWindowGUI*)data)->cb_refresh(); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void gdPluginWindowGUI::__cb_close()
+void gdPluginWindowGUI::cb_close()
 {
   Fl::remove_timeout(cb_refresh);
-  pPlugin->closeEditor();
+  m_plugin->closeEditor();
   gu_log("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
 }
 
@@ -103,7 +121,7 @@ void gdPluginWindowGUI::__cb_close()
 /* -------------------------------------------------------------------------- */
 
 
-void gdPluginWindowGUI::__cb_refresh()
+void gdPluginWindowGUI::cb_refresh()
 {
   pluginHost::runDispatchLoop();
   Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
@@ -115,7 +133,7 @@ void gdPluginWindowGUI::__cb_refresh()
 
 gdPluginWindowGUI::~gdPluginWindowGUI()
 {
-  __cb_close();
+  cb_close();
 }
 
 #endif // #ifdef WITH_VST
index d563aee6c127d7f55e7cc9ba80377d7c8f46627b..004be476e61569641890dcc374e5416da9cf0794 100644 (file)
@@ -50,16 +50,16 @@ class gdPluginWindowGUI : public gdWindow
 {
 private:
 
-       Plugin *pPlugin;
+       Plugin* m_plugin;
 
-       static void cb_close    (Fl_Widget *v, void *p);
-       static void cb_refresh  (void *data);
-       inline void __cb_close  ();
-       inline void __cb_refresh();
+       static void cb_close  (Fl_Widget* v, void* p);
+       static void cb_refresh(void* data);
+       inline void cb_close  ();
+       inline void cb_refresh();
 
 public:
 
-       gdPluginWindowGUI(Plugin *pPlugin);
+       gdPluginWindowGUI(Plugin* p);
        ~gdPluginWindowGUI();
 };
 
index ee000d3e76f6ee74925009d41d82d90f9e150bd3..4774df2ecbcc74cb6dc17644dd452bc35d32867d 100644 (file)
@@ -28,7 +28,7 @@
 #include <cmath>
 #include <FL/Fl.H>
 #include <FL/Fl_Group.H>
-#include <FL/fl_draw.H>
+//#include <FL/fl_draw.H>
 #include "../../glue/channel.h"
 #include "../../glue/sampleEditor.h"
 #include "../../core/waveFx.h"
@@ -108,7 +108,7 @@ gdSampleEditor::~gdSampleEditor()
   m::conf::sampleEditorH = h();
   m::conf::sampleEditorGridVal = atoi(grid->text());
   m::conf::sampleEditorGridOn  = snap->value();
-  c::sampleEditor::setPreview(ch, G_PREVIEW_NONE);
+  c::sampleEditor::setPreview(ch, PreviewMode::NONE);
 }
 
 
@@ -280,9 +280,9 @@ void gdSampleEditor::cb_togglePreview()
   using namespace giada::c;
 
        if (play->value())
-       sampleEditor::setPreview(ch, G_PREVIEW_NONE);
+       sampleEditor::setPreview(ch, PreviewMode::NONE);
        else
-       sampleEditor::setPreview(ch, loop->value() ? G_PREVIEW_LOOP : G_PREVIEW_NORMAL);
+       sampleEditor::setPreview(ch, loop->value() ? PreviewMode::LOOP : PreviewMode::NORMAL);
 }
 
 
index ee8c954306a933229215244f04fcfba9c7186d53..9a859404df4cfbb12d23735c1fc5a18d3d0ad536 100644 (file)
@@ -35,6 +35,7 @@
 #include "action.h"
 
 
+using namespace giada;
 using namespace giada::m;
 
 
@@ -68,7 +69,7 @@ geAction::geAction(int X, int Y, int H, int frame_a, unsigned index,
         * do that after the possible recording, otherwise we don't know which
         * key_release is associated. */
 
-       if (ch->mode == SINGLE_PRESS && type == G_ACTION_KEYPRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS && type == G_ACTION_KEYPRESS) {
                recorder::action *a2 = nullptr;
                recorder::getNextAction(ch->index, G_ACTION_KEYREL, frame_a, &a2);
                if (a2) {
@@ -99,7 +100,7 @@ void geAction::draw()
        else
                color = G_COLOR_LIGHT_1;
 
-       if (ch->mode == SINGLE_PRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
        }
        else {
@@ -146,7 +147,7 @@ int geAction::handle(int e)
 
                        /* handling of the two margins, left & right. 4 pixels are good enough */
 
-                       if (ch->mode == SINGLE_PRESS) {
+                       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                                onLeftEdge  = false;
                                onRightEdge = false;
                                if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
@@ -179,7 +180,7 @@ void geAction::addAction()
         * theory behind the singleshot.press actions; for any other kind the
         * (b) is just a graphical and meaningless point. */
 
-       if (ch->mode == SINGLE_PRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                recorder::rec(parent->chan->index, G_ACTION_KEYPRESS, frame_a);
                recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_a+4096);
                //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
@@ -205,15 +206,15 @@ void geAction::delAction()
        /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
         * actions. */
 
-       if (ch->mode == SINGLE_PRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                recorder::deleteAction(parent->chan->index, frame_a, G_ACTION_KEYPRESS,
-      false, &mixer::mutex_recs);
+      false, &mixer::mutex);
                recorder::deleteAction(parent->chan->index, frame_b, G_ACTION_KEYREL,
-      false, &mixer::mutex_recs);
+      false, &mixer::mutex);
        }
        else
                recorder::deleteAction(parent->chan->index, frame_a, type, false,
-      &mixer::mutex_recs);
+      &mixer::mutex);
 
   parent->chan->hasActions = recorder::hasActions(parent->chan->index);
 
@@ -249,7 +250,7 @@ void geAction::moveAction(int frame_a)
 
        recorder::rec(parent->chan->index, type, this->frame_a);
 
-       if (ch->mode == SINGLE_PRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                frame_b = xToFrame_b();
                recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_b);
        }
index 5adfc13ab52c42743f6910c9c1006ba4be136a5e..72a23fc0fb3e127efc1a7d9c18c78a25991d2e6c 100644 (file)
@@ -39,6 +39,7 @@
 extern gdMainWindow *G_MainWin;
 
 
+using namespace giada;
 using namespace giada::m;
 
 
@@ -69,8 +70,8 @@ geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChan
 
       if ((action->chan != pParent->chan->index)                          ||
           (recorder::frames.at(i) > clock::getFramesInLoop())             ||
-          (action->type == G_ACTION_KILL && ch->mode == SINGLE_PRESS)     ||
-          (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS)   ||
+          (action->type == G_ACTION_KILL && ch->mode == ChannelMode::SINGLE_PRESS)     ||
+          (action->type == G_ACTION_KEYREL && ch->mode == ChannelMode::SINGLE_PRESS)   ||
           (action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL))
       )
         continue;
@@ -123,7 +124,7 @@ void geActionEditor::updateActions()
                a = (geAction*)child(i);
                int newX = x() + (a->frame_a / pParent->zoom);
 
-               if (ch->mode == SINGLE_PRESS) {
+               if (ch->mode == ChannelMode::SINGLE_PRESS) {
                        int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
                        if (newW < geAction::MIN_WIDTH)
                                newW = geAction::MIN_WIDTH;
@@ -328,7 +329,7 @@ int geActionEditor::handle(int e)
                        bool noChanges = false;
                        if (actionOriginalX == selected->x())
                                noChanges = true;
-                       if (ch->mode == SINGLE_PRESS &&
+                       if (ch->mode == ChannelMode::SINGLE_PRESS &&
                                        actionOriginalX+actionOriginalW != selected->x()+selected->w())
                                noChanges = false;
 
@@ -352,7 +353,7 @@ int geActionEditor::handle(int e)
 
                                int action_x  = ((geAction*)child(i))->x();
                                int action_w  = ((geAction*)child(i))->w();
-                               if (ch->mode == SINGLE_PRESS) {
+                               if (ch->mode == ChannelMode::SINGLE_PRESS) {
 
                                        /* when 2 segments overlap?
                                         * start = the highest value between the two starting points
@@ -413,7 +414,7 @@ bool geActionEditor::actionCollides(int frame)
                if (((geAction*) child(i))->frame_a == frame)
                        collision = true;
 
-       if (ch->mode == SINGLE_PRESS) {
+       if (ch->mode == ChannelMode::SINGLE_PRESS) {
                for (int i=0; i<children() && !collision; i++) {
                        geAction *c = ((geAction*) child(i));
                        if (frame <= c->frame_b && frame >= c->frame_a)
index 22b97d5ab0db08c0796f88e68ac0a45ca1ef9aa5..4e032a82b04d04c19100f5cd84371d619439c0b4 100644 (file)
@@ -229,7 +229,7 @@ int geEnvelopeEditor::handle(int e) {
                                        }
                                        else {
                                                recorder::deleteAction(pParent->chan->index,
-              points.at(selectedPoint).frame, type, false, &mixer::mutex_recs);
+              points.at(selectedPoint).frame, type, false, &mixer::mutex);
             pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
             recorder::sortActions();
                                                points.erase(points.begin() + selectedPoint);
@@ -268,7 +268,7 @@ int geEnvelopeEditor::handle(int e) {
                                        /*  delete previous point and record a new one */
 
                                        recorder::deleteAction(pParent->chan->index,
-            points.at(draggedPoint).frame, type, false, &mixer::mutex_recs);
+            points.at(draggedPoint).frame, type, false, &mixer::mutex);
           pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
 
                                        if (range == G_RANGE_FLOAT) {
index 9ad12840dcc72063fd6687ddf8e1d844b5ca22b9..5ee61f26ebfa313d9405cbced52fbd1c605c59c0 100644 (file)
@@ -287,9 +287,9 @@ int geMuteEditor::handle(int e) {
                                        //              a, b, points.at(a).frame, points.at(b).frame);
 
                                        recorder::deleteAction(pParent->chan->index, points.at(a).frame,
-          points.at(a).type, false, &mixer::mutex_recs); // false = don't check vals
+          points.at(a).type, false, &mixer::mutex); // false = don't check vals
                                        recorder::deleteAction(pParent->chan->index,    points.at(b).frame,
-          points.at(b).type, false, &mixer::mutex_recs); // false = don't check vals
+          points.at(b).type, false, &mixer::mutex); // false = don't check vals
           pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
 
           recorder::sortActions();
@@ -316,7 +316,7 @@ int geMuteEditor::handle(int e) {
 
                                        recorder::deleteAction(pParent->chan->index,
             points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
-            &mixer::mutex_recs);  // don't check values
+            &mixer::mutex);  // don't check values
           pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
 
                                        recorder::rec(
index acb6442199305d2b6d847b026b5bf0924c58d529..01e787ff1f539ccae6cde6e0e15248e6d2404c6e 100644 (file)
@@ -117,9 +117,9 @@ void gePianoItem::removeAction()
 {
        MidiChannel* ch = static_cast<MidiChannel*>(pParent->chan);
        recorder::deleteAction(ch->index, a.frame, G_ACTION_MIDI, true,
-               &mixer::mutex_recs, a.iValue, 0.0);
+               &mixer::mutex, a.iValue, 0.0);
        recorder::deleteAction(ch->index, b.frame, G_ACTION_MIDI, true,
-               &mixer::mutex_recs, b.iValue, 0.0);
+               &mixer::mutex, b.iValue, 0.0);
 
        /* Send a note-off in case we are deleting it in a middle of a key_on/key_off
        sequence. */
index fc3290969afbecc5d475320931f7f82eeb80a606..261d3b7f525e902562eb96d77a231e5782dc44a6 100644 (file)
@@ -84,8 +84,8 @@ int gePianoItemOrphaned::handle(int e)
 void gePianoItemOrphaned::remove()
 {
   MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
-  recorder::deleteAction(ch->index, frame, G_ACTION_MIDI, true,
-    &mixer::mutex_recs, event, 0.0);
+  recorder::deleteAction(ch->index, frame, G_ACTION_MIDI, true, &mixer::mutex, 
+    event, 0.0);
   hide();   // for Windows
   Fl::delete_widget(this);
   ch->hasActions = recorder::hasActions(ch->index);
index 48db620e6b782b4bf0fe0a34eb545013fb03aa0d..83b37e2f9baa1e053d41543013f4bba62f3bf9a3 100644 (file)
@@ -49,10 +49,9 @@ extern gdMainWindow* G_MainWin;
 using namespace giada;
 
 
-geChannel::geChannel(int X, int Y, int W, int H, int type, Channel* ch)
+geChannel::geChannel(int X, int Y, int W, int H, Channel* ch)
  : Fl_Group(X, Y, W, H, nullptr),
-        ch      (ch),
-        type    (type)
+        ch      (ch)
 {
 }
 
@@ -149,38 +148,39 @@ void geChannel::blink()
 
 /* -------------------------------------------------------------------------- */
 
-
-void geChannel::setColorsByStatus(int playStatus, int recStatus)
+void geChannel::setColorsByStatus(ChannelStatus playStatus, ChannelStatus recStatus)
 {
        switch (playStatus) {
-               case STATUS_OFF:
-               case STATUS_EMPTY:
+               case ChannelStatus::OFF:
+               case ChannelStatus::EMPTY:
                        mainButton->setDefaultMode();
                        button->imgOn  = channelPlay_xpm;
                        button->imgOff = channelStop_xpm;
                        button->redraw();
                        break;
-               case STATUS_PLAY:
+               case ChannelStatus::PLAY:
                        mainButton->setPlayMode();
                        button->imgOn  = channelStop_xpm;
                        button->imgOff = channelPlay_xpm;
                        button->redraw();
                        break;
-               case STATUS_WAIT:
+               case ChannelStatus::WAIT:
                        blink();
                        break;
-               case STATUS_ENDING:
+               case ChannelStatus::ENDING:
                        mainButton->setEndingMode();
                        break;
+               default: break;
        }
 
        switch (recStatus) {
-               case REC_WAITING:
+               case ChannelStatus::WAIT:
                        blink();
                        break;
-               case REC_ENDING:
+               case ChannelStatus::ENDING:
                        mainButton->setEndingMode();
                        break;
+               default: break;
        }
 }
 
index 8a3b6bf738707d790ccfd46edbdb9cd7fae90fa8..7bff681db78839b7bb908889143e130572f20032 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <FL/Fl_Group.H>
+#include "../../../../core/types.h"
 
 
 class Channel;
@@ -86,7 +87,7 @@ protected:
        /* setColorByStatus
         * update colors depending on channel status. */
 
-       void setColorsByStatus(int playStatus, int recStatus);
+       void setColorsByStatus(giada::ChannelStatus chan, giada::ChannelStatus rec);
 
        /* handleKey
         * method wrapped by virtual handle(int e). */
@@ -100,7 +101,7 @@ protected:
 
 public:
 
-       geChannel(int x, int y, int w, int h, int type, Channel* ch);
+       geChannel(int x, int y, int w, int h, Channel* ch);
 
        /* reset
         * reset channel to initial status. */
@@ -148,8 +149,6 @@ public:
 #ifdef WITH_VST
        geStatusButton*  fx;
 #endif
-
-       int type;
 };
 
 
index 045be52f3e0eb5a04303c1d47555c6772f07d0d7..44f8a08cba54399f442465ff8f009daf8a00d1e9 100644 (file)
@@ -1,4 +1,4 @@
-/* -----------------------------------------------------------------------------
+ /* -----------------------------------------------------------------------------
  *
  * Giada - Your Hardcore Loopmachine
  *
@@ -36,6 +36,9 @@
 #include "channelMode.h"
 
 
+using namespace giada;
+
+
 geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch,
   const char *L)
   : Fl_Menu_Button(x, y, w, h, L), ch(ch)
@@ -45,14 +48,14 @@ geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch,
   textcolor(G_COLOR_LIGHT_2);
   color(G_COLOR_GREY_2);
 
-  add("Loop . basic",      0, cb_changeMode, (void *)LOOP_BASIC);
-  add("Loop . once",       0, cb_changeMode, (void *)LOOP_ONCE);
-  add("Loop . once . bar", 0, cb_changeMode, (void *)LOOP_ONCE_BAR);
-  add("Loop . repeat",     0, cb_changeMode, (void *)LOOP_REPEAT);
-  add("Oneshot . basic",   0, cb_changeMode, (void *)SINGLE_BASIC);
-  add("Oneshot . press",   0, cb_changeMode, (void *)SINGLE_PRESS);
-  add("Oneshot . retrig",  0, cb_changeMode, (void *)SINGLE_RETRIG);
-  add("Oneshot . endless", 0, cb_changeMode, (void *)SINGLE_ENDLESS);
+  add("Loop . basic",      0, cb_changeMode, (void*) ChannelMode::LOOP_BASIC);
+  add("Loop . once",       0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE);
+  add("Loop . once . bar", 0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE_BAR);
+  add("Loop . repeat",     0, cb_changeMode, (void*) ChannelMode::LOOP_REPEAT);
+  add("Oneshot . basic",   0, cb_changeMode, (void*) ChannelMode::SINGLE_BASIC);
+  add("Oneshot . press",   0, cb_changeMode, (void*) ChannelMode::SINGLE_PRESS);
+  add("Oneshot . retrig",  0, cb_changeMode, (void*) ChannelMode::SINGLE_RETRIG);
+  add("Oneshot . endless", 0, cb_changeMode, (void*) ChannelMode::SINGLE_ENDLESS);
 }
 
 
@@ -62,28 +65,28 @@ geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch,
 void geChannelMode::draw() {
   fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);    // border
   switch (ch->mode) {
-    case LOOP_BASIC:
+    case ChannelMode::LOOP_BASIC:
       fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1);
       break;
-    case LOOP_ONCE:
+    case ChannelMode::LOOP_ONCE:
       fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1);
       break;
-    case LOOP_ONCE_BAR:
+    case ChannelMode::LOOP_ONCE_BAR:
       fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1);
       break;
-    case LOOP_REPEAT:
+    case ChannelMode::LOOP_REPEAT:
       fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1);
       break;
-    case SINGLE_BASIC:
+    case ChannelMode::SINGLE_BASIC:
       fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1);
       break;
-    case SINGLE_PRESS:
+    case ChannelMode::SINGLE_PRESS:
       fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1);
       break;
-    case SINGLE_RETRIG:
+    case ChannelMode::SINGLE_RETRIG:
       fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1);
       break;
-    case SINGLE_ENDLESS:
+    case ChannelMode::SINGLE_ENDLESS:
       fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1);
       break;
   }
@@ -101,7 +104,7 @@ void geChannelMode::cb_changeMode(Fl_Widget *v, void *p) { ((geChannelMode*)v)->
 
 void geChannelMode::__cb_changeMode(int mode)
 {
-  ch->mode = 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
index 6d95b4699da55178482e87220636fcd82edb9426..cc9f2d89c190ee6e2fd09347746348204436f8e1 100644 (file)
@@ -34,6 +34,7 @@
 #include "channelStatus.h"
 
 
+using namespace giada;
 using namespace giada::m;
 
 
@@ -50,33 +51,37 @@ 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) {
-    if (ch->status    & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) ||
-        ch->recStatus & (REC_WAITING | REC_ENDING))
-    {
-      fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
-    }
-    else
-    if (ch->status == STATUS_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::active && recorder::canRec(ch, clock::isRunning(), mixer::recording))
-      fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE);     // action record
-
-    /* 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);
+  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::active && recorder::canRec(ch, clock::isRunning(), mixer::recording))
+    fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE);     // action record
+
+  /* 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);
+
 }
index 0f39ec5ebb404e7ab833a8dc6928b1219d719ad6..7bfdf49f3f06d9ec5fec168e572c333c29242eb7 100644 (file)
@@ -112,7 +112,7 @@ int geColumn::handle(int e)
                        for (string& path : paths) {
                                gu_log("[geColumn::handle] loading %s...\n", path.c_str());
                                SampleChannel* c = static_cast<SampleChannel*>(c::channel::addChannel(
-                                       m_index, G_CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
+                                       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);
@@ -217,7 +217,7 @@ geChannel* geColumn::addChannel(Channel* ch, int size)
        /* All geChannels are added with y=0. That's not a problem, they will be 
        repositioned later on during geColumn::resize(). */
 
-       if (ch->type == G_CHANNEL_SAMPLE)
+       if (ch->type == ChannelType::SAMPLE)
                gch = new geSampleChannel(x(), 0, w(), size, static_cast<SampleChannel*>(ch));
        else
                gch = new geMidiChannel(x(), 0, w(), size, static_cast<MidiChannel*>(ch));
@@ -254,17 +254,7 @@ void geColumn::deleteChannel(geChannel* gch)
 void geColumn::__cb_addChannel()
 {
        gu_log("[geColumn::__cb_addChannel] m_index = %d\n", m_index);
-       int type = openTypeMenu();
-       if (type)
-               c::channel::addChannel(m_index, type, G_GUI_CHANNEL_H_1);
-}
-
-
-/* -------------------------------------------------------------------------- */
 
-
-int geColumn::openTypeMenu()
-{
        Fl_Menu_Item rclick_menu[] = {
                {"Sample channel"},
                {"MIDI channel"},
@@ -278,16 +268,16 @@ int geColumn::openTypeMenu()
        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 0;
+       if (!m) return;
 
        if (strcmp(m->label(), "Sample channel") == 0)
-               return G_CHANNEL_SAMPLE;
-       if (strcmp(m->label(), "MIDI channel") == 0)
-               return G_CHANNEL_MIDI;
-       return 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);
 }
 
 
+
 /* -------------------------------------------------------------------------- */
 
 
index 47f31bedb7cd67a39335a3b4bd348c4ffb5793d9..582b052c77645d598adfaa3c16c789ce76d11cb0 100644 (file)
@@ -46,8 +46,6 @@ private:
        static void cb_addChannel  (Fl_Widget* v, void* p);
        inline void __cb_addChannel();
 
-       int openTypeMenu();
-
        geButton*     m_addChannelBtn;
        geResizerBar* m_resizer;
        geKeyboard*   m_parent;
index 9f913e77a7cb179de88115064cfe06c5ba4ad925..fb3860446b8d9a94edc6dd80fe5be07fc3168121 100644 (file)
@@ -148,7 +148,7 @@ void menuCallback(Fl_Widget* w, void* v)
 
 
 geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel* ch)
-       : geChannel(X, Y, W, H, G_CHANNEL_MIDI, ch)
+       : geChannel(X, Y, W, H, ch)
 {
        begin();
 
@@ -217,7 +217,7 @@ void geMidiChannel::cb_button()
        using namespace giada;
        
        if (button->value())
-               c::io::keyPress(static_cast<MidiChannel*>(ch), Fl::event_ctrl(), Fl::event_shift());
+               c::io::keyPress(static_cast<MidiChannel*>(ch), Fl::event_ctrl(), Fl::event_shift(), 0);
 }
 
 
index d477915c40394ce957c5b0821668304e9a414fb3..b97d0f867ba8fbdac1702431a79b4baf885f7260 100644 (file)
@@ -61,6 +61,9 @@
 extern gdMainWindow* G_MainWin;
 
 
+using namespace giada;
+
+
 namespace
 {
 enum class Menu
@@ -208,7 +211,7 @@ void menuCallback(Fl_Widget* w, void* v)
 
 
 geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch)
-       : geChannel(X, Y, W, H, G_CHANNEL_SAMPLE, ch)
+       : geChannel(X, Y, W, H, ch)
 {
        begin();
 
@@ -327,7 +330,9 @@ void geSampleChannel::cb_openMenu()
                {0}
        };
 
-       if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
+       if (ch->status == ChannelStatus::EMPTY || 
+                 ch->status == ChannelStatus::MISSING) 
+       {
                rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate();
                rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate();
                rclick_menu[(int) Menu::FREE_CHANNEL].deactivate();
@@ -341,7 +346,7 @@ void geSampleChannel::cb_openMenu()
        /* No 'clear start/stop actions' for those channels in loop mode: they cannot
        have start/stop actions. */
 
-       if (static_cast<SampleChannel*>(ch)->mode & LOOP_ANY)
+       if (static_cast<SampleChannel*>(ch)->isAnyLoopMode())
                rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate();
 
        Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50);
@@ -363,7 +368,7 @@ void geSampleChannel::cb_openMenu()
 void geSampleChannel::cb_readActions()
 {
        using namespace giada::c::channel;
-       toggleReadingRecs(static_cast<SampleChannel*>(ch));
+       toggleReadingActions(static_cast<SampleChannel*>(ch));
 }
 
 
@@ -412,11 +417,11 @@ void geSampleChannel::update()
        const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
 
        switch (sch->status) {
-               case STATUS_EMPTY:
+               case ChannelStatus::EMPTY:
                        mainButton->label("-- no sample --");
                        break;
-               case STATUS_MISSING:
-               case STATUS_WRONG:
+               case ChannelStatus::MISSING:
+               case ChannelStatus::WRONG:
                        mainButton->label("* file not found! *");
                        break;
                default:
@@ -436,7 +441,7 @@ void geSampleChannel::update()
        else
                hideActionButton();
 
-       modeBox->value(sch->mode);
+       modeBox->value(static_cast<int>(sch->mode));
        modeBox->redraw();
 
        vol->value(sch->volume);
index ced6cb81786601d165b0e36bac210532b27ffc0d..5a165dc79d1d27b681583bcb40894601ecc563b9 100644 (file)
@@ -39,7 +39,7 @@
 #include "../../elems/basics/boxtypes.h"
 #include "../../elems/basics/button.h"
 #include "../../dialogs/gd_mainWindow.h"
-#include "../../dialogs/gd_about.h"
+#include "../../dialogs/about.h"
 #include "../../dialogs/gd_config.h"
 #include "../../dialogs/gd_warnings.h"
 #include "../../dialogs/browser/browserLoad.h"
@@ -177,17 +177,17 @@ void geMainMenu::__cb_edit()
 
        menu[1].deactivate();
 
-       for (unsigned i=0; i<mixer::channels.size(); i++)
-               if (mixer::channels.at(i)->hasActions) {
+       for (const Channel* ch : mixer::channels)
+               if (ch->hasActions) {
                        menu[1].activate();
                        break;
                }
-       for (unsigned i=0; i<mixer::channels.size(); i++)
-               if (mixer::channels.at(i)->type == G_CHANNEL_SAMPLE)
-                       if (((SampleChannel*)mixer::channels.at(i))->wave != nullptr) {
-                               menu[0].activate();
-                               break;
-                       }
+
+       for (const Channel* ch : 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);
index 7a4bf3fee951497b3d9dc183b55796825cf1463f..053fa895c7b11f204b313b0e249456e04aa55938 100644 (file)
@@ -72,12 +72,12 @@ geMainTimer::geMainTimer(int x, int y)
        divider->callback(cb_divider, (void*)this);
 
        quantizer->add("off", 0, cb_quantizer, (void*)this);
-       quantizer->add("1b",  0, cb_quantizer, (void*)this);
-       quantizer->add("2b",  0, cb_quantizer, (void*)this);
-       quantizer->add("3b",  0, cb_quantizer, (void*)this);
-       quantizer->add("4b",  0, cb_quantizer, (void*)this);
-       quantizer->add("6b",  0, cb_quantizer, (void*)this);
-       quantizer->add("8b",  0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/1", 0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/2", 0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/3", 0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/4", 0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/6", 0, cb_quantizer, (void*)this);
+       quantizer->add("1\\/8", 0, cb_quantizer, (void*)this);
        quantizer->value(0); //  "off" by default
 }
 
@@ -85,17 +85,17 @@ geMainTimer::geMainTimer(int x, int y)
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::cb_bpm       (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_bpm(); }
-void geMainTimer::cb_meter     (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_meter(); }
-void geMainTimer::cb_quantizer (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_quantizer(); }
-void geMainTimer::cb_multiplier(Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_multiplier(); }
-void geMainTimer::cb_divider   (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_divider(); }
+void geMainTimer::cb_bpm       (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_bpm(); }
+void geMainTimer::cb_meter     (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_meter(); }
+void geMainTimer::cb_quantizer (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_quantizer(); }
+void geMainTimer::cb_multiplier(Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_multiplier(); }
+void geMainTimer::cb_divider   (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_divider(); }
 
 
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::__cb_bpm()
+void geMainTimer::cb_bpm()
 {
        gu_openSubWindow(G_MainWin, new gdBpmInput(bpm->label()), WID_BPM);
 }
@@ -104,7 +104,7 @@ void geMainTimer::__cb_bpm()
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::__cb_meter()
+void geMainTimer::cb_meter()
 {
        gu_openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS);
 }
@@ -113,7 +113,7 @@ void geMainTimer::__cb_meter()
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::__cb_quantizer()
+void geMainTimer::cb_quantizer()
 {
        glue_quantize(quantizer->value());
 }
@@ -122,7 +122,7 @@ void geMainTimer::__cb_quantizer()
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::__cb_multiplier()
+void geMainTimer::cb_multiplier()
 {
        glue_beatsMultiply();
 }
@@ -131,7 +131,7 @@ void geMainTimer::__cb_multiplier()
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::__cb_divider()
+void geMainTimer::cb_divider()
 {
        glue_beatsDivide();
 }
@@ -140,7 +140,7 @@ void geMainTimer::__cb_divider()
 /* -------------------------------------------------------------------------- */
 
 
-void geMainTimer::setBpm(const char *v)
+void geMainTimer::setBpm(const charv)
 {
        bpm->copy_label(v);
 }
@@ -175,6 +175,15 @@ void geMainTimer::setLock(bool v)
 /* -------------------------------------------------------------------------- */
 
 
+void geMainTimer::setQuantizer(int q)
+{
+       quantizer->value(q);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
 void geMainTimer::setMeter(int beats, int bars)
 {
        string tmp = gu_iToString(beats) + "/" + gu_iToString(bars);
index c3fe66168248bf2b0723d412c8cc6e0d3f2d7e03..917c05450cb98801e37ec727db19c3887b4ef86d 100644 (file)
@@ -40,31 +40,31 @@ 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();
+       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 setBpm(const char *v);
+       void setBpm(const charv);
        void setBpm(float v);
        void setMeter(int beats, int bars);
+       void setQuantizer(int q);
 
   /* setLock
   Locks bpm, beter and multipliers. Used during audio recordings. */
diff --git a/src/gui/elems/plugin/pluginElement.cpp b/src/gui/elems/plugin/pluginElement.cpp
new file mode 100644 (file)
index 0000000..c2ad697
--- /dev/null
@@ -0,0 +1,211 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <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/idButton.h"
+#include "../../elems/basics/choice.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../../dialogs/pluginList.h"
+#include "../../dialogs/pluginWindowGUI.h"
+#include "../../dialogs/pluginWindow.h"
+#include "pluginElement.h"
+
+
+extern gdMainWindow* G_MainWin;
+
+
+using std::string;
+using namespace giada;
+
+
+gePluginElement::gePluginElement(gdPluginList* gdp, Plugin* p, int X, int Y, int W)
+       : Fl_Group   (X, Y, W, 20), 
+         m_parentWin(gdp), 
+         m_plugin    (p)
+{
+       begin();
+       button    = new geIdButton(8, y(), 220, 20);
+       program   = new geChoice(button->x()+button->w()+4, y(), 132, 20);
+       bypass    = new geIdButton(program->x()+program->w()+4, y(), 20, 20);
+       shiftUp   = new geIdButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
+       shiftDown = new geIdButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
+       remove    = new geIdButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
+       end();
+
+       button->copy_label(m_plugin->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(gu_removeFltkChars(m_plugin->getProgramName(i)).c_str());
+
+       if (program->size() == 0) {
+               program->add("-- no programs --\0");
+               program->deactivate();
+       }
+       else
+               program->value(m_plugin->getCurrentProgram());
+
+       bypass->callback(cb_setBypass, (void*)this);
+       bypass->type(FL_TOGGLE_BUTTON);
+       bypass->value(m_plugin->isBypassed() ? 0 : 1);
+
+       shiftUp->callback(cb_shiftUp, (void*)this);
+       shiftDown->callback(cb_shiftDown, (void*)this);
+       remove->callback(cb_removePlugin, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginElement::cb_removePlugin    (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_removePlugin(); }
+void gePluginElement::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_openPluginWindow(); }
+void gePluginElement::cb_setBypass       (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
+void gePluginElement::cb_shiftUp         (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
+void gePluginElement::cb_shiftDown       (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
+void gePluginElement::cb_setProgram      (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+
+       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();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+
+       unsigned pluginIndex = m::pluginHost::getPluginIndex(m_plugin->getId(), m_parentWin->stackType, m_parentWin->ch);
+       unsigned stackSize   = (m::pluginHost::getStack(m_parentWin->stackType, m_parentWin->ch))->size();
+
+       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();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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).*/
+       
+       m_parentWin->delSubWindow(m_plugin->getId() + 1);
+       c::plugin::freePlugin(m_parentWin->ch, m_plugin->getId(), m_parentWin->stackType);
+       m_parentWin->refreshList();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginElement::cb_openPluginWindow()
+{
+       /* The new pluginWindow has id = id_plugin + 1, because id=0 is reserved for 
+       the parent window 'add plugin' (i.e. this). */
+
+       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);
+       }
+       else {
+               w = new gdPluginWindow(m_plugin);
+               gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid);
+       }
+       
+       if (m_parentWin->hasWindow(pwid))
+               m_parentWin->delSubWindow(pwid);
+       w->setId(pwid);
+       m_parentWin->addSubWindow(w);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginElement::cb_setBypass()
+{
+       m_plugin->toggleBypass();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginElement::cb_setProgram()
+{
+       c::plugin::setProgram(m_plugin, program->value());
+}
+
+
+#endif // #ifdef WITH_VST
diff --git a/src/gui/elems/plugin/pluginElement.h b/src/gui/elems/plugin/pluginElement.h
new file mode 100644 (file)
index 0000000..317cd87
--- /dev/null
@@ -0,0 +1,78 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#ifndef GE_PLUGIN_ELEMENT_H
+#define GE_PLUGIN_ELEMENT_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class gdPluginList;
+class Plugin;
+class geIdButton;
+class geChoice;
+
+
+class gePluginElement : public Fl_Group
+{
+private:
+
+       gdPluginList* m_parentWin;
+       Plugin*       m_plugin;
+
+       static void cb_removePlugin(Fl_Widget* v, void* p);
+       static void cb_openPluginWindow(Fl_Widget* v, void* p);
+       static void cb_setBypass(Fl_Widget* v, void* p);
+       static void cb_shiftUp(Fl_Widget* v, void* p);
+       static void cb_shiftDown(Fl_Widget* v, void* p);
+       static void cb_setProgram(Fl_Widget* v, void* p);
+       void cb_removePlugin();
+       void cb_openPluginWindow();
+       void cb_setBypass();
+       void cb_shiftUp();
+       void cb_shiftDown();
+       void cb_setProgram();
+
+public:
+
+       geIdButton* button;
+       geChoice*   program;
+       geIdButton* bypass;
+       geIdButton* shiftUp;
+       geIdButton* shiftDown;
+       geIdButton* remove;
+
+       gePluginElement(gdPluginList* gdp, Plugin* p, int x, int y, int w);
+};
+
+#endif
+
+#endif // #ifdef WITH_VST
index 84a2824d92866e5e97efb12aa24c93f1ebc8b8b8..7fdc453646a28da9fb4f6ef9c3e3730ef262e85a 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#include "../../../core/const.h"
-#ifdef G_OS_MAC  // our Clang still doesn't know about cstdint (c++11 stuff)
-       #include <stdint.h>
-#else
-       #include <cstdint>
-#endif
+#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 "waveform.h"
 #include "waveTools.h"
 
@@ -221,7 +217,7 @@ void geWaveTools::openMenu()
                {0}
        };
 
-       if (ch->status == STATUS_PLAY) {
+       if (ch->status == ChannelStatus::PLAY) {
                menu[(int)Menu::CUT].deactivate();
                menu[(int)Menu::TRIM].deactivate();
        }
diff --git a/src/utils/deps.cpp b/src/utils/deps.cpp
deleted file mode 100644 (file)
index fc58378..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../core/const.h"
-#ifdef G_OS_MAC
-       #include <RtMidi.h>
-#else
-       #include <rtmidi/RtMidi.h>
-#endif
-#include <sndfile.h>
-#include "../deps/rtaudio-mod/RtAudio.h"
-#include "deps.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace u     {
-namespace deps  
-{
-string getLibsndfileVersion()
-{
-  char buffer[128];
-  sf_command(NULL, SFC_GET_LIB_VERSION, buffer, sizeof(buffer));
-  return string(buffer);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string getRtAudioVersion()
-{
-  return RtAudio::getVersion();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string getRtMidiVersion()
-{
-  return RtMidi::getVersion();
-}
-
-}}};  // giada::u::deps::
\ No newline at end of file
diff --git a/src/utils/deps.h b/src/utils/deps.h
deleted file mode 100644 (file)
index e978b3b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_UTILS_DEPS_H
-#define G_UTILS_DEPS_H
-
-
-#include <string>
-
-
-namespace giada {
-namespace u     {
-namespace deps 
-{
-std::string getLibsndfileVersion();
-std::string getRtAudioVersion();
-std::string getRtMidiVersion();
-}}};  // giada::u::deps::
-
-#endif
\ No newline at end of file
index ec4de69e245f1035063e62033ddddc18fbac9895..39a856eafe96cd31f155f3af474d40d926a3c2d2 100644 (file)
@@ -123,6 +123,7 @@ void gu_updateControls()
 
        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::metronome);
diff --git a/src/utils/ver.cpp b/src/utils/ver.cpp
new file mode 100644 (file)
index 0000000..c22089f
--- /dev/null
@@ -0,0 +1,97 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../core/const.h"
+#ifdef G_OS_MAC
+       #include <RtMidi.h>
+#else
+       #include <rtmidi/RtMidi.h>
+#endif
+#include <sndfile.h>
+#include "../deps/rtaudio-mod/RtAudio.h"
+#include "ver.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace u     {
+namespace ver  
+{
+string getLibsndfileVersion()
+{
+  char buffer[128];
+  sf_command(NULL, SFC_GET_LIB_VERSION, buffer, sizeof(buffer));
+  return string(buffer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtAudioVersion()
+{
+#ifdef TESTS
+               return "";
+#else
+  return RtAudio::getVersion();
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtMidiVersion()
+{
+#ifdef TESTS
+               return "";
+#else
+  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
diff --git a/src/utils/ver.h b/src/utils/ver.h
new file mode 100644 (file)
index 0000000..a8322f4
--- /dev/null
@@ -0,0 +1,46 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 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_VER_H
+#define G_UTILS_VER_H
+
+
+#include <string>
+
+
+namespace giada {
+namespace u     {
+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
index 254941b6bcc437dfa582d26321939db6ce32cf92..daf869c37397805208e30ce9ee748cdfeca64a66 100644 (file)
@@ -3,7 +3,7 @@
 #include <catch.hpp>
 
 
-TEST_CASE("Test AudioBuffer class")
+TEST_CASE("AudioBuffer")
 {
        using namespace giada::m;
 
@@ -19,7 +19,7 @@ TEST_CASE("Test AudioBuffer class")
        {
                SECTION("test mono")
                {
-                       REQUIRE(buffer.alloc(BUFFER_SIZE, 1) == 1);
+                       buffer.alloc(BUFFER_SIZE, 1);
                        REQUIRE(buffer.countFrames() == BUFFER_SIZE);
                        REQUIRE(buffer.countSamples() == BUFFER_SIZE);
                        REQUIRE(buffer.countChannels() == 1);
@@ -34,7 +34,7 @@ TEST_CASE("Test AudioBuffer class")
 
                SECTION("test odd channels count")
                {
-                       REQUIRE(buffer.alloc(BUFFER_SIZE, 7) == 1);
+                       buffer.alloc(BUFFER_SIZE, 7);
                        REQUIRE(buffer.countFrames() == BUFFER_SIZE);
                        REQUIRE(buffer.countSamples() == BUFFER_SIZE * 7);
                        REQUIRE(buffer.countChannels() == 7);
index df9113c81678512ff190045e63f3f74772bb138a..a51f216f050e4fa752d83cc88b9adfd0de0551ee 100644 (file)
@@ -7,7 +7,7 @@ using std::string;
 using namespace giada::m;
 
 
-TEST_CASE("Test Conf class")
+TEST_CASE("conf")
 {
   conf::init();
   
index 37c527d3051c9bd84f4a002038f7d4c5b4488586..dc20323a2e4c44b0af13f95e801d1654de170bb4 100644 (file)
@@ -1,3 +1,9 @@
 #define CATCH_CONFIG_MAIN
 #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. */
+
+class gdMainWindow* G_MainWin;
+bool G_quit;
\ No newline at end of file
index 5903fca4cd7eb1767f99cbf4c7f6c60b75b5e5de..f3f2394ad29cd0802bac539ba353ddd3a07fe39e 100644 (file)
@@ -7,7 +7,7 @@ using std::string;
 using namespace giada::m;
 
 
-TEST_CASE("Test MidiMapConf")
+TEST_CASE("midiMapConf")
 {
   SECTION("test default values")
   {
index 8e302bd1c1637d82cb504c48b69b0e769e171055..3c52f4efe850006e4b4951daac18f3726284a551 100644 (file)
@@ -1,21 +1,23 @@
 #include "../src/core/patch.h"
 #include "../src/core/const.h"
+#include "../src/core/types.h"
 #include <catch.hpp>
 
 
 using std::string;
 using std::vector;
+using namespace giada;
 using namespace giada::m;
 
 
-TEST_CASE("Test Patch class")
+TEST_CASE("patch")
 {
        string filename = "./test-patch.json";
 
        SECTION("test write")
        {
+               patch::action_t  action0;
                patch::action_t  action1;
-               patch::action_t  action2;
                patch::channel_t channel1;
                patch::column_t  column;
 #ifdef WITH_VST
@@ -24,16 +26,16 @@ TEST_CASE("Test Patch class")
                patch::plugin_t  plugin3;
 #endif
 
-               action1.type   = 0;
-               action1.frame  = 50000;
-               action1.fValue = 0.3f;
-               action1.iValue = 1000;
-               action2.type   = 2;
-               action2.frame  = 589;
-               action2.fValue = 1.0f;
-               action2.iValue = 130;
+               action0.type   = 0;
+               action0.frame  = 50000;
+               action0.fValue = 0.3f;
+               action0.iValue = 1000;
+               action1.type   = 2;
+               action1.frame  = 589;
+               action1.fValue = 1.0f;
+               action1.iValue = 130;
+               channel1.actions.push_back(action0);
                channel1.actions.push_back(action1);
-               channel1.actions.push_back(action2);
 
 #ifdef WITH_VST
                plugin1.path   = "/path/to/plugin1";
@@ -55,12 +57,11 @@ TEST_CASE("Test Patch class")
                channel1.plugins.push_back(plugin2);
 #endif
 
-               channel1.type              = G_CHANNEL_SAMPLE;
+               channel1.type              = static_cast<int>(ChannelType::SAMPLE);
                channel1.index             = 666;
                channel1.size              = G_GUI_CHANNEL_H_1;
                channel1.column            = 0;
                channel1.mute              = 0;
-               channel1.mute_s            = 0;
                channel1.solo              = 0;
                channel1.volume            = 1.0f;
                channel1.pan               = 0.5f;
@@ -144,12 +145,11 @@ TEST_CASE("Test Patch class")
                REQUIRE(column0.width == 500);
 
                patch::channel_t channel0 = patch::channels.at(0);
-               REQUIRE(channel0.type == G_CHANNEL_SAMPLE);
+               REQUIRE(channel0.type == static_cast<int>(ChannelType::SAMPLE));
                REQUIRE(channel0.index == 666);
                REQUIRE(channel0.size == G_GUI_CHANNEL_H_1);
                REQUIRE(channel0.column == 0);
                REQUIRE(channel0.mute == 0);
-               REQUIRE(channel0.mute_s == 0);
                REQUIRE(channel0.solo == 0);
                REQUIRE(channel0.volume == Approx(1.0f));
                REQUIRE(channel0.pan == Approx(0.5f));
index 6d29d85d08df625335499a89a30724b5ebddf9e0..c42e6b1b74617f0e68db33c464947b87c20f4ad1 100644 (file)
@@ -7,7 +7,7 @@ using std::string;
 using namespace giada::m;
 
 
-TEST_CASE("Test Recorder")
+TEST_CASE("recorder")
 {
        /* Each SECTION the TEST_CASE is executed from the start. The following
        code is exectuted before each SECTION. */
diff --git a/tests/sampleChannel.cpp b/tests/sampleChannel.cpp
new file mode 100644 (file)
index 0000000..fe7eee3
--- /dev/null
@@ -0,0 +1,129 @@
+#include "../src/core/sampleChannel.h"
+#include "../src/core/wave.h"
+#include "../src/core/waveManager.h"
+#include <catch.hpp>
+
+
+using namespace giada;
+using namespace giada::m;
+
+
+TEST_CASE("sampleChannel")
+{
+       const int BUFFER_SIZE = 1024;
+
+       std::vector<ChannelMode> modes = { ChannelMode::LOOP_BASIC, 
+               ChannelMode::LOOP_ONCE, ChannelMode::LOOP_REPEAT, 
+               ChannelMode::LOOP_ONCE_BAR, ChannelMode::SINGLE_BASIC, 
+               ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG, 
+               ChannelMode::SINGLE_ENDLESS };
+
+       SampleChannel ch(false, BUFFER_SIZE);
+       Wave* w;
+       waveManager::create("tests/resources/test.wav", &w); 
+       ch.pushWave(w);
+
+       SECTION("push wave")
+       {
+               REQUIRE(ch.status == ChannelStatus::OFF);
+               REQUIRE(ch.wave == w);
+               REQUIRE(ch.begin == 0);
+               REQUIRE(ch.end == w->getSize() - 1);
+               REQUIRE(ch.name == w->getBasename());           
+       }
+
+       SECTION("begin/end")
+       {
+               ch.setBegin(-100);
+
+               REQUIRE(ch.getBegin() == 0);
+               REQUIRE(ch.tracker == 0);
+               REQUIRE(ch.trackerPreview == 0);
+
+               ch.setBegin(100000);
+
+               REQUIRE(ch.getBegin() == w->getSize());
+               REQUIRE(ch.tracker == w->getSize());
+               REQUIRE(ch.trackerPreview == w->getSize());
+
+               ch.setBegin(16);
+
+               REQUIRE(ch.getBegin() == 16);
+               REQUIRE(ch.tracker == 16);
+               REQUIRE(ch.trackerPreview == 16);
+
+               ch.setEnd(0);
+
+               REQUIRE(ch.getEnd() == 17);
+
+               ch.setEnd(100000);
+
+               REQUIRE(ch.getEnd() == w->getSize() - 1);
+
+               ch.setEnd(32);
+
+               REQUIRE(ch.getEnd() == 32);
+
+               ch.setBegin(64);
+
+               REQUIRE(ch.getBegin() == 31);
+       }
+
+       SECTION("pitch")
+       {
+               ch.setPitch(40.0f);
+
+               REQUIRE(ch.getPitch() == G_MAX_PITCH);
+
+               ch.setPitch(-2.0f);
+
+               REQUIRE(ch.getPitch() == G_MIN_PITCH);
+
+               ch.setPitch(0.8f);
+
+               REQUIRE(ch.getPitch() == 0.8f);
+       }
+
+       SECTION("position")
+       {
+               REQUIRE(ch.getPosition() == -1);  // Initially OFF
+
+               ch.status  = ChannelStatus::PLAY;
+               ch.tracker = 1000;
+
+               REQUIRE(ch.getPosition() == 1000);
+
+               ch.begin = 700;
+
+               REQUIRE(ch.getPosition() == 300);
+       }
+
+       SECTION("empty")
+       {
+               ch.empty();
+
+               REQUIRE(ch.status == 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);
+       }
+
+       SECTION("can record audio")
+       {       
+               REQUIRE(ch.canInputRec() == false); // Can't record if not armed
+
+               ch.armed = true;
+
+               REQUIRE(ch.canInputRec() == false); // Can't record with Wave in it
+
+               ch.empty();
+
+               REQUIRE(ch.canInputRec() == true);
+       }
+
+       /* TODO - fillBuffer, isAnyLoopMode, isAnySingleMode, isOnLastFrame */
+}
diff --git a/tests/sampleChannelProc.cpp b/tests/sampleChannelProc.cpp
new file mode 100644 (file)
index 0000000..82f2e07
--- /dev/null
@@ -0,0 +1,250 @@
+#include "../src/core/sampleChannel.h"
+#include "../src/core/sampleChannelProc.h"
+#include "../src/core/wave.h"
+#include "../src/core/waveManager.h"
+#include <catch.hpp>
+
+
+using namespace giada;
+using namespace giada::m;
+
+
+TEST_CASE("sampleChannelProc")
+{
+       const int BUFFER_SIZE = 1024;
+
+       std::vector<ChannelMode> modes = { ChannelMode::LOOP_BASIC, 
+               ChannelMode::LOOP_ONCE, ChannelMode::LOOP_REPEAT, 
+               ChannelMode::LOOP_ONCE_BAR, ChannelMode::SINGLE_BASIC, 
+               ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG, 
+               ChannelMode::SINGLE_ENDLESS };
+
+       Wave* w;
+       SampleChannel ch(false, BUFFER_SIZE);
+       waveManager::create("tests/resources/test.wav", &w); 
+
+       REQUIRE(ch.status == ChannelStatus::EMPTY);
+       REQUIRE(ch.mode == ChannelMode::SINGLE_BASIC);
+
+       SECTION("buffer")
+       {
+               SECTION("prepare")
+               {
+                       /* With no wave data in it. */
+                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
+
+                       REQUIRE(ch.tracker == 0);
+
+                       /* With data, stopped. */
+                       ch.pushWave(w);
+                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
+
+                       REQUIRE(ch.tracker == 0);
+
+                       /* With data, playing. */
+                       ch.status = ChannelStatus::PLAY;
+                       sampleChannelProc::prepareBuffer(&ch, /*running=*/false);
+
+                       REQUIRE(ch.tracker == BUFFER_SIZE);
+               }
+
+               SECTION("fill")
+               {
+                       ch.pushWave(w);
+
+                       /* Zero offset. */
+                       REQUIRE(ch.fillBuffer(ch.buffer, 0, 0) == BUFFER_SIZE);
+
+                       /* Non-zero offset. */
+                       REQUIRE(ch.fillBuffer(ch.buffer, 0, 666) == BUFFER_SIZE - 666);
+
+                       /* At the end of the waveform. */
+                       REQUIRE(ch.fillBuffer(ch.buffer, ch.end - 666, 0) == (ch.end - (ch.end - 666)) + 1);
+               }
+       }
+
+       SECTION("statuses")
+       {
+               ch.pushWave(w);
+
+               SECTION("start from OFF")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode   = mode;
+                               ch.status = ChannelStatus::OFF;
+                               sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
+
+                               if (ch.isAnyLoopMode())
+                                       REQUIRE(ch.status == ChannelStatus::WAIT);
+                               else
+                                       REQUIRE(ch.status == ChannelStatus::PLAY);
+                       }
+               }
+
+               SECTION("start from PLAY")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode    = mode;
+                               ch.status  = 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.tracker == 0);
+                               }
+                               else
+                               if (ch.isAnyLoopMode() || ch.mode == ChannelMode::SINGLE_ENDLESS) 
+                                       REQUIRE(ch.status == ChannelStatus::ENDING);
+                               else
+                               if (ch.mode == ChannelMode::SINGLE_BASIC) {
+                                       REQUIRE(ch.status == ChannelStatus::OFF);
+                                       REQUIRE(ch.tracker == 0);
+                               }
+                       }
+               }
+
+               SECTION("start from WAIT")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode   = mode;
+                               ch.status = ChannelStatus::WAIT;
+                               sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
+
+                               REQUIRE(ch.status == ChannelStatus::OFF);
+                       }
+               }
+
+               SECTION("start from ENDING")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode   = mode;
+                               ch.status = ChannelStatus::ENDING;
+                               sampleChannelProc::start(&ch, 0, /*doQuantize=*/false, /*velocity=*/0);
+
+                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                       }
+               }
+
+               SECTION("stop from PLAY")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode   = mode;
+                               ch.status = ChannelStatus::PLAY;
+                               ch.tracker = 16; // simulate processing
+                               sampleChannelProc::stop(&ch);
+
+                               if (ch.mode == ChannelMode::SINGLE_PRESS) {
+                                       REQUIRE(ch.status == ChannelStatus::OFF);
+                                       REQUIRE(ch.tracker == 0);
+                               }
+                               else {
+                                       /* Nothing should change for other modes. */
+                                       REQUIRE(ch.status == ChannelStatus::PLAY);
+                                       REQUIRE(ch.tracker == 16);                                      
+                               }
+                       }
+               }
+
+               SECTION("kill")
+               {
+                       std::vector<ChannelStatus> statuses = { ChannelStatus::ENDING, 
+                               ChannelStatus::WAIT, ChannelStatus::PLAY, ChannelStatus::OFF, 
+                               ChannelStatus::EMPTY, ChannelStatus::MISSING, ChannelStatus::WRONG };  
+
+                       for (ChannelMode mode : modes) {
+                               for (ChannelStatus status : statuses) {
+                                       ch.mode    = mode;
+                                       ch.status  = 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);
+                                               REQUIRE(ch.tracker == 0);
+                                       }
+                               }
+                       }               
+               }
+
+               SECTION("quantized start")
+               {
+                       for (ChannelMode mode : modes) {
+                               ch.mode   = mode;               
+                               ch.status = ChannelStatus::OFF; 
+                               sampleChannelProc::start(&ch, 0, /*doQuantize=*/true, /*velocity=*/0);
+
+                               if (ch.isAnyLoopMode())
+                                       REQUIRE(ch.status == ChannelStatus::WAIT);      
+                               else {
+                                       REQUIRE(ch.status == ChannelStatus::OFF);       
+                                       REQUIRE(ch.qWait == true);      
+                               }
+                       }
+               }
+       } 
+
+       SECTION("stop input recordings")
+       {
+               /* 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;
+                       
+                       sampleChannelProc::stopInputRec(&ch, /*globalFrame=*/666);
+
+                       if (ch.isAnyLoopMode()) {
+                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.tracker == 666);
+                       }
+                       else {
+                               REQUIRE(ch.status == ChannelStatus::OFF);
+                               REQUIRE(ch.tracker == 16);                              
+                       }
+               }
+       }
+
+       SECTION("rewind by sequencer")
+       {
+               ch.pushWave(w);
+
+               /* Test loop modes. */
+
+               for (ChannelMode mode : modes) {
+                       ch.mode    = mode;
+                       ch.status  = ChannelStatus::PLAY;       
+                       ch.tracker = 16; // simulate processing
+
+                       sampleChannelProc::rewindBySeq(&ch);
+
+                       if (ch.isAnyLoopMode()) {
+                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.tracker == 0);
+                       }
+                       else {
+                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.tracker == 16);                              
+                       }
+               }
+
+               /* 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;
+
+                       sampleChannelProc::rewindBySeq(&ch);
+
+                       if (ch.isAnySingleMode()) {
+                               REQUIRE(ch.status == ChannelStatus::PLAY);
+                               REQUIRE(ch.tracker == 0);
+                       }
+               }               
+       }
+}
diff --git a/tests/sampleChannelRec.cpp b/tests/sampleChannelRec.cpp
new file mode 100644 (file)
index 0000000..c115d07
--- /dev/null
@@ -0,0 +1,85 @@
+#include "../src/core/sampleChannel.h"
+#include "../src/core/sampleChannelRec.h"
+#include <catch.hpp>
+
+
+using namespace giada;
+using namespace giada::m;
+
+
+TEST_CASE("sampleChannelRec")
+{
+       const int BUFFER_SIZE = 1024;
+
+       SampleChannel ch(false, BUFFER_SIZE);
+
+       SECTION("start reading actions, don't treat recs as loop")
+       {
+               sampleChannelRec::startReadingActions(&ch, /*treatRecsAsLoops=*/false, 
+                       /*recsStopOnChanHalt=*/false);  
+
+               REQUIRE(ch.recStatus == ChannelStatus::OFF);
+               REQUIRE(ch.readActions == true);
+       }       
+
+       SECTION("start reading actions, do treat recs as loop")
+       {
+               sampleChannelRec::startReadingActions(&ch, /*treatRecsAsLoops=*/true, 
+                       /*recsStopOnChanHalt=*/false);  
+
+               REQUIRE(ch.recStatus == ChannelStatus::WAIT);
+               REQUIRE(ch.readActions == false);
+       }       
+
+       SECTION("stop reading actions")
+       {
+               /* First state: PLAY */
+               ch.recStatus = ChannelStatus::PLAY;
+
+               sampleChannelRec::stopReadingActions(&ch, /*clockRunning=*/true, 
+                       /*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+
+               REQUIRE(ch.readActions == false);               
+               REQUIRE(ch.recStatus == ChannelStatus::PLAY);           
+
+               /* Second state: WAIT */
+               ch.recStatus = ChannelStatus::WAIT;
+
+               sampleChannelRec::stopReadingActions(&ch, /*clockRunning=*/true, 
+                       /*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+
+               REQUIRE(ch.readActions == false);               
+               REQUIRE(ch.recStatus == ChannelStatus::OFF);            
+
+               /* Third state: WAIT */
+               ch.recStatus = ChannelStatus::ENDING;
+
+               sampleChannelRec::stopReadingActions(&ch, /*clockRunning=*/true, 
+                       /*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+
+               REQUIRE(ch.readActions == false);               
+               REQUIRE(ch.recStatus == ChannelStatus::PLAY);
+
+               /* Fourth state: any, but with clockRunning == false. */
+
+               sampleChannelRec::stopReadingActions(&ch, /*clockRunning=*/false, 
+                       /*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+
+               REQUIRE(ch.readActions == false);               
+               REQUIRE(ch.recStatus == ChannelStatus::OFF);            
+       }
+
+
+       SECTION("set read actions status to false with recsStopOnChanHalt")
+       {
+               ch.status  = ChannelStatus::PLAY;
+               ch.tracker = 1024;
+
+               sampleChannelRec::setReadActions(&ch, false, /*recsStopOnChanHalt=*/true);
+
+               REQUIRE(ch.readActions == false);
+               REQUIRE(ch.status == ChannelStatus::OFF);
+               REQUIRE(ch.tracker == 0);
+
+       }
+}
index c17b51dafbb527d5ed257f181bd6704b77b94fc5..79aeb4de4ca156e1bd8c30fd177f8774fd36c534 100644 (file)
@@ -1,10 +1,11 @@
 #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("Test filesystem utils")
+TEST_CASE("u::fs")
 {
        REQUIRE(gu_fileExists("README.md") == true);
        REQUIRE(gu_fileExists("ghost_file") == false);
@@ -32,7 +33,7 @@ TEST_CASE("Test filesystem utils")
 }
 
 
-TEST_CASE("Test string utils")
+TEST_CASE("u::string")
 {
        using std::vector;
 
@@ -52,11 +53,22 @@ TEST_CASE("Test string utils")
 }
 
 
-TEST_CASE("Test math utils")
+TEST_CASE("::math")
 {
        using namespace giada::u::math;
 
        REQUIRE(map( 0.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 0.0f);
        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 00977202e59c72f48db23f1a042b971b6bf3bff8..095a4c45539c83054227c3335424f0b5a62df454 100644 (file)
@@ -6,7 +6,7 @@
 using std::string;
 
 
-TEST_CASE("Test Wave class")
+TEST_CASE("Wave")
 {
        static const int SAMPLE_RATE = 44100;
        static const int BUFFER_SIZE = 4096;
@@ -20,8 +20,7 @@ TEST_CASE("Test Wave class")
        SECTION("test allocation")
        {
                Wave wave;
-
-               REQUIRE(wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav") == true);
+               wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
 
                SECTION("test basename")
                {
index e12c8cbca1230b6922c28ea8a6a87d0524856606..74738782dc8445ff9aee44dad7926a19ab4435fd 100644 (file)
@@ -9,7 +9,7 @@ using std::string;
 using namespace giada::m;
 
 
-TEST_CASE("Test waveFx")
+TEST_CASE("waveFx")
 {
        static const int SAMPLE_RATE = 44100;
        static const int BUFFER_SIZE = 4000;
index d32be1b7efd0b166b66818db72167c2cc6644d1e..f19905ce819e0a7bedf8c0ab89c81b84dc60d23e 100644 (file)
@@ -14,7 +14,7 @@ using namespace giada::m;
 #define G_CHANNELS 2
 
 
-TEST_CASE("Test waveManager")
+TEST_CASE("waveManager")
 {
   /* 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. */
@@ -35,11 +35,10 @@ TEST_CASE("Test waveManager")
 
   SECTION("test recording")
   {
-    int res = waveManager::createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE, 
+    waveManager::createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE, 
       "test.wav", &w);
     std::unique_ptr<Wave> wave(w);
 
-    REQUIRE(res == G_RES_OK);
     REQUIRE(wave->getRate() == G_SAMPLE_RATE);
     REQUIRE(wave->getSize() == G_BUFFER_SIZE);
     REQUIRE(wave->getChannels() == G_CHANNELS);
@@ -49,15 +48,12 @@ TEST_CASE("Test waveManager")
 
   SECTION("test resampling")
   {
-    int res = waveManager::create("tests/resources/test.wav", &w);
+    waveManager::create("tests/resources/test.wav", &w);
     std::unique_ptr<Wave> wave(w);
-    
-    REQUIRE(res == G_RES_OK);
 
     int oldSize = wave->getSize();
-    res = waveManager::resample(wave.get(), 1, G_SAMPLE_RATE * 2);
+    waveManager::resample(wave.get(), 1, G_SAMPLE_RATE * 2);
     
-    REQUIRE(res == G_RES_OK);
     REQUIRE(wave->getRate() == G_SAMPLE_RATE * 2);
     REQUIRE(wave->getSize() == oldSize * 2);
     REQUIRE(wave->getChannels() == G_CHANNELS);