--------------------------------------------------------------------------------
+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
# 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 \
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 \
-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 \
# 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
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
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)
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)
/* -------------------------------------------------------------------------- */
-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;
}
int countChannels() const;
bool isAllocd() const;
- bool alloc(int size, int channels) noexcept;
+ void alloc(int size, int channels);
void free();
/* copyData
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),
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);
}
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;
/* -------------------------------------------------------------------------- */
-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;
}
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);
}
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);
}
}
/* -------------------------------------------------------------------------- */
-void Channel::receiveMidi(const MidiEvent& midiEvent)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
bool Channel::isMidiInAllowed(int c) const
{
return midiInFilter == -1 || midiInFilter == c;
/* -------------------------------------------------------------------------- */
-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;
/* -------------------------------------------------------------------------- */
-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;
}
#include <vector>
#include <string>
#include <pthread.h>
+#include "types.h"
+#include "mixer.h"
#include "midiMapConf.h"
#include "midiEvent.h"
#include "recorder.h"
#endif
+#undef Status
+
+
class Plugin;
class MidiMapConf;
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). */
/* 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). */
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
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
#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;
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
};
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;
/* -------------------------------------------------------------------------- */
-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;
}
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;
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;
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();
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;
{
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;
}
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?
}
}
#include <string>
+#include "types.h"
class Channel;
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);
void incrCurrentFrame() {
currentFrame++;
- if (currentFrame > framesInLoop) {
+ if (currentFrame >= framesInLoop) {
currentFrame = 0;
currentBeat = 0;
}
/* -------------------------------------------------------------------------- */
+bool canQuantize()
+{
+ return getQuantize() > 0 && isRunning();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
int getCurrentFrame()
{
return currentFrame;
bool quantoHasPassed();
+/* quantoHasPassed
+Whether the quantizer value is > 0 and the clock is running. */
+
+bool canQuantize();
+
/* updateFrameBars
Updates bpm, frames, beats and so on. */
/* -- 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
#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"
#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"
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);
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();
}
}
+int getAPI() { return api; }
+
+
/* -------------------------------------------------------------------------- */
int getDefaultOut();
int getDefaultIn();
bool hasAPI(int API);
+int getAPI();
#ifdef __linux__
/* -------------------------------------------------------------------------- */
+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;
#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 {
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. */
#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])
{
/* -------------------------------------------------------------------------- */
-MidiChannel::~MidiChannel() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
{
Channel::copy(src_, 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;
}
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]);
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
/* -------------------------------------------------------------------------- */
-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)
bool MidiChannel::canInputRec()
{
return false; // midi channels don't handle input audio
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::clear() {}
+}
\ No newline at end of file
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;
--- /dev/null
+#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
--- /dev/null
+#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
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);
}
}
int inputTracker = 0;
-/* -------------------------------------------------------------------------- */
-
-
-bool isChannelAudible(Channel* ch)
-{
- return !hasSolos || (hasSolos && ch->solo);
-}
-
-
/* -------------------------------------------------------------------------- */
/* computePeak */
/* -------------------------------------------------------------------------- */
-/* 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());
}
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);
}
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
}
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);
}
void testLastBeat()
{
- if (clock::isOnBeat())
- if (metronome && !tickPlay)
- tockPlay = true;
+ if (clock::isOnBeat() && metronome && !tickPlay)
+ tockPlay = true;
}
-
}; // {anonymous}
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;
/* -------------------------------------------------------------------------- */
{
/* 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();
}
/* -------------------------------------------------------------------------- */
-bool allocVirtualInput(int frames)
+void allocVirtualInput(int frames)
{
- return vChanInput.alloc(frames, G_MAX_IO_CHANS);
+ vChanInput.alloc(frames, G_MAX_IO_CHANS);
}
if (!ready)
return 0;
+ pthread_mutex_lock(&mutex);
+
#ifdef __linux__
clock::recvJackSync();
#endif
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. */
out.setData(nullptr, 0, 0);
in.setData(nullptr, 0, 0);
+ pthread_mutex_unlock(&mutex);
+
return 0;
}
clock::stop();
while (channels.size() > 0)
mh::deleteChannel(channels.at(0));
+ pthread_mutex_destroy(&mutex);
}
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);
+}
+
+
/* -------------------------------------------------------------------------- */
clock::rewind();
if (clock::isRunning())
for (Channel* ch : channels)
- ch->rewind();
+ ch->rewindBySeq();
}
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)
}
vChanInput.clear();
}
-
-
}}}; // giada::m::mixer::
#include <pthread.h>
#include <vector>
+#include "recorder.h"
#include "../deps/rtaudio-mod/RtAudio.h"
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();
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. */
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::;
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++)
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())
/* -------------------------------------------------------------------------- */
-Channel* addChannel(int type)
+Channel* addChannel(ChannelType type)
{
Channel* ch = nullptr;
channelManager::create(type, kernelAudio::getRealBufSize(),
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;
}
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;
}
}
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;
}
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;
}
void updateSoloCount()
{
- for (Channel* ch : mixer::channels)
+ for (const Channel* ch : mixer::channels)
if (ch->solo) {
mixer::hasSolos = true;
return;
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;
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");
}
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;
}
#include <string>
+#include "types.h"
class Channel;
/* 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. */
#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"
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);
+ }
+ }
}
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;
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));
std::string name;
int column;
int mute;
- int mute_s;
int solo;
float volume;
float pan;
/* -------------------------------------------------------------------------- */
-Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate,
+Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate,
int buffersize)
: ui (nullptr),
plugin(plugin),
void close()
{
messageManager->deleteInstance();
+ pthread_mutex_destroy(&mutex_midi);
}
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++)
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();
}
{
/* 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;
/* -------------------------------------------------------------------------- */
+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)
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()?
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_t* mixerMutex);
/* forEachAction
Applies a read-only callback on each action recorded. */
* -------------------------------------------------------------------------- */
-#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);
}
SampleChannel::~SampleChannel()
{
- if (wave != nullptr)
- delete wave;
+ delete wave;
if (rsmp_state != nullptr)
src_delete(rsmp_state);
}
/* -------------------------------------------------------------------------- */
-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);
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)
/* -------------------------------------------------------------------------- */
-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;
}
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();
}
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
#include <functional>
#include <samplerate.h>
+#include "types.h"
#include "channel.h"
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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
/* -------------------------------------------------------------------------- */
-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;
}
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:
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++)
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);
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);
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) */
}
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");
/* -------------------------------------------------------------------------- */
-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);
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);
*out = wave;
gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
-
- return G_RES_OK;
}
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);
/* 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);
/* 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. */
/* -------------------------------------------------------------------------- */
-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);
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);
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;
}
G_MainWin->keyboard->freeChannel(ch->guiChannel);
m::recorder::clearChan(ch->index);
- ch->hasActions = false;
ch->empty();
/* delete any related subwindow */
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;
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);
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);
/* -------------------------------------------------------------------------- */
-void toggleReadingRecs(SampleChannel* ch, bool gui)
+void toggleReadingActions(Channel* ch, bool gui)
{
/* When you call startReadingRecs with conf::treatRecsAsLoops, the
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);
/* -------------------------------------------------------------------------- */
-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();
#include <string>
+#include "../core/types.h"
class Channel;
class SampleChannel;
class gdSampleEditor;
+
namespace giada {
namespace c {
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. */
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::
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);
}
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();
+ }
}
recorder::active = true;
if (!clock::isRunning())
- glue_startSeq(false); // update gui ayway
+ glue_startSeq(false); // update gui
if (!gui) {
Fl::lock();
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) {
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);
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
#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);
}
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();
#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);
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();
#ifdef WITH_VST
+#include <FL/Fl.H>
#include "../core/pluginHost.h"
#include "../core/mixer.h"
#include "../core/plugin.h"
{
if (index >= pluginHost::countAvailablePlugins())
return nullptr;
- return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch);
+ return pluginHost::addPlugin(index, stackType, &mixer::mutex, ch);
}
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);
}
void freePlugin(Channel* ch, int index, int stackType)
{
- pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch);
+ pluginHost::freePlugin(index, stackType, &mixer::mutex, ch);
}
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
{
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);
}
/* -------------------------------------------------------------------------- */
-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());
}
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();
#define G_GLUE_SAMPLE_EDITOR_H
+#include "../core/types.h"
+
+
class SampleChannel;
class geWaveform;
void setPlayHead(SampleChannel* ch, int f);
-void setPreview(SampleChannel* ch, int mode);
+void setPreview(SampleChannel* ch, PreviewMode mode);
void rewindPreview(SampleChannel* ch);
/* toNewChannel
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);
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);
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * 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();
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * 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
using std::string;
+using namespace giada;
using namespace giada::m;
-gdActionEditor::gdActionEditor(Channel *chan)
+gdActionEditor::gdActionEditor(Channel* chan)
: gdWindow(640, 284),
chan (chan),
zoom (100),
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");
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 {
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);
/* 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 {
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());
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());
using std::string;
using std::vector;
+using namespace giada;
using namespace giada::m;
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();
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);
}
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,
#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"
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());
gdPluginList::~gdPluginList()
{
- conf::pluginListX = x();
- conf::pluginListY = y();
+ m::conf::pluginListX = x();
+ m::conf::pluginListY = y();
}
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();
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,
void gdPluginList::refreshList()
{
+ using namespace giada::m;
+
/* delete the previous list */
list->clear();
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++;
}
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;
}
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-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
#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_Scroll* list;
- 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
+ Channel* ch; // ch == nullptr ? masterOut
int stackType;
- gdPluginList(int stackType, Channel *ch=nullptr);
+ gdPluginList(int stackType, Channel* ch=nullptr);
~gdPluginList();
/* special callback, passed to browser. When closed (i.e. plugin
};
-/* -------------------------------------------------------------------------- */
-
-
-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
#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();
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));
+ void* cocoaWindow = (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());
}
/* -------------------------------------------------------------------------- */
-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);
}
/* -------------------------------------------------------------------------- */
-void gdPluginWindowGUI::__cb_refresh()
+void gdPluginWindowGUI::cb_refresh()
{
pluginHost::runDispatchLoop();
Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
gdPluginWindowGUI::~gdPluginWindowGUI()
{
- __cb_close();
+ cb_close();
}
#endif // #ifdef WITH_VST
{
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();
};
#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"
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);
}
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);
}
#include "action.h"
+using namespace giada;
using namespace giada::m;
* 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) {
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 {
/* 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) {
* 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);
/* 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);
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);
}
extern gdMainWindow *G_MainWin;
+using namespace giada;
using namespace giada::m;
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;
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;
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;
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
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)
}
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);
/* 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) {
// 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();
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(
{
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. */
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);
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)
{
}
/* -------------------------------------------------------------------------- */
-
-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;
}
}
#include <FL/Fl_Group.H>
+#include "../../../../core/types.h"
class Channel;
/* 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). */
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. */
#ifdef WITH_VST
geStatusButton* fx;
#endif
-
- int type;
};
-/* -----------------------------------------------------------------------------
+ /* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
#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)
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);
}
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;
}
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
#include "channelStatus.h"
+using namespace giada;
using namespace giada::m;
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);
+
}
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);
/* 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));
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"},
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);
}
+
/* -------------------------------------------------------------------------- */
static void cb_addChannel (Fl_Widget* v, void* p);
inline void __cb_addChannel();
- int openTypeMenu();
-
geButton* m_addChannelBtn;
geResizerBar* m_resizer;
geKeyboard* m_parent;
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();
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);
}
extern gdMainWindow* G_MainWin;
+using namespace giada;
+
+
namespace
{
enum class Menu
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();
{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();
/* 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);
void geSampleChannel::cb_readActions()
{
using namespace giada::c::channel;
- toggleReadingRecs(static_cast<SampleChannel*>(ch));
+ toggleReadingActions(static_cast<SampleChannel*>(ch));
}
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:
else
hideActionButton();
- modeBox->value(sch->mode);
+ modeBox->value(static_cast<int>(sch->mode));
modeBox->redraw();
vol->value(sch->volume);
#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"
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);
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
}
/* -------------------------------------------------------------------------- */
-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);
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::__cb_meter()
+void geMainTimer::cb_meter()
{
gu_openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS);
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::__cb_quantizer()
+void geMainTimer::cb_quantizer()
{
glue_quantize(quantizer->value());
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::__cb_multiplier()
+void geMainTimer::cb_multiplier()
{
glue_beatsMultiply();
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::__cb_divider()
+void geMainTimer::cb_divider()
{
glue_beatsDivide();
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::setBpm(const char *v)
+void geMainTimer::setBpm(const char* v)
{
bpm->copy_label(v);
}
/* -------------------------------------------------------------------------- */
+void geMainTimer::setQuantizer(int q)
+{
+ quantizer->value(q);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void geMainTimer::setMeter(int beats, int bars)
{
string tmp = gu_iToString(beats) + "/" + gu_iToString(bars);
{
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 char* v);
void setBpm(float v);
void setMeter(int beats, int bars);
+ void setQuantizer(int q);
/* setLock
Locks bpm, beter and multipliers. Used during audio recordings. */
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
* -------------------------------------------------------------------------- */
-#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"
{0}
};
- if (ch->status == STATUS_PLAY) {
+ if (ch->status == ChannelStatus::PLAY) {
menu[(int)Menu::CUT].deactivate();
menu[(int)Menu::TRIM].deactivate();
}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * 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
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * 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
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);
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
#include <catch.hpp>
-TEST_CASE("Test AudioBuffer class")
+TEST_CASE("AudioBuffer")
{
using namespace giada::m;
{
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);
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);
using namespace giada::m;
-TEST_CASE("Test Conf class")
+TEST_CASE("conf")
{
conf::init();
#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
using namespace giada::m;
-TEST_CASE("Test MidiMapConf")
+TEST_CASE("midiMapConf")
{
SECTION("test default values")
{
#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
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";
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;
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));
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. */
--- /dev/null
+#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 */
+}
--- /dev/null
+#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);
+ }
+ }
+ }
+}
--- /dev/null
+#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);
+
+ }
+}
#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);
}
-TEST_CASE("Test string utils")
+TEST_CASE("u::string")
{
using std::vector;
}
-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
using std::string;
-TEST_CASE("Test Wave class")
+TEST_CASE("Wave")
{
static const int SAMPLE_RATE = 44100;
static const int BUFFER_SIZE = 4096;
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")
{
using namespace giada::m;
-TEST_CASE("Test waveFx")
+TEST_CASE("waveFx")
{
static const int SAMPLE_RATE = 44100;
static const int BUFFER_SIZE = 4000;
#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. */
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);
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);