From 2c7d895753393a9cf67ab44505f697b420764720 Mon Sep 17 00:00:00 2001 From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 4 Jul 2018 21:29:11 +0200 Subject: [PATCH] New upstream version 0.15.1+ds1 --- ChangeLog | 11 + Makefile.am | 589 ++++++----- src/core/audioBuffer.cpp | 5 +- src/core/audioBuffer.h | 2 +- src/core/channel.cpp | 138 +-- src/core/channel.h | 220 ++--- src/core/channelManager.cpp | 37 +- src/core/channelManager.h | 3 +- src/core/clock.cpp | 11 +- src/core/clock.h | 5 + src/core/const.h | 50 +- src/core/init.cpp | 30 +- src/core/kernelAudio.cpp | 3 + src/core/kernelAudio.h | 1 + src/core/kernelMidi.cpp | 19 + src/core/kernelMidi.h | 16 +- src/core/midiChannel.cpp | 183 +--- src/core/midiChannel.h | 24 +- src/core/midiChannelProc.cpp | 163 +++ src/core/midiChannelProc.h | 52 + src/core/midiDispatcher.cpp | 2 +- src/core/mixer.cpp | 175 +--- src/core/mixer.h | 85 +- src/core/mixerHandler.cpp | 44 +- src/core/mixerHandler.h | 3 +- src/core/patch.cpp | 18 +- src/core/patch.h | 1 - src/core/plugin.cpp | 2 +- src/core/pluginHost.cpp | 3 +- src/core/recorder.cpp | 35 +- src/core/recorder.h | 9 +- src/core/sampleChannel.cpp | 925 ++++-------------- src/core/sampleChannel.h | 189 ++-- src/core/sampleChannelProc.cpp | 468 +++++++++ src/core/sampleChannelProc.h | 91 ++ src/core/sampleChannelRec.cpp | 327 +++++++ src/core/sampleChannelRec.h | 73 ++ src/core/types.h | 58 ++ src/core/wave.cpp | 6 +- src/core/wave.h | 2 +- src/core/waveFx.cpp | 20 +- src/core/waveManager.cpp | 31 +- src/core/waveManager.h | 4 +- src/glue/channel.cpp | 73 +- src/glue/channel.h | 10 +- src/glue/io.cpp | 196 +--- src/glue/io.h | 13 +- src/glue/main.cpp | 91 +- src/glue/main.h | 14 +- src/glue/plugin.cpp | 7 +- src/glue/recorder.cpp | 2 +- src/glue/sampleEditor.cpp | 19 +- src/glue/sampleEditor.h | 5 +- src/glue/storage.cpp | 4 +- src/gui/dialogs/{gd_about.cpp => about.cpp} | 14 +- src/gui/dialogs/{gd_about.h => about.h} | 14 +- src/gui/dialogs/gd_actionEditor.cpp | 19 +- src/gui/dialogs/midiIO/midiInputChannel.cpp | 7 +- src/gui/dialogs/pluginList.cpp | 221 +---- src/gui/dialogs/pluginList.h | 53 +- src/gui/dialogs/pluginWindowGUI.cpp | 54 +- src/gui/dialogs/pluginWindowGUI.h | 12 +- src/gui/dialogs/sampleEditor.cpp | 8 +- src/gui/elems/actionEditor/action.cpp | 19 +- src/gui/elems/actionEditor/actionEditor.cpp | 13 +- src/gui/elems/actionEditor/envelopeEditor.cpp | 4 +- src/gui/elems/actionEditor/muteEditor.cpp | 6 +- src/gui/elems/actionEditor/pianoItem.cpp | 4 +- .../elems/actionEditor/pianoItemOrphaned.cpp | 4 +- src/gui/elems/mainWindow/keyboard/channel.cpp | 24 +- src/gui/elems/mainWindow/keyboard/channel.h | 7 +- .../elems/mainWindow/keyboard/channelMode.cpp | 39 +- .../mainWindow/keyboard/channelStatus.cpp | 61 +- src/gui/elems/mainWindow/keyboard/column.cpp | 24 +- src/gui/elems/mainWindow/keyboard/column.h | 2 - .../elems/mainWindow/keyboard/midiChannel.cpp | 4 +- .../mainWindow/keyboard/sampleChannel.cpp | 21 +- src/gui/elems/mainWindow/mainMenu.cpp | 18 +- src/gui/elems/mainWindow/mainTimer.cpp | 43 +- src/gui/elems/mainWindow/mainTimer.h | 36 +- src/gui/elems/plugin/pluginElement.cpp | 211 ++++ src/gui/elems/plugin/pluginElement.h | 78 ++ src/gui/elems/sampleEditor/waveTools.cpp | 10 +- src/utils/gui.cpp | 1 + src/utils/{deps.cpp => ver.cpp} | 32 +- src/utils/{deps.h => ver.h} | 10 +- tests/audioBuffer.cpp | 6 +- tests/conf.cpp | 2 +- tests/main.cpp | 6 + tests/midiMapConf.cpp | 2 +- tests/patch.cpp | 30 +- tests/recorder.cpp | 2 +- tests/sampleChannel.cpp | 129 +++ tests/sampleChannelProc.cpp | 250 +++++ tests/sampleChannelRec.cpp | 85 ++ tests/utils.cpp | 18 +- tests/wave.cpp | 5 +- tests/waveFx.cpp | 2 +- tests/waveManager.cpp | 12 +- 99 files changed, 3503 insertions(+), 2686 deletions(-) create mode 100644 src/core/midiChannelProc.cpp create mode 100644 src/core/midiChannelProc.h create mode 100644 src/core/sampleChannelProc.cpp create mode 100644 src/core/sampleChannelProc.h create mode 100644 src/core/sampleChannelRec.cpp create mode 100644 src/core/sampleChannelRec.h create mode 100644 src/core/types.h rename src/gui/dialogs/{gd_about.cpp => about.cpp} (92%) rename src/gui/dialogs/{gd_about.h => about.h} (89%) create mode 100644 src/gui/elems/plugin/pluginElement.cpp create mode 100644 src/gui/elems/plugin/pluginElement.h rename src/utils/{deps.cpp => ver.cpp} (79%) rename src/utils/{deps.h => ver.h} (89%) create mode 100644 tests/sampleChannel.cpp create mode 100644 tests/sampleChannelProc.cpp create mode 100644 tests/sampleChannelRec.cpp diff --git a/ChangeLog b/ChangeLog index f593b9d..bc3ffe3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,17 @@ -------------------------------------------------------------------------------- +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 diff --git a/Makefile.am b/Makefile.am index f007a12..2ce25d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,15 +4,296 @@ AUTOMAKE_OPTIONS = foreign # WITH_VST, LINUX, WINDOWS and OSX are varibles defined via AM_CONDITIONAL # inside configure.ac. -extraSources = + cppFlags = -cxxFlags = -std=c++11 -Wall -Werror +cxxFlags = -std=c++11 -Wall ldAdd = ldFlags = +sourcesExtra = +sourcesMain = src/main.cpp +sourcesCore = \ + src/core/const.h \ + src/core/types.h \ + src/core/channel.h \ + src/core/channel.cpp \ + src/core/sampleChannel.h \ + src/core/sampleChannel.cpp \ + src/core/midiDispatcher.h \ + src/core/midiDispatcher.cpp \ + src/core/midiChannel.h \ + src/core/midiChannel.cpp \ + src/core/midiMapConf.h \ + src/core/midiMapConf.cpp \ + src/core/midiEvent.h \ + src/core/midiEvent.cpp \ + src/core/audioBuffer.h \ + src/core/audioBuffer.cpp \ + src/core/conf.h \ + src/core/conf.cpp \ + src/core/kernelAudio.h \ + src/core/kernelAudio.cpp \ + src/core/pluginHost.h \ + src/core/pluginHost.cpp \ + src/core/mixerHandler.h \ + src/core/mixerHandler.cpp \ + src/core/init.h \ + src/core/init.cpp \ + src/core/plugin.h \ + src/core/plugin.cpp \ + src/core/wave.h \ + src/core/wave.cpp \ + src/core/waveFx.h \ + src/core/waveFx.cpp \ + src/core/kernelMidi.h \ + src/core/kernelMidi.cpp \ + src/core/graphics.h \ + src/core/graphics.cpp \ + src/core/patch.h \ + src/core/patch.cpp \ + src/core/recorder.h \ + src/core/recorder.cpp \ + src/core/mixer.h \ + src/core/mixer.cpp \ + src/core/storager.h \ + src/core/storager.cpp \ + src/core/clock.h \ + src/core/clock.cpp \ + src/core/waveManager.h \ + src/core/waveManager.cpp \ + src/core/channelManager.h \ + src/core/channelManager.cpp \ + src/core/sampleChannelProc.h \ + src/core/sampleChannelProc.cpp \ + src/core/sampleChannelRec.h \ + src/core/sampleChannelRec.cpp \ + src/core/midiChannelProc.h \ + src/core/midiChannelProc.cpp \ + src/glue/main.h \ + src/glue/main.cpp \ + src/glue/io.h \ + src/glue/io.cpp \ + src/glue/storage.h \ + src/glue/storage.cpp \ + src/glue/channel.h \ + src/glue/channel.cpp \ + src/glue/plugin.h \ + src/glue/plugin.cpp \ + src/glue/transport.h \ + src/glue/transport.cpp \ + src/glue/recorder.h \ + src/glue/recorder.cpp \ + src/glue/sampleEditor.h \ + src/glue/sampleEditor.cpp \ + src/gui/dialogs/window.h \ + src/gui/dialogs/window.cpp \ + src/gui/dialogs/gd_keyGrabber.h \ + src/gui/dialogs/gd_keyGrabber.cpp \ + src/gui/dialogs/about.h \ + src/gui/dialogs/about.cpp \ + src/gui/dialogs/gd_mainWindow.h \ + src/gui/dialogs/gd_mainWindow.cpp \ + src/gui/dialogs/beatsInput.h \ + src/gui/dialogs/beatsInput.cpp \ + src/gui/dialogs/gd_warnings.h \ + src/gui/dialogs/gd_warnings.cpp \ + src/gui/dialogs/bpmInput.h \ + src/gui/dialogs/bpmInput.cpp \ + src/gui/dialogs/channelNameInput.h \ + src/gui/dialogs/channelNameInput.cpp \ + src/gui/dialogs/gd_config.h \ + src/gui/dialogs/gd_config.cpp \ + src/gui/dialogs/gd_devInfo.h \ + src/gui/dialogs/gd_devInfo.cpp \ + src/gui/dialogs/pluginList.h \ + src/gui/dialogs/pluginList.cpp \ + src/gui/dialogs/pluginWindow.h \ + src/gui/dialogs/pluginWindow.cpp \ + src/gui/dialogs/sampleEditor.h \ + src/gui/dialogs/sampleEditor.cpp \ + src/gui/dialogs/pluginWindowGUI.h \ + src/gui/dialogs/pluginWindowGUI.cpp \ + src/gui/dialogs/gd_actionEditor.h \ + src/gui/dialogs/gd_actionEditor.cpp \ + src/gui/dialogs/pluginChooser.h \ + src/gui/dialogs/pluginChooser.cpp \ + src/gui/dialogs/browser/browserBase.h \ + src/gui/dialogs/browser/browserBase.cpp \ + src/gui/dialogs/browser/browserDir.h \ + src/gui/dialogs/browser/browserDir.cpp \ + src/gui/dialogs/browser/browserLoad.h \ + src/gui/dialogs/browser/browserLoad.cpp \ + src/gui/dialogs/browser/browserSave.h \ + src/gui/dialogs/browser/browserSave.cpp \ + src/gui/dialogs/midiIO/midiOutputBase.h \ + src/gui/dialogs/midiIO/midiOutputBase.cpp \ + src/gui/dialogs/midiIO/midiOutputSampleCh.h \ + src/gui/dialogs/midiIO/midiOutputSampleCh.cpp \ + src/gui/dialogs/midiIO/midiOutputMidiCh.h \ + src/gui/dialogs/midiIO/midiOutputMidiCh.cpp \ + src/gui/dialogs/midiIO/midiInputBase.h \ + src/gui/dialogs/midiIO/midiInputBase.cpp \ + src/gui/dialogs/midiIO/midiInputChannel.h \ + src/gui/dialogs/midiIO/midiInputChannel.cpp \ + src/gui/dialogs/midiIO/midiInputMaster.h \ + src/gui/dialogs/midiIO/midiInputMaster.cpp \ + src/gui/elems/midiLearner.h \ + src/gui/elems/midiLearner.cpp \ + src/gui/elems/browser.h \ + src/gui/elems/browser.cpp \ + src/gui/elems/soundMeter.h \ + src/gui/elems/soundMeter.cpp \ + src/gui/elems/plugin/pluginBrowser.h \ + src/gui/elems/plugin/pluginBrowser.cpp \ + src/gui/elems/plugin/pluginParameter.h \ + src/gui/elems/plugin/pluginParameter.cpp \ + src/gui/elems/plugin/pluginElement.h \ + src/gui/elems/plugin/pluginElement.cpp \ + src/gui/elems/sampleEditor/waveform.h \ + src/gui/elems/sampleEditor/waveform.cpp \ + src/gui/elems/sampleEditor/waveTools.h \ + src/gui/elems/sampleEditor/waveTools.cpp \ + src/gui/elems/sampleEditor/volumeTool.h \ + src/gui/elems/sampleEditor/volumeTool.cpp \ + src/gui/elems/sampleEditor/boostTool.h \ + src/gui/elems/sampleEditor/boostTool.cpp \ + src/gui/elems/sampleEditor/panTool.h \ + src/gui/elems/sampleEditor/panTool.cpp \ + src/gui/elems/sampleEditor/pitchTool.h \ + src/gui/elems/sampleEditor/pitchTool.cpp \ + src/gui/elems/sampleEditor/rangeTool.h \ + src/gui/elems/sampleEditor/rangeTool.cpp \ + src/gui/elems/sampleEditor/shiftTool.h \ + src/gui/elems/sampleEditor/shiftTool.cpp \ + src/gui/elems/actionEditor/baseActionEditor.h \ + src/gui/elems/actionEditor/baseActionEditor.cpp \ + src/gui/elems/actionEditor/envelopeEditor.h \ + src/gui/elems/actionEditor/envelopeEditor.cpp \ + src/gui/elems/actionEditor/pianoRoll.h \ + src/gui/elems/actionEditor/pianoRoll.cpp \ + src/gui/elems/actionEditor/noteEditor.h \ + src/gui/elems/actionEditor/noteEditor.cpp \ + src/gui/elems/actionEditor/basePianoItem.h \ + src/gui/elems/actionEditor/basePianoItem.cpp \ + src/gui/elems/actionEditor/pianoItem.h \ + src/gui/elems/actionEditor/pianoItem.cpp \ + src/gui/elems/actionEditor/pianoItemOrphaned.h \ + src/gui/elems/actionEditor/pianoItemOrphaned.cpp \ + src/gui/elems/actionEditor/muteEditor.h \ + src/gui/elems/actionEditor/muteEditor.cpp \ + src/gui/elems/actionEditor/actionEditor.h \ + src/gui/elems/actionEditor/actionEditor.cpp \ + src/gui/elems/actionEditor/action.h \ + src/gui/elems/actionEditor/action.cpp \ + src/gui/elems/actionEditor/gridTool.h \ + src/gui/elems/actionEditor/gridTool.cpp \ + src/gui/elems/mainWindow/mainIO.h \ + src/gui/elems/mainWindow/mainIO.cpp \ + src/gui/elems/mainWindow/mainMenu.h \ + src/gui/elems/mainWindow/mainMenu.cpp \ + src/gui/elems/mainWindow/mainTimer.h \ + src/gui/elems/mainWindow/mainTimer.cpp \ + src/gui/elems/mainWindow/mainTransport.h \ + src/gui/elems/mainWindow/mainTransport.cpp \ + src/gui/elems/mainWindow/beatMeter.h \ + src/gui/elems/mainWindow/beatMeter.cpp \ + src/gui/elems/mainWindow/keyboard/channelMode.h \ + src/gui/elems/mainWindow/keyboard/channelMode.cpp \ + src/gui/elems/mainWindow/keyboard/channelButton.h \ + src/gui/elems/mainWindow/keyboard/channelButton.cpp \ + src/gui/elems/mainWindow/keyboard/channelStatus.h \ + src/gui/elems/mainWindow/keyboard/channelStatus.cpp \ + src/gui/elems/mainWindow/keyboard/keyboard.h \ + src/gui/elems/mainWindow/keyboard/keyboard.cpp \ + src/gui/elems/mainWindow/keyboard/column.h \ + src/gui/elems/mainWindow/keyboard/column.cpp \ + src/gui/elems/mainWindow/keyboard/sampleChannel.h \ + src/gui/elems/mainWindow/keyboard/sampleChannel.cpp \ + src/gui/elems/mainWindow/keyboard/midiChannel.h \ + src/gui/elems/mainWindow/keyboard/midiChannel.cpp \ + src/gui/elems/mainWindow/keyboard/channel.h \ + src/gui/elems/mainWindow/keyboard/channel.cpp \ + src/gui/elems/mainWindow/keyboard/sampleChannelButton.h \ + src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \ + src/gui/elems/mainWindow/keyboard/midiChannelButton.h \ + src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp \ + src/gui/elems/config/tabMisc.h \ + src/gui/elems/config/tabMisc.cpp \ + src/gui/elems/config/tabMidi.h \ + src/gui/elems/config/tabMidi.cpp \ + src/gui/elems/config/tabAudio.h \ + src/gui/elems/config/tabAudio.cpp \ + src/gui/elems/config/tabBehaviors.h \ + src/gui/elems/config/tabBehaviors.cpp \ + src/gui/elems/config/tabPlugins.h \ + src/gui/elems/config/tabPlugins.cpp \ + src/gui/elems/basics/scroll.h \ + src/gui/elems/basics/scroll.cpp \ + src/gui/elems/basics/boxtypes.h \ + src/gui/elems/basics/boxtypes.cpp \ + src/gui/elems/basics/baseButton.h \ + src/gui/elems/basics/baseButton.cpp \ + src/gui/elems/basics/statusButton.h \ + src/gui/elems/basics/statusButton.cpp \ + src/gui/elems/basics/button.h \ + src/gui/elems/basics/button.cpp \ + src/gui/elems/basics/idButton.h \ + src/gui/elems/basics/idButton.cpp \ + src/gui/elems/basics/resizerBar.h \ + src/gui/elems/basics/resizerBar.cpp \ + src/gui/elems/basics/input.h \ + src/gui/elems/basics/input.cpp \ + src/gui/elems/basics/liquidScroll.h \ + src/gui/elems/basics/liquidScroll.cpp \ + src/gui/elems/basics/choice.h \ + src/gui/elems/basics/choice.cpp \ + src/gui/elems/basics/dial.h \ + src/gui/elems/basics/dial.cpp \ + src/gui/elems/basics/box.h \ + src/gui/elems/basics/box.cpp \ + src/gui/elems/basics/slider.h \ + src/gui/elems/basics/slider.cpp \ + src/gui/elems/basics/progress.h \ + src/gui/elems/basics/progress.cpp \ + src/gui/elems/basics/check.h \ + src/gui/elems/basics/check.cpp \ + src/gui/elems/basics/radio.h \ + src/gui/elems/basics/radio.cpp \ + src/utils/log.h \ + src/utils/log.cpp \ + src/utils/time.h \ + src/utils/time.cpp \ + src/utils/math.h \ + src/utils/math.cpp \ + src/utils/gui.h \ + src/utils/gui.cpp \ + src/utils/gvector.h \ + src/utils/fs.h \ + src/utils/fs.cpp \ + src/utils/ver.h \ + src/utils/ver.cpp \ + src/utils/string.h \ + src/utils/string.cpp \ + src/deps/rtaudio-mod/RtAudio.h \ + src/deps/rtaudio-mod/RtAudio.cpp +sourcesTests = \ + tests/main.cpp \ + tests/conf.cpp \ + tests/wave.cpp \ + tests/waveManager.cpp \ + tests/patch.cpp \ + tests/midiMapConf.cpp \ + tests/pluginHost.cpp \ + tests/utils.cpp \ + tests/recorder.cpp \ + tests/waveFx.cpp \ + tests/audioBuffer.cpp \ + tests/sampleChannel.cpp \ + tests/sampleChannelProc.cpp \ + tests/sampleChannelRec.cpp if WITH_VST -extraSources += \ +sourcesExtra += \ src/deps/juce/modules/juce_audio_basics/juce_audio_basics.cpp \ src/deps/juce/modules/juce_audio_processors/juce_audio_processors.cpp \ src/deps/juce/modules/juce_core/juce_core.cpp \ @@ -44,7 +325,7 @@ endif if WINDOWS -extraSources += \ +sourcesExtra += \ src/deps/rtaudio-mod/include/asio.h \ src/deps/rtaudio-mod/include/asio.cpp \ src/deps/rtaudio-mod/include/asiolist.h \ @@ -61,8 +342,6 @@ cppFlags += \ -D__WINDOWS_WASAPI__ \ -D__WINDOWS_DS__ -cxxFlags += -Wno-error - ldAdd += -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 \ -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile -lsamplerate -lrtmidi \ -lwinmm -lsetupapi -lksuser -ljansson -limm32 -lglu32 -lshell32 -lversion \ @@ -78,9 +357,6 @@ if LINUX # Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio. cppFlags += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__ -# Don't stop on JUCE's unused functions. -cxxFlags += -Wno-error=unused-function - ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \ -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \ -lfreetype @@ -89,13 +365,13 @@ endif if OSX -extraSources += src/utils/cocoa.mm src/utils/cocoa.h +sourcesExtra += src/utils/cocoa.mm src/utils/cocoa.h # Add preprocessor flags to enable CoreAudio in RtAudio. cppFlags += -D__MACOSX_CORE__ # -ObjC++: Juce requires to build some Objective C code -cxxFlags += -ObjC++ -Wno-auto-var-id +cxxFlags += -ObjC++ ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -ljansson -lm -lpthread \ -lFLAC -logg -lvorbis -lvorbisenc @@ -110,264 +386,7 @@ endif bin_PROGRAMS = giada -giada_SOURCES = \ -src/main.cpp \ -src/core/const.h \ -src/core/channel.h \ -src/core/channel.cpp \ -src/core/sampleChannel.h \ -src/core/sampleChannel.cpp \ -src/core/midiDispatcher.h \ -src/core/midiDispatcher.cpp \ -src/core/midiChannel.h \ -src/core/midiChannel.cpp \ -src/core/midiMapConf.h \ -src/core/midiMapConf.cpp \ -src/core/midiEvent.h \ -src/core/midiEvent.cpp \ -src/core/audioBuffer.h \ -src/core/audioBuffer.cpp \ -src/core/conf.h \ -src/core/conf.cpp \ -src/core/kernelAudio.h \ -src/core/kernelAudio.cpp \ -src/core/pluginHost.h \ -src/core/pluginHost.cpp \ -src/core/mixerHandler.h \ -src/core/mixerHandler.cpp \ -src/core/init.h \ -src/core/init.cpp \ -src/core/plugin.h \ -src/core/plugin.cpp \ -src/core/wave.h \ -src/core/wave.cpp \ -src/core/waveFx.h \ -src/core/waveFx.cpp \ -src/core/kernelMidi.h \ -src/core/kernelMidi.cpp \ -src/core/graphics.h \ -src/core/graphics.cpp \ -src/core/patch.h \ -src/core/patch.cpp \ -src/core/recorder.h \ -src/core/recorder.cpp \ -src/core/mixer.h \ -src/core/mixer.cpp \ -src/core/storager.h \ -src/core/storager.cpp \ -src/core/clock.h \ -src/core/clock.cpp \ -src/core/waveManager.h \ -src/core/waveManager.cpp \ -src/core/channelManager.h \ -src/core/channelManager.cpp \ -src/glue/main.h \ -src/glue/main.cpp \ -src/glue/io.h \ -src/glue/io.cpp \ -src/glue/storage.h \ -src/glue/storage.cpp \ -src/glue/channel.h \ -src/glue/channel.cpp \ -src/glue/plugin.h \ -src/glue/plugin.cpp \ -src/glue/transport.h \ -src/glue/transport.cpp \ -src/glue/recorder.h \ -src/glue/recorder.cpp \ -src/glue/sampleEditor.h \ -src/glue/sampleEditor.cpp \ -src/gui/dialogs/window.h \ -src/gui/dialogs/window.cpp \ -src/gui/dialogs/gd_keyGrabber.h \ -src/gui/dialogs/gd_keyGrabber.cpp \ -src/gui/dialogs/gd_about.h \ -src/gui/dialogs/gd_about.cpp \ -src/gui/dialogs/gd_mainWindow.h \ -src/gui/dialogs/gd_mainWindow.cpp \ -src/gui/dialogs/beatsInput.h \ -src/gui/dialogs/beatsInput.cpp \ -src/gui/dialogs/gd_warnings.h \ -src/gui/dialogs/gd_warnings.cpp \ -src/gui/dialogs/bpmInput.h \ -src/gui/dialogs/bpmInput.cpp \ -src/gui/dialogs/channelNameInput.h \ -src/gui/dialogs/channelNameInput.cpp \ -src/gui/dialogs/gd_config.h \ -src/gui/dialogs/gd_config.cpp \ -src/gui/dialogs/gd_devInfo.h \ -src/gui/dialogs/gd_devInfo.cpp \ -src/gui/dialogs/pluginList.h \ -src/gui/dialogs/pluginList.cpp \ -src/gui/dialogs/pluginWindow.h \ -src/gui/dialogs/pluginWindow.cpp \ -src/gui/dialogs/sampleEditor.h \ -src/gui/dialogs/sampleEditor.cpp \ -src/gui/dialogs/pluginWindowGUI.h \ -src/gui/dialogs/pluginWindowGUI.cpp \ -src/gui/dialogs/gd_actionEditor.h \ -src/gui/dialogs/gd_actionEditor.cpp \ -src/gui/dialogs/pluginChooser.h \ -src/gui/dialogs/pluginChooser.cpp \ -src/gui/dialogs/browser/browserBase.h \ -src/gui/dialogs/browser/browserBase.cpp \ -src/gui/dialogs/browser/browserDir.h \ -src/gui/dialogs/browser/browserDir.cpp \ -src/gui/dialogs/browser/browserLoad.h \ -src/gui/dialogs/browser/browserLoad.cpp \ -src/gui/dialogs/browser/browserSave.h \ -src/gui/dialogs/browser/browserSave.cpp \ -src/gui/dialogs/midiIO/midiOutputBase.h \ -src/gui/dialogs/midiIO/midiOutputBase.cpp \ -src/gui/dialogs/midiIO/midiOutputSampleCh.h \ -src/gui/dialogs/midiIO/midiOutputSampleCh.cpp \ -src/gui/dialogs/midiIO/midiOutputMidiCh.h \ -src/gui/dialogs/midiIO/midiOutputMidiCh.cpp \ -src/gui/dialogs/midiIO/midiInputBase.h \ -src/gui/dialogs/midiIO/midiInputBase.cpp \ -src/gui/dialogs/midiIO/midiInputChannel.h \ -src/gui/dialogs/midiIO/midiInputChannel.cpp \ -src/gui/dialogs/midiIO/midiInputMaster.h \ -src/gui/dialogs/midiIO/midiInputMaster.cpp \ -src/gui/elems/midiLearner.h \ -src/gui/elems/midiLearner.cpp \ -src/gui/elems/browser.h \ -src/gui/elems/browser.cpp \ -src/gui/elems/soundMeter.h \ -src/gui/elems/soundMeter.cpp \ -src/gui/elems/plugin/pluginBrowser.h \ -src/gui/elems/plugin/pluginBrowser.cpp \ -src/gui/elems/plugin/pluginParameter.h \ -src/gui/elems/plugin/pluginParameter.cpp \ -src/gui/elems/sampleEditor/waveform.h \ -src/gui/elems/sampleEditor/waveform.cpp \ -src/gui/elems/sampleEditor/waveTools.h \ -src/gui/elems/sampleEditor/waveTools.cpp \ -src/gui/elems/sampleEditor/volumeTool.h \ -src/gui/elems/sampleEditor/volumeTool.cpp \ -src/gui/elems/sampleEditor/boostTool.h \ -src/gui/elems/sampleEditor/boostTool.cpp \ -src/gui/elems/sampleEditor/panTool.h \ -src/gui/elems/sampleEditor/panTool.cpp \ -src/gui/elems/sampleEditor/pitchTool.h \ -src/gui/elems/sampleEditor/pitchTool.cpp \ -src/gui/elems/sampleEditor/rangeTool.h \ -src/gui/elems/sampleEditor/rangeTool.cpp \ -src/gui/elems/sampleEditor/shiftTool.h \ -src/gui/elems/sampleEditor/shiftTool.cpp \ -src/gui/elems/actionEditor/baseActionEditor.h \ -src/gui/elems/actionEditor/baseActionEditor.cpp \ -src/gui/elems/actionEditor/envelopeEditor.h \ -src/gui/elems/actionEditor/envelopeEditor.cpp \ -src/gui/elems/actionEditor/pianoRoll.h \ -src/gui/elems/actionEditor/pianoRoll.cpp \ -src/gui/elems/actionEditor/noteEditor.h \ -src/gui/elems/actionEditor/noteEditor.cpp \ -src/gui/elems/actionEditor/basePianoItem.h \ -src/gui/elems/actionEditor/basePianoItem.cpp \ -src/gui/elems/actionEditor/pianoItem.h \ -src/gui/elems/actionEditor/pianoItem.cpp \ -src/gui/elems/actionEditor/pianoItemOrphaned.h \ -src/gui/elems/actionEditor/pianoItemOrphaned.cpp \ -src/gui/elems/actionEditor/muteEditor.h \ -src/gui/elems/actionEditor/muteEditor.cpp \ -src/gui/elems/actionEditor/actionEditor.h \ -src/gui/elems/actionEditor/actionEditor.cpp \ -src/gui/elems/actionEditor/action.h \ -src/gui/elems/actionEditor/action.cpp \ -src/gui/elems/actionEditor/gridTool.h \ -src/gui/elems/actionEditor/gridTool.cpp \ -src/gui/elems/mainWindow/mainIO.h \ -src/gui/elems/mainWindow/mainIO.cpp \ -src/gui/elems/mainWindow/mainMenu.h \ -src/gui/elems/mainWindow/mainMenu.cpp \ -src/gui/elems/mainWindow/mainTimer.h \ -src/gui/elems/mainWindow/mainTimer.cpp \ -src/gui/elems/mainWindow/mainTransport.h \ -src/gui/elems/mainWindow/mainTransport.cpp \ -src/gui/elems/mainWindow/beatMeter.h \ -src/gui/elems/mainWindow/beatMeter.cpp \ -src/gui/elems/mainWindow/keyboard/channelMode.h \ -src/gui/elems/mainWindow/keyboard/channelMode.cpp \ -src/gui/elems/mainWindow/keyboard/channelButton.h \ -src/gui/elems/mainWindow/keyboard/channelButton.cpp \ -src/gui/elems/mainWindow/keyboard/channelStatus.h \ -src/gui/elems/mainWindow/keyboard/channelStatus.cpp \ -src/gui/elems/mainWindow/keyboard/keyboard.h \ -src/gui/elems/mainWindow/keyboard/keyboard.cpp \ -src/gui/elems/mainWindow/keyboard/column.h \ -src/gui/elems/mainWindow/keyboard/column.cpp \ -src/gui/elems/mainWindow/keyboard/sampleChannel.h \ -src/gui/elems/mainWindow/keyboard/sampleChannel.cpp \ -src/gui/elems/mainWindow/keyboard/midiChannel.h \ -src/gui/elems/mainWindow/keyboard/midiChannel.cpp \ -src/gui/elems/mainWindow/keyboard/channel.h \ -src/gui/elems/mainWindow/keyboard/channel.cpp \ -src/gui/elems/mainWindow/keyboard/sampleChannelButton.h \ -src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \ -src/gui/elems/mainWindow/keyboard/midiChannelButton.h \ -src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp \ -src/gui/elems/config/tabMisc.h \ -src/gui/elems/config/tabMisc.cpp \ -src/gui/elems/config/tabMidi.h \ -src/gui/elems/config/tabMidi.cpp \ -src/gui/elems/config/tabAudio.h \ -src/gui/elems/config/tabAudio.cpp \ -src/gui/elems/config/tabBehaviors.h \ -src/gui/elems/config/tabBehaviors.cpp \ -src/gui/elems/config/tabPlugins.h \ -src/gui/elems/config/tabPlugins.cpp \ -src/gui/elems/basics/scroll.h \ -src/gui/elems/basics/scroll.cpp \ -src/gui/elems/basics/boxtypes.h \ -src/gui/elems/basics/boxtypes.cpp \ -src/gui/elems/basics/baseButton.h \ -src/gui/elems/basics/baseButton.cpp \ -src/gui/elems/basics/statusButton.h \ -src/gui/elems/basics/statusButton.cpp \ -src/gui/elems/basics/button.h \ -src/gui/elems/basics/button.cpp \ -src/gui/elems/basics/idButton.h \ -src/gui/elems/basics/idButton.cpp \ -src/gui/elems/basics/resizerBar.h \ -src/gui/elems/basics/resizerBar.cpp \ -src/gui/elems/basics/input.h \ -src/gui/elems/basics/input.cpp \ -src/gui/elems/basics/liquidScroll.h \ -src/gui/elems/basics/liquidScroll.cpp \ -src/gui/elems/basics/choice.h \ -src/gui/elems/basics/choice.cpp \ -src/gui/elems/basics/dial.h \ -src/gui/elems/basics/dial.cpp \ -src/gui/elems/basics/box.h \ -src/gui/elems/basics/box.cpp \ -src/gui/elems/basics/slider.h \ -src/gui/elems/basics/slider.cpp \ -src/gui/elems/basics/progress.h \ -src/gui/elems/basics/progress.cpp \ -src/gui/elems/basics/check.h \ -src/gui/elems/basics/check.cpp \ -src/gui/elems/basics/radio.h \ -src/gui/elems/basics/radio.cpp \ -src/utils/log.h \ -src/utils/log.cpp \ -src/utils/time.h \ -src/utils/time.cpp \ -src/utils/math.h \ -src/utils/math.cpp \ -src/utils/gui.h \ -src/utils/gui.cpp \ -src/utils/gvector.h \ -src/utils/fs.h \ -src/utils/fs.cpp \ -src/utils/deps.h \ -src/utils/deps.cpp \ -src/utils/string.h \ -src/utils/string.cpp \ -src/deps/rtaudio-mod/RtAudio.h \ -src/deps/rtaudio-mod/RtAudio.cpp - -giada_SOURCES += $(extraSources) +giada_SOURCES = $(sourcesCore) $(sourcesMain) $(sourcesExtra) giada_CPPFLAGS = $(cppFlags) giada_CXXFLAGS = $(cxxFlags) giada_LDADD = $(ldAdd) @@ -382,35 +401,9 @@ resource.o: TESTS = giada_tests check_PROGRAMS = giada_tests -giada_tests_SOURCES = \ -tests/main.cpp \ -tests/conf.cpp \ -tests/wave.cpp \ -tests/waveManager.cpp \ -tests/patch.cpp \ -tests/midiMapConf.cpp \ -tests/pluginHost.cpp \ -tests/utils.cpp \ -tests/recorder.cpp \ -tests/waveFx.cpp \ -tests/audioBuffer.cpp \ -src/core/conf.cpp \ -src/core/wave.cpp \ -src/core/waveManager.cpp \ -src/core/waveFx.cpp \ -src/core/midiMapConf.cpp \ -src/core/patch.cpp \ -src/core/plugin.cpp \ -src/core/storager.cpp \ -src/core/recorder.cpp \ -src/core/audioBuffer.cpp \ -src/utils/fs.cpp \ -src/utils/string.cpp \ -src/utils/time.cpp \ -src/utils/log.cpp - -giada_tests_SOURCES += $(extraSources) -giada_tests_CPPFLAGS = $(cppFlags) +giada_tests_SOURCES = $(sourcesCore) $(sourcesExtra) $(sourcesTests) +giada_tests_CPPFLAGS = $(cppFlags) +giada_tests_CPPFLAGS += -DTESTS giada_tests_CXXFLAGS = $(cxxFlags) giada_tests_LDADD = $(ldAdd) giada_tests_LDFLAGS = $(ldFlags) diff --git a/src/core/audioBuffer.cpp b/src/core/audioBuffer.cpp index ac64df9..e54f24c 100644 --- a/src/core/audioBuffer.cpp +++ b/src/core/audioBuffer.cpp @@ -60,14 +60,13 @@ bool AudioBuffer::isAllocd() const { return m_data != nullptr; } /* -------------------------------------------------------------------------- */ -bool AudioBuffer::alloc(int size, int channels) noexcept +void AudioBuffer::alloc(int size, int channels) { free(); m_size = size; m_channels = channels; - m_data = new (std::nothrow) float[m_size * m_channels]; + m_data = new float[m_size * m_channels]; clear(); // does nothing if m_data == nullptr - return m_data != nullptr; } diff --git a/src/core/audioBuffer.h b/src/core/audioBuffer.h index 702f77a..b8b0519 100644 --- a/src/core/audioBuffer.h +++ b/src/core/audioBuffer.h @@ -30,7 +30,7 @@ public: int countChannels() const; bool isAllocd() const; - bool alloc(int size, int channels) noexcept; + void alloc(int size, int channels); void free(); /* copyData diff --git a/src/core/channel.cpp b/src/core/channel.cpp index 906b18f..741e1eb 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -47,28 +47,27 @@ using std::string; +using namespace giada; using namespace giada::m; -Channel::Channel(int type, int status, int bufferSize) -: bufferSize (bufferSize), - volume_i (1.0f), - volume_d (0.0f), - mute_i (false), - guiChannel (nullptr), - previewMode (G_PREVIEW_NONE), +Channel::Channel(ChannelType type, ChannelStatus status, int bufferSize) +: guiChannel (nullptr), + type (type), + status (status), + recStatus (ChannelStatus::OFF), + previewMode (PreviewMode::NONE), pan (0.5f), volume (G_DEFAULT_VOL), armed (false), - type (type), - status (status), key (0), mute (false), - mute_s (false), solo (false), + volume_i (1.0f), + volume_d (0.0f), + mute_i (false), hasActions (false), readActions (false), - recStatus (REC_STOPPED), midiIn (true), midiInKeyPress (0x0), midiInKeyRel (0x0), @@ -83,28 +82,7 @@ Channel::Channel(int type, int status, int bufferSize) midiOutLmute (0x0), midiOutLsolo (0x0) { -} - - -/* -------------------------------------------------------------------------- */ - - -Channel::~Channel() -{ - status = STATUS_OFF; -} - - -/* -------------------------------------------------------------------------- */ - - -bool Channel::allocBuffers() -{ - if (!vChan.alloc(bufferSize, G_MAX_IO_CHANS)) { - gu_log("[Channel::allocBuffers] unable to alloc memory for vChan!\n"); - return false; - } - return true; + buffer.alloc(bufferSize, G_MAX_IO_CHANS); } @@ -113,13 +91,14 @@ bool Channel::allocBuffers() void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex) { + using namespace giada::m; + key = src->key; volume = src->volume; volume_i = src->volume_i; volume_d = src->volume_d; pan = src->pan; mute_i = src->mute_i; - mute_s = src->mute_s; mute = src->mute; solo = src->solo; hasActions = src->hasActions; @@ -162,30 +141,9 @@ void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex) /* -------------------------------------------------------------------------- */ -void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t& msg) -{ - gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n", - learn, msg.channel, msg.value, msg.offset); - - /* isolate 'channel' from learnt message and offset it as requested by 'nn' - * in the midimap configuration file. */ - - uint32_t out = ((learn & 0x00FF0000) >> 16) << msg.offset; - - /* merge the previously prepared channel into final message, and finally - * send it. */ - - out |= msg.value | (msg.channel << 24); - kernelMidi::send(out); -} - - -/* -------------------------------------------------------------------------- */ - - bool Channel::isPlaying() const { - return status & (STATUS_PLAY | STATUS_ENDING); + return status == ChannelStatus::PLAY || status == ChannelStatus::ENDING; } @@ -215,9 +173,9 @@ void Channel::sendMidiLmute() if (!midiOutL || midiOutLmute == 0x0) return; if (mute) - sendMidiLmessage(midiOutLsolo, midimap::muteOn); + kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOn); else - sendMidiLmessage(midiOutLsolo, midimap::muteOff); + kernelMidi::sendMidiLightning(midiOutLmute, midimap::muteOff); } @@ -229,31 +187,34 @@ void Channel::sendMidiLsolo() if (!midiOutL || midiOutLsolo == 0x0) return; if (solo) - sendMidiLmessage(midiOutLsolo, midimap::soloOn); + kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOn); else - sendMidiLmessage(midiOutLsolo, midimap::soloOff); + kernelMidi::sendMidiLightning(midiOutLsolo, midimap::soloOff); } /* -------------------------------------------------------------------------- */ -void Channel::sendMidiLplay() +void Channel::sendMidiLstatus() { if (!midiOutL || midiOutLplaying == 0x0) return; switch (status) { - case STATUS_OFF: - sendMidiLmessage(midiOutLplaying, midimap::stopped); + case ChannelStatus::OFF: + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopped); + break; + case ChannelStatus::PLAY: + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing); + break; + case ChannelStatus::WAIT: + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::waiting); break; - case STATUS_PLAY: - sendMidiLmessage(midiOutLplaying, midimap::playing); + case ChannelStatus::ENDING: + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopping); break; - case STATUS_WAIT: - sendMidiLmessage(midiOutLplaying, midimap::waiting); + default: break; - case STATUS_ENDING: - sendMidiLmessage(midiOutLplaying, midimap::stopping); } } @@ -261,14 +222,6 @@ void Channel::sendMidiLplay() /* -------------------------------------------------------------------------- */ -void Channel::receiveMidi(const MidiEvent& midiEvent) -{ -} - - -/* -------------------------------------------------------------------------- */ - - bool Channel::isMidiInAllowed(int c) const { return midiInFilter == -1 || midiInFilter == c; @@ -299,16 +252,7 @@ float Channel::getPan() const /* -------------------------------------------------------------------------- */ -void Channel::setVolumeI(float v) -{ - volume_i = v; -} - - -/* -------------------------------------------------------------------------- */ - - -float Channel::calcPanning(int ch) +float Channel::calcPanning(int ch) const { if (pan == 0.5f) // center: nothing to do return 1.0; @@ -322,15 +266,29 @@ float Channel::calcPanning(int ch) /* -------------------------------------------------------------------------- */ -void Channel::setPreviewMode(int m) +void Channel::calcVolumeEnvelope() { - previewMode = m; + volume_i += volume_d; + if (volume_i < 0.0f) + volume_i = 0.0f; + else + if (volume_i > 1.0f) + volume_i = 1.0f; } bool Channel::isPreview() const { - return previewMode != G_PREVIEW_NONE; + return previewMode != PreviewMode::NONE; +} + + +/* -------------------------------------------------------------------------- */ + + +bool Channel::isReadingActions() const +{ + return hasActions && readActions; } diff --git a/src/core/channel.h b/src/core/channel.h index 8a78c55..904bdb4 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -32,6 +32,8 @@ #include #include #include +#include "types.h" +#include "mixer.h" #include "midiMapConf.h" #include "midiEvent.h" #include "recorder.h" @@ -42,6 +44,9 @@ #endif +#undef Status + + class Plugin; class MidiMapConf; class geChannel; @@ -49,79 +54,32 @@ class geChannel; class Channel { -protected: - - Channel(int type, int status, int bufferSize); - - /* sendMidiLMessage - Composes a MIDI message by merging bytes from MidiMap conf class, and sends it - to KernelMidi. */ - - void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t& msg); - - /* calcPanning - Given an audio channel (stereo: 0 or 1) computes the current panning value. */ - - float calcPanning(int ch); - - /* vChan - Virtual channel for internal processing. */ - - giada::m::AudioBuffer vChan; - -#ifdef WITH_VST - - /* MidiBuffer contains MIDI events. When ready, events are sent to each plugin - in the channel. This is available for any kind of channel, but it makes sense - only for MIDI channels. */ - - juce::MidiBuffer midiBuffer; - -#endif - - /* bufferSize - Size of every buffer in this channel (vChan, pChan) */ - - int bufferSize; - - /* volume_* - Internal volume variables: volume_i for envelopes, volume_d keeps track of - the delta during volume changes. */ - - float volume_i; - float volume_d; - - bool mute_i; // internal mute - public: - virtual ~Channel(); + virtual ~Channel() {}; /* copy - Makes a shallow copy (no vChan/pChan allocation) of another channel. */ + Makes a shallow copy (no internal buffers allocation) of another channel. */ virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0; - /* process - Merges vChannels into buffer, plus plugin processing (if any). Warning: - inBuffer might be nullptr if no input devices are available for recording. */ + /* parseEvents + Prepares channel for rendering. This is called on each frame. */ - virtual void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) = 0; + virtual void parseEvents(giada::m::mixer::FrameEvents fe) = 0; - /* Preview - Makes itself audibile for audio preview, such as Sample Editor or other - tools. */ + /* process + Merges working buffers into 'out', plus plugin processing (if any). Warning: + inBuffer might be nullptr if no input devices are available for recording. */ - virtual void preview(giada::m::AudioBuffer& in) = 0; + virtual void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, + bool audible, bool running) = 0; /* start Action to do when channel starts. doQuantize = false (don't quantize) - when Mixer is reading actions from Recorder. If isUserGenerated means that - the channel has been started by a human key press and not a pre-recorded - action. */ + when Mixer is reading actions from Recorder. */ - virtual void start(int frame, bool doQuantize, int quantize, - bool mixerIsRunning, bool forceStart, bool isUserGenerated) = 0; + virtual void start(int localFrame, bool doQuantize, int velocity) = 0; /* stop What to do when channel is stopped normally (via key or MIDI). */ @@ -131,14 +89,12 @@ public: /* kill What to do when channel stops abruptly. */ - virtual void kill(int frame) = 0; + virtual void kill(int localFrame) = 0; - /* mute - What to do when channel is muted. If internal == true, set internal mute - without altering main mute. */ + /* set + What to do when channel is un/muted. */ - virtual void setMute (bool internal) = 0; - virtual void unsetMute(bool internal) = 0; + virtual void setMute(bool value, giada::EventType eventType) = 0; /* empty Frees any associated resources (e.g. waveform for SAMPLE). */ @@ -150,44 +106,10 @@ public: virtual void stopBySeq(bool chansStopOnSeqHalt) = 0; - /* quantize - Starts channel according to quantizer. Index = array index of mixer::channels - used by recorder, localFrame = frame within the current buffer, - globalFrame = frame within the whole sequencer loop. */ - - virtual void quantize(int index, int localFrame, int globalFrame) = 0; - - /* onZero - What to do when frame goes to zero, i.e. sequencer restart. */ - - virtual void onZero(int frame, bool recsStopOnChanHalt) = 0; - - /* onBar - What to do when a bar has passed. */ - - virtual void onBar(int frame) = 0; - - /* parseAction - Does something on a recorded action. Parameters: - - action *a - action to parse - - localFrame - frame number of the processed buffer - - globalFrame - actual frame in Mixer */ - - // TODO - quantize is useless! - - virtual void parseAction(giada::m::recorder::action* a, int localFrame, - int globalFrame, int quantize, bool mixerIsRunning) = 0; - /* rewind Rewinds channel when rewind button is pressed. */ - virtual void rewind() = 0; - - /* clear - Clears all memory buffers. This is actually useful to sample channels only. - TODO - please rename it to clearBuffers. */ - - virtual void clear() = 0; + virtual void rewindBySeq() = 0; /* canInputRec Tells whether a channel can accept and handle input audio. Always false for @@ -196,48 +118,66 @@ public: virtual bool canInputRec() = 0; - /* readPatch - Fills channel with data from patch. */ + virtual bool hasLogicalData() const { return false; }; + virtual bool hasEditedData() const { return false; }; + virtual bool hasData() const { return false; }; - virtual void readPatch(const std::string& basePath, int i); + virtual bool recordStart(bool canQuantize) { return true; }; + virtual bool recordKill() { return false; }; + virtual void recordStop() {}; + virtual void recordMute() {}; + + /* prepareBuffer + Fill audio buffer with audio data from the internal source. This is actually + useful to sample channels only. */ + + virtual void prepareBuffer(bool running) {}; - /* writePatch - Fills a patch with channel values. Returns the index of the last - Patch::channel_t added. */ + virtual void startReadingActions(bool treatRecsAsLoops, + bool recsStopOnChanHalt) {}; + virtual void stopReadingActions(bool running, bool treatRecsAsLoops, + bool recsStopOnChanHalt) {}; + virtual void stopInputRec(int globalFrame) {}; + + virtual void readPatch(const std::string& basePath, int i); virtual void writePatch(int i, bool isProject); /* receiveMidi Receives and processes midi messages from external devices. */ - virtual void receiveMidi(const giada::m::MidiEvent& midiEvent); + virtual void receiveMidi(const giada::m::MidiEvent& midiEvent) {}; - /* allocBuffers - Mandatory method to allocate memory for internal buffers. Call it after the - object has been constructed. */ + /* calcPanning + Given an audio channel (stereo: 0 or 1) computes the current panning value. */ - virtual bool allocBuffers(); + float calcPanning(int ch) const; bool isPlaying() const; float getPan() const; bool isPreview() const; - /* isMidiAllowed - Given a MIDI channel 'c' tells whether this channel should be allowed to receive - and process MIDI events on MIDI channel 'c'. */ + /* isMidiInAllowed + Given a MIDI channel 'c' tells whether this channel should be allowed to + receive and process MIDI events on MIDI channel 'c'. */ bool isMidiInAllowed(int c) const; + /* isReadingActions + Tells whether the channel as actions and it is currently reading them. */ + + bool isReadingActions() const; + /* sendMidiL* - * send MIDI lightning events to a physical device. */ + Sends MIDI lightning events to a physical device. */ void sendMidiLmute(); void sendMidiLsolo(); - void sendMidiLplay(); + void sendMidiLstatus(); void setPan(float v); - void setVolumeI(float v); - void setPreviewMode(int m); + + void calcVolumeEnvelope(); #ifdef WITH_VST @@ -251,28 +191,47 @@ public: #endif - geChannel* guiChannel; // pointer to a gChannel object, part of the GUI + /* guiChannel + Pointer to a gChannel object, part of the GUI. TODO - remove this and send + signals instead. */ + + geChannel* guiChannel; + + /* buffer + Working buffer for internal processing. */ + giada::m::AudioBuffer buffer; + + giada::ChannelType type; + giada::ChannelStatus status; + giada::ChannelStatus recStatus; + /* previewMode Whether the channel is in audio preview mode or not. */ - int previewMode; + giada::PreviewMode previewMode; float pan; float volume; // global volume bool armed; std::string name; int index; // unique id - int type; // midi or sample - int status; // status: see const.h int key; // keyboard button bool mute; // global mute - bool mute_s; // previous mute status after being solo'd TODO - remove it with mute refactoring bool solo; + /* volume_* + Internal volume variables: volume_i for envelopes, volume_d keeps track of + the delta during volume changes (or the line slope between two volume + points). */ + + float volume_i; + float volume_d; + + bool mute_i; // internal mute + bool hasActions; // has something recorded bool readActions; // read what's recorded - int recStatus; // status of recordings (waiting, ending, ...) bool midiIn; // enable midi input uint32_t midiInKeyPress; @@ -303,6 +262,19 @@ public: std::vector 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 }; diff --git a/src/core/channelManager.cpp b/src/core/channelManager.cpp index addc5cb..0c3e5fd 100644 --- a/src/core/channelManager.cpp +++ b/src/core/channelManager.cpp @@ -105,7 +105,7 @@ void readPlugins_(Channel* ch, const patch::channel_t& pch) for (const patch::plugin_t& ppl : pch.plugins) { Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL, - &mixer::mutex_plugins, ch); + &mixer::mutex, ch); if (plugin == nullptr) continue; @@ -132,21 +132,12 @@ void readPlugins_(Channel* ch, const patch::channel_t& pch) /* -------------------------------------------------------------------------- */ -int create(int type, int bufferSize, bool inputMonitorOn, Channel** out) +int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out) { - Channel* ch; - if (type == G_CHANNEL_SAMPLE) - ch = new SampleChannel(bufferSize, inputMonitorOn); + if (type == ChannelType::SAMPLE) + *out = new SampleChannel(inputMonitorOn, bufferSize); else - ch = new MidiChannel(bufferSize); - - if (!ch->allocBuffers()) { - delete ch; - return G_RES_ERR_MEMORY; - } - - *out = ch; - + *out = new MidiChannel(bufferSize); return G_RES_OK; } @@ -157,7 +148,7 @@ int create(int type, int bufferSize, bool inputMonitorOn, Channel** out) int writePatch(const Channel* ch, bool isProject) { patch::channel_t pch; - pch.type = ch->type; + pch.type = static_cast(ch->type); pch.index = ch->index; pch.size = ch->guiChannel->getSize(); pch.name = ch->name; @@ -165,7 +156,6 @@ int writePatch(const Channel* ch, bool isProject) pch.armed = ch->armed; pch.column = ch->guiChannel->getColumnIndex(); pch.mute = ch->mute; - // pch.mute_s = ch->mute_s; TODO remove it with mute refactoring pch.solo = ch->solo; pch.volume = ch->volume; pch.pan = ch->pan; @@ -218,7 +208,7 @@ void writePatch(const SampleChannel* ch, bool isProject, int index) else pch.samplePath = ""; - pch.mode = ch->mode; + pch.mode = static_cast(ch->mode); pch.begin = ch->getBegin(); pch.end = ch->getEnd(); pch.boost = ch->getBoost(); @@ -239,11 +229,10 @@ void readPatch(Channel* ch, int i) ch->key = pch.key; ch->armed = pch.armed; - ch->type = pch.type; + ch->type = static_cast(pch.type); ch->name = pch.name; ch->index = pch.index; ch->mute = pch.mute; - //ch->mute_s = pch.mute_s; ch->solo = pch.solo; ch->volume = pch.volume; ch->pan = pch.pan; @@ -272,9 +261,9 @@ void readPatch(SampleChannel* ch, const string& basePath, int i) { const patch::channel_t& pch = patch::channels.at(i); - ch->mode = pch.mode; + ch->mode = static_cast(pch.mode); ch->readActions = pch.recActive; - ch->recStatus = pch.recActive ? REC_READING : REC_STOPPED; + ch->recStatus = pch.recActive ? ChannelStatus::PLAY : ChannelStatus::OFF; ch->midiInVeloAsVol = pch.midiInVeloAsVol; ch->midiInReadActions = pch.midiInReadActions; ch->midiInPitch = pch.midiInPitch; @@ -292,11 +281,11 @@ void readPatch(SampleChannel* ch, const string& basePath, int i) } else { if (res == G_RES_ERR_NO_DATA) - ch->status = STATUS_EMPTY; + ch->status = ChannelStatus::EMPTY; else if (res == G_RES_ERR_IO) - ch->status = STATUS_MISSING; - ch->sendMidiLplay(); // FIXME - why sending MIDI lightning if sample status is wrong? + ch->status = ChannelStatus::MISSING; + ch->sendMidiLstatus(); // FIXME - why sending MIDI lightning if sample status is wrong? } } diff --git a/src/core/channelManager.h b/src/core/channelManager.h index 3e3a11c..327e27b 100644 --- a/src/core/channelManager.h +++ b/src/core/channelManager.h @@ -30,6 +30,7 @@ #include +#include "types.h" class Channel; @@ -41,7 +42,7 @@ namespace giada { namespace m { namespace channelManager { -int create(int type, int bufferSize, bool inputMonitorOn, Channel** out); +int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out); int writePatch(const Channel* ch, bool isProject); void writePatch(const SampleChannel* ch, bool isProject, int index); diff --git a/src/core/clock.cpp b/src/core/clock.cpp index 9a38b6a..66abecc 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -197,7 +197,7 @@ void setQuantize(int q) void incrCurrentFrame() { currentFrame++; - if (currentFrame > framesInLoop) { + if (currentFrame >= framesInLoop) { currentFrame = 0; currentBeat = 0; } @@ -363,6 +363,15 @@ void recvJackSync() /* -------------------------------------------------------------------------- */ +bool canQuantize() +{ + return getQuantize() > 0 && isRunning(); +} + + +/* -------------------------------------------------------------------------- */ + + int getCurrentFrame() { return currentFrame; diff --git a/src/core/clock.h b/src/core/clock.h index c7dd35e..43f225b 100644 --- a/src/core/clock.h +++ b/src/core/clock.h @@ -71,6 +71,11 @@ Tells whether a quanto unit has passed yet. */ bool quantoHasPassed(); +/* quantoHasPassed +Whether the quantizer value is > 0 and the clock is running. */ + +bool canQuantize(); + /* updateFrameBars Updates bpm, frames, beats and so on. */ diff --git a/src/core/const.h b/src/core/const.h index e93bcd0..6d2e6ba 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- 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" @@ -93,13 +93,16 @@ /* -- 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 @@ -152,7 +155,6 @@ #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 @@ -166,41 +168,6 @@ -/* -- 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 @@ -237,12 +204,6 @@ -/* -- channel types --------------------------------------------------------- */ -#define G_CHANNEL_SAMPLE 0x01 -#define G_CHANNEL_MIDI 0x02 - - - /* -- unique IDs of mainWin's subwindows ------------------------------------ */ /* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */ #define WID_BEATS -1 @@ -361,7 +322,6 @@ const int MIDI_CHANS[16] = { #define PATCH_KEY_CHANNEL_NAME "name" #define PATCH_KEY_CHANNEL_COLUMN "column" #define PATCH_KEY_CHANNEL_MUTE "mute" -#define PATCH_KEY_CHANNEL_MUTE_S "mute_s" #define PATCH_KEY_CHANNEL_SOLO "solo" #define PATCH_KEY_CHANNEL_VOLUME "volume" #define PATCH_KEY_CHANNEL_PAN "pan" diff --git a/src/core/init.cpp b/src/core/init.cpp index 1809e50..f9cac87 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -29,6 +29,9 @@ #ifdef __APPLE__ #include #endif +#if defined(__linux__) && defined(WITH_VST) + #include // For XInitThreads +#endif #include "../utils/log.h" #include "../utils/fs.h" #include "../utils/gui.h" @@ -130,6 +133,14 @@ void init_prepareMidiMap() void init_startGUI(int argc, char** argv) { + /* This is of paramount importance on Linux with VST enabled, otherwise many + plug-ins go nuts and crash hard. It seems that some plug-ins or our Juce-based + PluginHost use Xlib concurrently. */ + +#if defined(__linux__) && defined(WITH_VST) + XInitThreads(); +#endif + G_MainWin = new gdMainWindow(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv); G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW, conf::mainWindowH); @@ -183,27 +194,24 @@ void init_shutdown() else gu_log("[init] configuration saved\n"); - /* if kernelAudio::getStatus() we close the kernelAudio FIRST, THEN the mixer. - * The opposite could cause random segfaults (even now with RtAudio?). */ - - if (kernelAudio::getStatus()) { - kernelAudio::closeDevice(); - gu_log("[init] KernelAudio closed\n"); - mixer::close(); - gu_log("[init] Mixer closed\n"); - } - recorder::clearAll(); gu_log("[init] Recorder cleaned up\n"); #ifdef WITH_VST - pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins); + pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex); pluginHost::close(); gu_log("[init] PluginHost cleaned up\n"); #endif + if (kernelAudio::getStatus()) { + kernelAudio::closeDevice(); + gu_log("[init] KernelAudio closed\n"); + mixer::close(); + gu_log("[init] Mixer closed\n"); + } + gu_log("[init] Giada " G_VERSION_STR " closed\n\n"); gu_logClose(); } diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp index c1fa4e4..e30c6f6 100644 --- a/src/core/kernelAudio.cpp +++ b/src/core/kernelAudio.cpp @@ -439,6 +439,9 @@ bool hasAPI(int API) } +int getAPI() { return api; } + + /* -------------------------------------------------------------------------- */ diff --git a/src/core/kernelAudio.h b/src/core/kernelAudio.h index 88dbc44..bd256fe 100644 --- a/src/core/kernelAudio.h +++ b/src/core/kernelAudio.h @@ -78,6 +78,7 @@ int getDeviceByName(const char* name); int getDefaultOut(); int getDefaultIn(); bool hasAPI(int API); +int getAPI(); #ifdef __linux__ diff --git a/src/core/kernelMidi.cpp b/src/core/kernelMidi.cpp index 22331e5..52f760b 100644 --- a/src/core/kernelMidi.cpp +++ b/src/core/kernelMidi.cpp @@ -256,6 +256,25 @@ void send(int b1, int b2, int b3) /* -------------------------------------------------------------------------- */ +void sendMidiLightning(uint32_t learn, const midimap::message_t& msg) +{ + gu_log("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, msg.channel, + msg.value, msg.offset); + + /* Isolate 'channel' from learnt message and offset it as requested by 'nn' in + the midimap configuration file. */ + uint32_t out = ((learn & 0x00FF0000) >> 16) << msg.offset; + + /* Merge the previously prepared channel into final message, and finally send + it. */ + out |= msg.value | (msg.channel << 24); + send(out); +} + + +/* -------------------------------------------------------------------------- */ + + unsigned countInPorts() { return numInPorts; diff --git a/src/core/kernelMidi.h b/src/core/kernelMidi.h index d1c8e86..450854c 100644 --- a/src/core/kernelMidi.h +++ b/src/core/kernelMidi.h @@ -29,12 +29,9 @@ #define G_KERNELMIDI_H -#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff) - #include -#else - #include -#endif +#include #include +#include "midiMapConf.h" namespace giada { @@ -54,14 +51,15 @@ channel. */ uint32_t setChannel(uint32_t iValue, int channel); /* send - * send a MIDI message 's' (uint32_t). */ +Sends a MIDI message 's' as uint32_t or as separate bytes. */ void send(uint32_t s); +void send(int b1, int b2=-1, int b3=-1); -/* send (2) - * send separate bytes of MIDI message. */ +/* sendMidiLightning +Sends a MIDI lightning message defined by 'msg'. */ -void send(int b1, int b2=-1, int b3=-1); +void sendMidiLightning(uint32_t learn, const midimap::message_t& msg); /* setApi * set the Api in use for both in & out messages. */ diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp index 4445d75..4c83b02 100644 --- a/src/core/midiChannel.cpp +++ b/src/core/midiChannel.cpp @@ -26,7 +26,7 @@ #include "../utils/log.h" -#include "midiChannel.h" +#include "midiChannelProc.h" #include "channelManager.h" #include "channel.h" #include "patch.h" @@ -36,14 +36,16 @@ #include "mixer.h" #include "pluginHost.h" #include "kernelMidi.h" +#include "midiChannel.h" using std::string; +using namespace giada; using namespace giada::m; MidiChannel::MidiChannel(int bufferSize) - : Channel (G_CHANNEL_MIDI, STATUS_OFF, bufferSize), + : Channel (ChannelType::MIDI, ChannelStatus::OFF, bufferSize), midiOut (false), midiOutChan(MIDI_CHANS[0]) { @@ -53,12 +55,6 @@ MidiChannel::MidiChannel(int bufferSize) /* -------------------------------------------------------------------------- */ -MidiChannel::~MidiChannel() {} - - -/* -------------------------------------------------------------------------- */ - - void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) { Channel::copy(src_, pluginMutex); @@ -71,179 +67,109 @@ void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) /* -------------------------------------------------------------------------- */ -#ifdef WITH_VST - -void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame) +void MidiChannel::parseEvents(mixer::FrameEvents fe) { - juce::MidiMessage message = juce::MidiMessage( - kernelMidi::getB1(msg), - kernelMidi::getB2(msg), - kernelMidi::getB3(msg)); - midiBuffer.addEvent(message, localFrame); + midiChannelProc::parseEvents(this, fe); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::onBar(int frame) {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::stop() {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::empty() {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::quantize(int index, int localFrame, int globalFrame) {} - - /* -------------------------------------------------------------------------- */ -void MidiChannel::parseAction(recorder::action* a, int localFrame, - int globalFrame, int quantize, bool mixerIsRunning) +void MidiChannel::process(giada::m::AudioBuffer& out, + const giada::m::AudioBuffer& in, bool audible, bool running) { - if (a->type == G_ACTION_MIDI) - sendMidi(a, localFrame); + midiChannelProc::process(this, out, in, audible); } /* -------------------------------------------------------------------------- */ -void MidiChannel::onZero(int frame, bool recsStopOnChanHalt) +void MidiChannel::stopBySeq(bool chansStopOnSeqHalt) { - if (status == STATUS_ENDING) { - status = STATUS_OFF; - sendMidiLplay(); - } - else - if (status == STATUS_WAIT) { - status = STATUS_PLAY; - sendMidiLplay(); - } + midiChannelProc::stopBySeq(this); } /* -------------------------------------------------------------------------- */ -void MidiChannel::setMute(bool internal) +void MidiChannel::start(int frame, bool doQuantize, int velocity) { - mute = true; // internal mute does not exist for midi (for now) - if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); -#ifdef WITH_VST - addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); -#endif - sendMidiLmute(); + midiChannelProc::start(this); } /* -------------------------------------------------------------------------- */ -void MidiChannel::unsetMute(bool internal) +void MidiChannel::kill(int localFrame) { - mute = false; // internal mute does not exist for midi (for now) - sendMidiLmute(); + midiChannelProc::kill(this, localFrame); } /* -------------------------------------------------------------------------- */ -void MidiChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) +void MidiChannel::rewindBySeq() { -#ifdef WITH_VST - pluginHost::processStack(vChan, pluginHost::CHANNEL, this); -#endif - - /* TODO - isn't this useful only if WITH_VST ? */ - for (int i=0; iiValue | MIDI_CHANS[midiOutChan]); @@ -265,7 +191,7 @@ void MidiChannel::sendMidi(recorder::action* a, int localFrame) void MidiChannel::sendMidi(uint32_t data) { - if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) { + if (isPlaying() && !mute) { if (midiOut) kernelMidi::send(data | MIDI_CHANS[midiOutChan]); #ifdef WITH_VST @@ -278,29 +204,6 @@ void MidiChannel::sendMidi(uint32_t data) /* -------------------------------------------------------------------------- */ -void MidiChannel::rewind() -{ - if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); -#ifdef WITH_VST - addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::writePatch(int i, bool isProject) -{ - Channel::writePatch(i, isProject); - channelManager::writePatch(this, isProject, i); -} - - -/* -------------------------------------------------------------------------- */ - - void MidiChannel::receiveMidi(const MidiEvent& midiEvent) { if (!armed) @@ -339,10 +242,4 @@ void MidiChannel::receiveMidi(const MidiEvent& midiEvent) bool MidiChannel::canInputRec() { return false; // midi channels don't handle input audio -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::clear() {} +} \ No newline at end of file diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index 3ecad62..08f2729 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -44,28 +44,20 @@ class MidiChannel : public Channel public: MidiChannel(int bufferSize); - ~MidiChannel(); void copy(const Channel* src, pthread_mutex_t* pluginMutex) override; - void clear() override; - void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override; - void preview(giada::m::AudioBuffer& out) override; - void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning, - bool forceStart, bool isUserGenerated) override; - void kill(int frame) override; + void parseEvents(giada::m::mixer::FrameEvents fe) override; + void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, + bool audible, bool running) override; + void start(int frame, bool doQuantize, int velocity) override; + void kill(int localFrame) override; void empty() override; void stopBySeq(bool chansStopOnSeqHalt) override; - void stop() override; - void rewind() override; - void setMute(bool internal) override; - void unsetMute(bool internal) override; + void stop() override {}; + void rewindBySeq() override; + void setMute(bool value, giada::EventType eventType) override; void readPatch(const std::string& basePath, int i) override; void writePatch(int i, bool isProject) override; - void quantize(int index, int localFrame, int globalFrame) override; - void onZero(int frame, bool recsStopOnChanHalt) override; - void onBar(int frame) override; - void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame, - int quantize, bool mixerIsRunning) override; void receiveMidi(const giada::m::MidiEvent& midiEvent) override; bool canInputRec() override; diff --git a/src/core/midiChannelProc.cpp b/src/core/midiChannelProc.cpp new file mode 100644 index 0000000..965d440 --- /dev/null +++ b/src/core/midiChannelProc.cpp @@ -0,0 +1,163 @@ +#include "midiChannel.h" +#include "pluginHost.h" +#include "kernelMidi.h" +#include "const.h" +#include "midiChannelProc.h" + + +namespace giada { +namespace m { +namespace midiChannelProc +{ +namespace +{ +void onFirstBeat_(MidiChannel* ch) +{ + if (ch->status == ChannelStatus::ENDING) { + ch->status = ChannelStatus::OFF; + ch->sendMidiLstatus(); + } + else + if (ch->status == ChannelStatus::WAIT) { + ch->status = ChannelStatus::PLAY; + ch->sendMidiLstatus(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void parseAction_(MidiChannel* ch, const recorder::action* a, int localFrame) +{ + if (ch->isPlaying() && !ch->mute) { + if (ch->midiOut) + kernelMidi::send(a->iValue | MIDI_CHANS[ch->midiOutChan]); +#ifdef WITH_VST + ch->addVstMidiEvent(a->iValue, localFrame); +#endif + } +} + +}; // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +void parseEvents(MidiChannel* ch, mixer::FrameEvents fe) +{ + if (fe.onFirstBeat) + onFirstBeat_(ch); + for (const recorder::action* action : fe.actions) + if (action->chan == ch->index && action->type == G_ACTION_MIDI) + parseAction_(ch, action, fe.frameLocal); +} + + +/* -------------------------------------------------------------------------- */ + + +void process(MidiChannel* ch, giada::m::AudioBuffer& out, + const giada::m::AudioBuffer& in, bool audible) +{ + #ifdef WITH_VST + pluginHost::processStack(ch->buffer, pluginHost::CHANNEL, ch); + #endif + + /* Process the plugin stack first, then quit if the channel is muted/soloed. + This way there's no risk of cutting midi event pairs such as note-on and + note-off while triggering a mute/solo. */ + + /* TODO - this is meaningful only if WITH_VST is defined */ + if (audible) + for (int i=0; ibuffer[i][j] * ch->volume; +} + + +/* -------------------------------------------------------------------------- */ + + +void start(MidiChannel* ch) +{ + switch (ch->status) { + case ChannelStatus::PLAY: + ch->status = ChannelStatus::ENDING; + ch->sendMidiLstatus(); + break; + + case ChannelStatus::ENDING: + case ChannelStatus::WAIT: + ch->status = ChannelStatus::OFF; + ch->sendMidiLstatus(); + break; + + case ChannelStatus::OFF: + ch->status = ChannelStatus::WAIT; + ch->sendMidiLstatus(); + break; + + default: break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void kill(MidiChannel* ch, int localFrame) +{ + if (ch->isPlaying()) { + if (ch->midiOut) + kernelMidi::send(MIDI_ALL_NOTES_OFF); +#ifdef WITH_VST + ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); +#endif + } + ch->status = ChannelStatus::OFF; + ch->sendMidiLstatus(); +} + + +/* -------------------------------------------------------------------------- */ + + +void rewindBySeq(MidiChannel* ch) +{ + if (ch->midiOut) + kernelMidi::send(MIDI_ALL_NOTES_OFF); +#ifdef WITH_VST + ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void setMute(MidiChannel* ch, bool v) +{ + ch->mute = v; + if (ch->mute) { + if (ch->midiOut) + kernelMidi::send(MIDI_ALL_NOTES_OFF); + #ifdef WITH_VST + ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); + #endif + } + ch->sendMidiLmute(); +} + + +/* -------------------------------------------------------------------------- */ + + +void stopBySeq(MidiChannel* ch) +{ + kill(ch, 0); +} +}}}; \ No newline at end of file diff --git a/src/core/midiChannelProc.h b/src/core/midiChannelProc.h new file mode 100644 index 0000000..ffd4929 --- /dev/null +++ b/src/core/midiChannelProc.h @@ -0,0 +1,52 @@ +#ifndef G_MIDI_CHANNEL_PROC_H +#define G_MIDI_CHANNEL_PROC_H + + +#include "mixer.h" +#include "audioBuffer.h" + + +class MidiChannel; + + +namespace giada { +namespace m { +namespace midiChannelProc +{ +/* parseEvents +Parses events gathered by Mixer::masterPlay(). */ + +void parseEvents(MidiChannel* ch, mixer::FrameEvents ev); + +/**/ +void process(MidiChannel* ch, giada::m::AudioBuffer& out, + const giada::m::AudioBuffer& in, bool audible); + +/* kill +Stops a channel abruptly. */ + +void kill(MidiChannel* ch, int localFrame); + +/* start +Starts a channel. */ + +void start(MidiChannel* ch); + +/* stopBySeq +Stops a channel when the stop button on main transport is pressed. */ + +void stopBySeq(MidiChannel* ch); + +/* rewind +Rewinds channel when rewind button on main transport is pressed. */ + +void rewindBySeq(MidiChannel* ch); + +/* mute|unmute +Mutes/unmutes a channel. */ + +void setMute(MidiChannel* ch, bool v); +}}}; + + +#endif \ No newline at end of file diff --git a/src/core/midiDispatcher.cpp b/src/core/midiDispatcher.cpp index 1d49fc1..e5d93ea 100644 --- a/src/core/midiDispatcher.cpp +++ b/src/core/midiDispatcher.cpp @@ -153,7 +153,7 @@ void processChannels(const MidiEvent& midiEvent) else if (pure == sch->midiInReadActions) { gu_log(" >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure); - c::channel::toggleReadingRecs(sch, false); + c::channel::toggleReadingActions(sch, false); } } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 7592927..1931ed7 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -87,15 +87,6 @@ Sample position while recording. */ int inputTracker = 0; -/* -------------------------------------------------------------------------- */ - - -bool isChannelAudible(Channel* ch) -{ - return !hasSolos || (hasSolos && ch->solo); -} - - /* -------------------------------------------------------------------------- */ /* computePeak */ @@ -159,40 +150,15 @@ void processLineIn(const AudioBuffer& inBuf, unsigned frame) /* -------------------------------------------------------------------------- */ -/* clearAllBuffers +/* prepareBuffers Cleans up every buffer, both in Mixer and in channels. */ -void clearAllBuffers(AudioBuffer& outBuf) +void prepareBuffers(AudioBuffer& outBuf) { outBuf.clear(); vChanInToOut.clear(); - - pthread_mutex_lock(&mutex_chans); for (Channel* channel : channels) - channel->clear(); - pthread_mutex_unlock(&mutex_chans); -} - - -/* -------------------------------------------------------------------------- */ - -/* readActions -Reads all recorded actions. */ - -void readActions(unsigned frame) -{ - pthread_mutex_lock(&mutex_recs); - for (unsigned i=0; ichan); - ch->parseAction(action, frame, clock::getCurrentFrame(), - clock::getQuantize(), clock::isRunning()); - } - break; - } - pthread_mutex_unlock(&mutex_recs); + channel->prepareBuffer(clock::isRunning()); } @@ -212,27 +178,6 @@ void doQuantize(unsigned frame) rewindWait = false; rewind(); } - - pthread_mutex_lock(&mutex_chans); - for (unsigned i=0; iquantize(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(ch)->sum(frame, clock::isRunning()); - pthread_mutex_unlock(&mutex_chans); } @@ -272,19 +217,12 @@ content to the output buffer). Process plugins too, if any. */ void renderIO(AudioBuffer& outBuf, const AudioBuffer& inBuf) { - pthread_mutex_lock(&mutex_chans); - for (Channel* ch : channels) { - if (isChannelAudible(ch)) - ch->process(outBuf, inBuf); - ch->preview(outBuf); - } - pthread_mutex_unlock(&mutex_chans); + for (Channel* channel : channels) + channel->process(outBuf, inBuf, isChannelAudible(channel), clock::isRunning()); #ifdef WITH_VST - pthread_mutex_lock(&mutex_plugins); pluginHost::processStack(outBuf, pluginHost::MASTER_OUT); pluginHost::processStack(vChanInToOut, pluginHost::MASTER_IN); - pthread_mutex_unlock(&mutex_plugins); #endif } @@ -330,30 +268,8 @@ last frame). */ void testBar(unsigned frame) { - if (!clock::isOnBar()) - return; - - if (metronome) + if (clock::isOnBar() && metronome) tickPlay = true; - - pthread_mutex_lock(&mutex_chans); - for (Channel* ch : channels) - ch->onBar(frame); - pthread_mutex_unlock(&mutex_chans); -} - - -/* -------------------------------------------------------------------------- */ - - -void testFirstBeat(unsigned frame) -{ - if (!clock::isOnFirstBeat()) - return; - pthread_mutex_lock(&mutex_chans); - for (Channel* ch : channels) - ch->onZero(frame, conf::recsStopOnChanHalt); - pthread_mutex_unlock(&mutex_chans); } @@ -362,11 +278,9 @@ void testFirstBeat(unsigned frame) void testLastBeat() { - if (clock::isOnBeat()) - if (metronome && !tickPlay) - tockPlay = true; + if (clock::isOnBeat() && metronome && !tickPlay) + tockPlay = true; } - }; // {anonymous} @@ -389,9 +303,7 @@ bool rewindWait = false; bool hasSolos = false; bool inToOut = false; -pthread_mutex_t mutex_recs; -pthread_mutex_t mutex_chans; -pthread_mutex_t mutex_plugins; +pthread_mutex_t mutex; /* -------------------------------------------------------------------------- */ @@ -401,23 +313,16 @@ void init(int framesInSeq, int framesInBuffer) { /* Allocate virtual input channels. vChanInput has variable size: it depends on how many frames there are in sequencer. */ - if (!allocVirtualInput(framesInSeq)) { - gu_log("[Mixer::init] vChanInput alloc error!\n"); - return; - } - if (!vChanInToOut.alloc(framesInBuffer, G_MAX_IO_CHANS)) { - gu_log("[Mixer::init] vChanInToOut alloc error!\n"); - return; - } + + vChanInput.alloc(framesInSeq, G_MAX_IO_CHANS); + vChanInToOut.alloc(framesInBuffer, G_MAX_IO_CHANS); gu_log("[Mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n", framesInSeq, framesInBuffer); hasSolos = false; - pthread_mutex_init(&mutex_recs, nullptr); - pthread_mutex_init(&mutex_chans, nullptr); - pthread_mutex_init(&mutex_plugins, nullptr); + pthread_mutex_init(&mutex, nullptr); rewind(); } @@ -426,9 +331,9 @@ void init(int framesInSeq, int framesInBuffer) /* -------------------------------------------------------------------------- */ -bool allocVirtualInput(int frames) +void allocVirtualInput(int frames) { - return vChanInput.alloc(frames, G_MAX_IO_CHANS); + vChanInput.alloc(frames, G_MAX_IO_CHANS); } @@ -441,6 +346,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, if (!ready) return 0; + pthread_mutex_lock(&mutex); + #ifdef __linux__ clock::recvJackSync(); #endif @@ -453,23 +360,33 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, peakOut = 0.0f; // reset peak calculator peakIn = 0.0f; // reset peak calculator - clearAllBuffers(out); + prepareBuffers(out); for (unsigned j=0; jparseEvents(fe); + + lineInRec(in, j); // TODO - can go outside this loop doQuantize(j); testBar(j); - testFirstBeat(j); - readActions(j); + testLastBeat(); clock::incrCurrentFrame(); - testLastBeat(); // this test must be the last one clock::sendMIDIsync(); } - sumChannels(j); } - + renderIO(out, in); /* Post processing. */ @@ -486,6 +403,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, out.setData(nullptr, 0, 0); in.setData(nullptr, 0, 0); + pthread_mutex_unlock(&mutex); + return 0; } @@ -498,6 +417,7 @@ void close() clock::stop(); while (channels.size() > 0) mh::deleteChannel(channels.at(0)); + pthread_mutex_destroy(&mutex); } @@ -507,12 +427,22 @@ void close() bool isSilent() { for (const Channel* ch : channels) - if (ch->status == STATUS_PLAY) + if (ch->isPlaying()) return false; return true; } + +/* -------------------------------------------------------------------------- */ + + +bool isChannelAudible(Channel* ch) +{ + return !hasSolos || (hasSolos && ch->solo); +} + + /* -------------------------------------------------------------------------- */ @@ -521,7 +451,7 @@ void rewind() clock::rewind(); if (clock::isRunning()) for (Channel* ch : channels) - ch->rewind(); + ch->rewindBySeq(); } @@ -541,7 +471,8 @@ void startInputRec() void mergeVirtualInput() { for (Channel* ch : channels) { - if (ch->type == G_CHANNEL_MIDI) + /* TODO - move this to audioProc::*/ + if (ch->type == ChannelType::MIDI) continue; SampleChannel* sch = static_cast(ch); if (sch->armed) @@ -549,6 +480,4 @@ void mergeVirtualInput() } vChanInput.clear(); } - - }}}; // giada::m::mixer:: diff --git a/src/core/mixer.h b/src/core/mixer.h index 36c566b..4f1f341 100644 --- a/src/core/mixer.h +++ b/src/core/mixer.h @@ -31,6 +31,7 @@ #include #include +#include "recorder.h" #include "../deps/rtaudio-mod/RtAudio.h" @@ -41,13 +42,56 @@ namespace giada { namespace m { namespace mixer { +struct FrameEvents +{ + int frameLocal; + int frameGlobal; + bool doQuantize; + bool onBar; + bool onFirstBeat; + bool quantoPassed; + std::vector 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 channels; + +extern bool recording; // is recording something? +extern bool ready; +extern float outVol; +extern float inVol; +extern float peakOut; +extern float peakIn; +extern bool metronome; +extern int waitRec; // delayComp guard +extern bool rewindWait; // rewind guard, if quantized +extern bool hasSolos; // more than 0 channels soloed + +/* inToOut +Copy, process and paste the input into the output, in order to obtain a "hear +what you're playing" feature. */ + +extern bool inToOut; + +extern pthread_mutex_t mutex; + void init(int framesInSeq, int framesInBuffer); /* allocVirtualInput Allocates new memory for the virtual input channel. Call this whenever you shrink or resize the sequencer. */ -bool allocVirtualInput(int frames); +void allocVirtualInput(int frames); void close(); @@ -55,13 +99,15 @@ void close(); Core method (callback) */ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime, - RtAudioStreamStatus status, void* userData); + RtAudioStreamStatus status, void* userData); /* isSilent Is mixer silent? */ bool isSilent(); +bool isChannelAudible(Channel* ch); + /* rewind Rewinds sequencer to frame 0. */ @@ -77,41 +123,6 @@ Copies the virtual channel input in the channels designed for input recording. Called by mixerHandler on stopInputRec(). */ void mergeVirtualInput(); - -enum { // const - what to do when a fadeout ends - DO_STOP = 0x01, - DO_MUTE = 0x02, - DO_MUTE_I = 0x04 -}; - -enum { // const - fade types - FADEOUT = 0x01, - XFADE = 0x02 -}; - -extern std::vector 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::; diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index ecbf42f..66ecc81 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -72,8 +72,8 @@ int readPatchPlugins(vector* list, int type) for (unsigned i=0; isize(); 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; jparams.size(); j++) @@ -120,7 +120,7 @@ int getNewChanIndex() bool uniqueSamplePath(const SampleChannel* skip, const string& path) { for (const Channel* ch : mixer::channels) { - if (skip == ch || ch->type != G_CHANNEL_SAMPLE) // skip itself and MIDI channels + if (skip == ch || ch->type != ChannelType::SAMPLE) // skip itself and MIDI channels continue; const SampleChannel* sch = static_cast(ch); if (sch->wave != nullptr && path == sch->wave->getPath()) @@ -133,7 +133,7 @@ bool uniqueSamplePath(const SampleChannel* skip, const string& path) /* -------------------------------------------------------------------------- */ -Channel* addChannel(int type) +Channel* addChannel(ChannelType type) { Channel* ch = nullptr; channelManager::create(type, kernelAudio::getRealBufSize(), @@ -142,10 +142,10 @@ Channel* addChannel(int type) return nullptr; while (true) { - if (pthread_mutex_trylock(&mixer::mutex_chans) != 0) + if (pthread_mutex_trylock(&mixer::mutex) != 0) continue; mixer::channels.push_back(ch); - pthread_mutex_unlock(&mixer::mutex_chans); + pthread_mutex_unlock(&mixer::mutex); break; } @@ -162,12 +162,12 @@ Channel* addChannel(int type) void deleteChannel(Channel* target) { while (true) { - if (pthread_mutex_trylock(&mixer::mutex_chans) != 0) + if (pthread_mutex_trylock(&mixer::mutex) != 0) continue; auto it = std::find(mixer::channels.begin(), mixer::channels.end(), target); if (it != mixer::channels.end()) mixer::channels.erase(it); - pthread_mutex_unlock(&mixer::mutex_chans); + pthread_mutex_unlock(&mixer::mutex); return; } } @@ -191,13 +191,9 @@ Channel* getChannelByIndex(int index) bool hasLogicalSamples() { - for (const Channel* ch : mixer::channels) { - if (ch->type != G_CHANNEL_SAMPLE) - continue; - const SampleChannel* sch = static_cast(ch); - if (sch->wave != nullptr && sch->wave->isLogical()) + for (const Channel* ch : mixer::channels) + if (ch->hasLogicalData()) return true; - } return false; } @@ -207,13 +203,9 @@ bool hasLogicalSamples() bool hasEditedSamples() { - for (const Channel* ch : mixer::channels) { - if (ch->type != G_CHANNEL_SAMPLE) - continue; - const SampleChannel* sch = static_cast(ch); - if (sch->wave != nullptr && sch->wave->isEdited()) + for (const Channel* ch : mixer::channels) + if (ch->hasEditedData()) return true; - } return false; } @@ -234,7 +226,7 @@ void stopSequencer() void updateSoloCount() { - for (Channel* ch : mixer::channels) + for (const Channel* ch : mixer::channels) if (ch->solo) { mixer::hasSolos = true; return; @@ -307,10 +299,8 @@ bool startInputRec() Wave* wave = nullptr; string name = string("TAKE-" + gu_iToString(patch::lastTakeId++)); // Increase lastTakeId - int result = waveManager::createEmpty(clock::getFramesInLoop(), G_MAX_IO_CHANS, + waveManager::createEmpty(clock::getFramesInLoop(), G_MAX_IO_CHANS, conf::samplerate, name + ".wav", &wave); - if (result != G_RES_OK) - continue; sch->pushWave(wave); sch->name = name; @@ -338,6 +328,10 @@ void stopInputRec() mixer::mergeVirtualInput(); mixer::recording = false; mixer::waitRec = 0; // in case delay compensation is in use + + for (Channel* ch : mixer::channels) + ch->stopInputRec(clock::getCurrentFrame()); + gu_log("[mh] stop input recs\n"); } @@ -348,7 +342,7 @@ void stopInputRec() bool hasArmedSampleChannels() { for (const Channel* ch : mixer::channels) - if (ch->type == G_CHANNEL_SAMPLE && ch->armed) + if (ch->type == ChannelType::SAMPLE && ch->armed) return true; return false; } diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index ae160a9..b37a14c 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -30,6 +30,7 @@ #include +#include "types.h" class Channel; @@ -43,7 +44,7 @@ namespace mh /* addChannel Adds a new channel of type 'type' into mixer's stack. */ -Channel* addChannel(int type); +Channel* addChannel(ChannelType type); /* deleteChannel Completely removes a channel from the stack. */ diff --git a/src/core/patch.cpp b/src/core/patch.cpp index a834775..6691b6d 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -27,7 +27,9 @@ #include "../utils/log.h" #include "../utils/string.h" +#include "../utils/ver.h" #include "const.h" +#include "types.h" #include "storager.h" #include "conf.h" #include "mixer.h" @@ -82,11 +84,23 @@ Makes sure an older patch is compatible with the current version. */ void modernize() { /* Starting from 0.15.0 actions are recorded on frames, not samples. */ - if (versionMajor <= 0 && versionMinor < 15) { + if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 0)) { for (channel_t& ch : channels) for (action_t& a : ch.actions) a.frame /= 2; } + + /* Starting from 0.15.1 Channel Modes have different values. */ + if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 1)) { + for (channel_t& ch : channels) { + if (ch.mode == 0x04) ch.mode = static_cast(ChannelMode::SINGLE_BASIC); + else if (ch.mode == 0x08) ch.mode = static_cast(ChannelMode::SINGLE_PRESS); + else if (ch.mode == 0x10) ch.mode = static_cast(ChannelMode::SINGLE_RETRIG); + else if (ch.mode == 0x20) ch.mode = static_cast(ChannelMode::LOOP_REPEAT); + else if (ch.mode == 0x40) ch.mode = static_cast(ChannelMode::SINGLE_ENDLESS); + else if (ch.mode == 0x80) ch.mode = static_cast(ChannelMode::LOOP_ONCE_BAR); + } + } } @@ -227,7 +241,6 @@ bool readChannels(json_t* jContainer) if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_NAME, channel.name)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0; - if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SOLO, channel.solo)) return 0; if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME, channel.volume)) return 0; if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN, channel.pan)) return 0; @@ -406,7 +419,6 @@ void writeChannels(json_t* jContainer, vector* channels) json_object_set_new(jChannel, PATCH_KEY_CHANNEL_NAME, json_string(channel.name.c_str())); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN, json_integer(channel.column)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE, json_integer(channel.mute)); - json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO, json_integer(channel.solo)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME, json_real(channel.volume)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN, json_real(channel.pan)); diff --git a/src/core/patch.h b/src/core/patch.h index 651c2af..77d48d7 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -64,7 +64,6 @@ struct channel_t std::string name; int column; int mute; - int mute_s; int solo; float volume; float pan; diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index d6b32fa..afcb4c9 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -45,7 +45,7 @@ int Plugin::idGenerator = 1; /* -------------------------------------------------------------------------- */ -Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate, +Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, int buffersize) : ui (nullptr), plugin(plugin), diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp index 153b149..6ba0465 100644 --- a/src/core/pluginHost.cpp +++ b/src/core/pluginHost.cpp @@ -141,6 +141,7 @@ pthread_mutex_t mutex_midi; void close() { messageManager->deleteInstance(); + pthread_mutex_destroy(&mutex_midi); } @@ -413,7 +414,7 @@ void processStack(AudioBuffer& outBuf, int stackType, Channel* ch) Sample channels and Master in/out want audio data instead: let's convert the internal buffer from Giada to Juce. */ - if (ch != nullptr && ch->type == G_CHANNEL_MIDI) + if (ch != nullptr && ch->type == ChannelType::MIDI) audioBuffer.clear(); else for (int i=0; itype == G_CHANNEL_SAMPLE && static_cast(ch)->wave == nullptr) - ) - return false; - return true; + /* Can record on a channel if: + - recorder is on + - mixer is running + - mixer is not recording a take somewhere + - channel is SAMPLE type and has data in it */ + return active && clockRunning && !mixerRecording && ch->type == ChannelType::SAMPLE && ch->hasData(); } @@ -125,7 +118,7 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue) { /* allocating the action */ - action* a = (action*) malloc(sizeof(action)); + action* a = (action*) malloc(sizeof(action)); /* TODO - AAARRRGGHHHHHH!!!! */ a->chan = index; a->type = type; a->frame = frame; @@ -661,6 +654,20 @@ void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex) /* -------------------------------------------------------------------------- */ +vector getActionsOnFrame(int frame) +{ + for (size_t i=0; i(); +} + + +/* -------------------------------------------------------------------------- */ + + void forEachAction(std::function f) { for (const vector actions : recorder::global) diff --git a/src/core/recorder.h b/src/core/recorder.h index 0f0b0e5..cdbcc2c 100644 --- a/src/core/recorder.h +++ b/src/core/recorder.h @@ -79,6 +79,8 @@ Contains the actual actions. E.g.: global[1] = */ extern std::vector> global; +/* TODO - this frames vs global madness must be replaced with a map: +std::map> */ extern bool active; extern bool sortedActions; // are actions sorted via sortActions()? @@ -174,12 +176,17 @@ Returns a pointer to action in chan 'chan' of type 'action' at frame 'frame'. */ int getAction(int chan, char action, int frame, struct action** out); +/* getActionsOnFrame +Returns a vector of actions that occur on frame 'frame'. */ + +std::vector 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. */ diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 0a69632..0ce44f6 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -25,56 +25,43 @@ * -------------------------------------------------------------------------- */ -#include -#include -#include #include "../utils/log.h" -#include "../utils/fs.h" -#include "../utils/string.h" -#include "patch.h" +#include "sampleChannelProc.h" +#include "sampleChannelRec.h" #include "channelManager.h" #include "const.h" -#include "conf.h" -#include "clock.h" -#include "mixer.h" #include "wave.h" -#include "pluginHost.h" -#include "waveFx.h" -#include "waveManager.h" -#include "mixerHandler.h" -#include "kernelMidi.h" -#include "kernelAudio.h" #include "sampleChannel.h" using std::string; +using namespace giada; using namespace giada::m; -SampleChannel::SampleChannel(int bufferSize, bool inputMonitor) - : Channel (G_CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize), - rsmp_state (nullptr), - frameRewind (-1), - begin (0), - end (0), - pitch (G_DEFAULT_PITCH), - boost (G_DEFAULT_BOOST), - fadeinOn (false), - fadeinVol (1.0f), - fadeoutOn (false), - fadeoutVol (1.0f), - fadeoutTracker (0), - fadeoutStep (G_DEFAULT_FADEOUT_STEP), +SampleChannel::SampleChannel(bool inputMonitor, int bufferSize) + : Channel (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize), + mode (ChannelMode::SINGLE_BASIC), wave (nullptr), tracker (0), trackerPreview (0), shift (0), - mode (G_DEFAULT_CHANMODE), qWait (false), inputMonitor (inputMonitor), + boost (G_DEFAULT_BOOST), + pitch (G_DEFAULT_PITCH), + begin (0), + end (0), midiInReadActions(0x0), - midiInPitch (0x0) + midiInPitch (0x0), + rsmp_state (nullptr) { + rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr); + if (rsmp_state == nullptr) { + gu_log("[SampleChannel] unable to alloc memory for SRC_STATE!\n"); + throw std::bad_alloc(); + } + bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS); } @@ -83,8 +70,7 @@ SampleChannel::SampleChannel(int bufferSize, bool inputMonitor) SampleChannel::~SampleChannel() { - if (wave != nullptr) - delete wave; + delete wave; if (rsmp_state != nullptr) src_delete(rsmp_state); } @@ -93,34 +79,6 @@ SampleChannel::~SampleChannel() /* -------------------------------------------------------------------------- */ -bool SampleChannel::allocBuffers() -{ - if (!Channel::allocBuffers()) - return false; - - rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr); - if (rsmp_state == nullptr) { - gu_log("[SampleChannel::allocBuffers] unable to alloc memory for SRC_STATE!\n"); - return false; - } - - if (!pChan.alloc(bufferSize, G_MAX_IO_CHANS)) { - gu_log("[SampleChannel::allocBuffers] unable to alloc memory for pChan!\n"); - return false; - } - - if (!vChanPreview.alloc(bufferSize, G_MAX_IO_CHANS)) { - gu_log("[SampleChannel::allocBuffers] unable to alloc memory for vChanPreview!\n"); - return false; - } - - return true; -} - - -/* -------------------------------------------------------------------------- */ - - void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) { Channel::copy(src_, pluginMutex); @@ -131,14 +89,6 @@ void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) boost = src->boost; mode = src->mode; qWait = src->qWait; - fadeinOn = src->fadeinOn; - fadeinVol = src->fadeinVol; - fadeoutOn = src->fadeoutOn; - fadeoutVol = src->fadeoutVol; - fadeoutTracker = src->fadeoutTracker; - fadeoutStep = src->fadeoutStep; - fadeoutType = src->fadeoutType; - fadeoutEnd = src->fadeoutEnd; setPitch(src->pitch); if (src->wave) @@ -149,596 +99,291 @@ void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) /* -------------------------------------------------------------------------- */ -void SampleChannel::clear() +void SampleChannel::parseEvents(m::mixer::FrameEvents fe) { - /** TODO - these clear() may be done only if status PLAY | ENDING (if below), - * but it would require extra clearPChan calls when samples stop */ - - vChan.clear(); - pChan.clear(); - - if (status & (STATUS_PLAY | STATUS_ENDING)) { - tracker = fillChan(vChan, tracker, 0); - if (fadeoutOn && fadeoutType == XFADE) { - gu_log("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker); - fadeoutTracker = fillChan(pChan, fadeoutTracker, 0); - } - } + sampleChannelProc::parseEvents(this, fe); + sampleChannelRec::parseEvents(this, fe); } /* -------------------------------------------------------------------------- */ -void SampleChannel::calcVolumeEnv(int frame) +void SampleChannel::prepareBuffer(bool running) { - /* method: check this frame && next frame, then calculate delta */ - - recorder::action* a0 = nullptr; - recorder::action* a1 = nullptr; - int res; - - /* get this action on frame 'frame'. It's unlikely that the action - * is not found. */ + sampleChannelProc::prepareBuffer(this, running); +} - res = recorder::getAction(index, G_ACTION_VOLUME, frame, &a0); - if (res == 0) - return; - /* get the action next to this one. - * res == -1: a1 not found, this is the last one. Rewind the search - * and use action at frame number 0 (actions[0]). - * res == -2 G_ACTION_VOLUME not found. This should never happen */ - res = recorder::getNextAction(index, G_ACTION_VOLUME, frame, &a1); +/* -------------------------------------------------------------------------- */ - if (res == -1) - res = recorder::getAction(index, G_ACTION_VOLUME, 0, &a1); - volume_i = a0->fValue; - volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f; +void SampleChannel::rewindBySeq() +{ + sampleChannelProc::rewindBySeq(this); } /* -------------------------------------------------------------------------- */ -void SampleChannel::hardStop(int frame) +void SampleChannel::start(int localFrame, bool doQuantize, int velocity) { - if (frame != 0) - vChan.clear(frame); // clear data in range [frame, [end]] - status = STATUS_OFF; - sendMidiLplay(); - reset(frame); + sampleChannelProc::start(this, localFrame, doQuantize, velocity); } /* -------------------------------------------------------------------------- */ -void SampleChannel::onBar(int frame) +void SampleChannel::stop() { - ///if (mode == LOOP_REPEAT && status == STATUS_PLAY) - /// //setXFade(frame * 2); - /// reset(frame * 2); - - if (mode == LOOP_REPEAT) { - if (status == STATUS_PLAY) - //setXFade(frame * 2); - reset(frame); - } - else - if (mode == LOOP_ONCE_BAR) { - if (status == STATUS_WAIT) { - status = STATUS_PLAY; - tracker = fillChan(vChan, tracker, frame); - sendMidiLplay(); - } - } + sampleChannelProc::stop(this); } /* -------------------------------------------------------------------------- */ -void SampleChannel::setBegin(int f) +void SampleChannel::stopBySeq(bool chansStopOnSeqHalt) { - if (f < 0) - begin = 0; - else - if (f > wave->getSize()) - begin = wave->getSize(); - else - if (f >= end) - begin = end - 1; - else - begin = f; - - tracker = begin; - trackerPreview = begin; + sampleChannelProc::stopBySeq(this, chansStopOnSeqHalt); } /* -------------------------------------------------------------------------- */ -void SampleChannel::setEnd(int f) +void SampleChannel::kill(int localFrame) { - if (f < 0) - end = begin + wave->getChannels(); - else - if (f >= wave->getSize()) - end = wave->getSize() - 1; - else - if (f <= begin) - end = begin + 1; - else - end = f; + sampleChannelProc::kill(this, localFrame); } /* -------------------------------------------------------------------------- */ -int SampleChannel::getBegin() const { return begin; } -int SampleChannel::getEnd() const { return end; } - - -/* -------------------------------------------------------------------------- */ +bool SampleChannel::recordStart(bool canQuantize) +{ + return sampleChannelRec::recordStart(this, canQuantize); +} -void SampleChannel::setPitch(float v) +bool SampleChannel::recordKill() { - if (v > G_MAX_PITCH) - pitch = G_MAX_PITCH; - else - if (v < 0.1f) - pitch = 0.1000f; - else - pitch = v; - - rsmp_data.src_ratio = 1/pitch; + return sampleChannelRec::recordKill(this); +} - /* if status is off don't slide between frequencies */ - if (status & (STATUS_OFF | STATUS_WAIT)) - src_set_ratio(rsmp_state, 1/pitch); +void SampleChannel::recordStop() +{ + sampleChannelRec::recordStop(this); } -float SampleChannel::getPitch() const { return pitch; } +void SampleChannel::recordMute() +{ + sampleChannelRec::recordMute(this); +} /* -------------------------------------------------------------------------- */ -void SampleChannel::rewind() +void SampleChannel::startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) { - /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */ - - if (wave != nullptr) { - if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY))) - reset(0); // rewind is user-generated events, always on frame 0 - } + sampleChannelRec::startReadingActions(this, treatRecsAsLoops, recsStopOnChanHalt); } /* -------------------------------------------------------------------------- */ -void SampleChannel::parseAction(recorder::action* a, int localFrame, - int globalFrame, int quantize, bool mixerIsRunning) +void SampleChannel::stopReadingActions(bool running, bool treatRecsAsLoops, + bool recsStopOnChanHalt) { - if (readActions == false) - return; - - switch (a->type) { - case G_ACTION_KEYPRESS: - if (mode & SINGLE_ANY) - start(localFrame, false, quantize, mixerIsRunning, false, false); - break; - case G_ACTION_KEYREL: - if (mode & SINGLE_ANY) - stop(); - break; - case G_ACTION_KILL: - if (mode & SINGLE_ANY) - kill(localFrame); - break; - case G_ACTION_MUTEON: - setMute(true); // internal mute - break; - case G_ACTION_MUTEOFF: - unsetMute(true); // internal mute - break; - case G_ACTION_VOLUME: - calcVolumeEnv(globalFrame); - break; - } + sampleChannelRec::stopReadingActions(this, running, treatRecsAsLoops, + recsStopOnChanHalt); } /* -------------------------------------------------------------------------- */ -void SampleChannel::sum(int frame, bool running) +void SampleChannel::stopInputRec(int globalFrame) { - if (wave == nullptr || status & ~(STATUS_PLAY | STATUS_ENDING)) - return; - - if (frame != frameRewind) { - - /* volume envelope, only if seq is running */ - - if (running) { - volume_i += volume_d; - if (volume_i < 0.0f) - volume_i = 0.0f; - else - if (volume_i > 1.0f) - volume_i = 1.0f; - } - - /* fadein or fadeout processes. If mute, delete any signal. */ - - /** TODO - big issue: fade[in/out]Vol * internal_volume might be a - * bad choice: it causes glitches when muting on and off during a - * volume envelope. */ - - if (mute || mute_i) { - for (int i=0; i 0.0f) { // fadeout ongoing - if (fadeoutType == XFADE) { - for (int i=0; i G_MAX_BOOST_DB) - boost = G_MAX_BOOST_DB; - else - if (v < 0.0f) - boost = 0.0f; - else - boost = v; -} +bool SampleChannel::hasLogicalData() const +{ + return wave != nullptr && wave->isLogical(); +}; -float SampleChannel::getBoost() const +bool SampleChannel::hasEditedData() const +{ + return wave != nullptr && wave->isEdited(); +}; + + +bool SampleChannel::hasData() const { - return boost; + return wave != nullptr; } /* -------------------------------------------------------------------------- */ -void SampleChannel::calcFadeoutStep() +void SampleChannel::setBegin(int f) { - if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP)) - fadeoutStep = ceil((end - tracker) / volume); /// or volume_i ??? + if (f < 0) + begin = 0; + else + if (f > wave->getSize()) + begin = wave->getSize(); else - fadeoutStep = G_DEFAULT_FADEOUT_STEP; + if (f >= end) + begin = end - 1; + else + begin = f; + + tracker = begin; + trackerPreview = begin; } /* -------------------------------------------------------------------------- */ -void SampleChannel::setReadActions(bool v, bool killOnFalse) +void SampleChannel::setEnd(int f) { - readActions = v; - if (!readActions && killOnFalse) - kill(0); /// FIXME - wrong frame value + if (f >= wave->getSize()) + end = wave->getSize() - 1; + else + if (f <= begin) + end = begin + 1; + else + end = f; } /* -------------------------------------------------------------------------- */ -void SampleChannel::setFadeIn(bool internal) -{ - if (internal) mute_i = false; // remove mute before fading in - else mute = false; - fadeinOn = true; - fadeinVol = 0.0f; -} +int SampleChannel::getBegin() const { return begin; } +int SampleChannel::getEnd() const { return end; } /* -------------------------------------------------------------------------- */ -void SampleChannel::setFadeOut(int actionPostFadeout) +void SampleChannel::setPitch(float v) { - calcFadeoutStep(); - fadeoutOn = true; - fadeoutVol = 1.0f; - fadeoutType = FADEOUT; - fadeoutEnd = actionPostFadeout; + if (v > G_MAX_PITCH) + pitch = G_MAX_PITCH; + else + if (v < G_MIN_PITCH) + pitch = G_MIN_PITCH; + else + pitch = v; + +// ???? /* if status is off don't slide between frequencies */ +// ???? +// ???? if (status & (STATUS_OFF | STATUS_WAIT)) +// ???? src_set_ratio(rsmp_state, 1/pitch); } +float SampleChannel::getPitch() const { return pitch; } + + /* -------------------------------------------------------------------------- */ -void SampleChannel::setXFade(int frame) +int SampleChannel::getPosition() const { - gu_log("[xFade] frame=%d tracker=%d\n", frame, tracker); - - calcFadeoutStep(); - fadeoutOn = true; - fadeoutVol = 1.0f; - fadeoutType = XFADE; - fadeoutTracker = fillChan(pChan, tracker, 0, false); - reset(frame); + if (status != ChannelStatus::EMPTY && + status != ChannelStatus::MISSING && + status != ChannelStatus::OFF) + return tracker - begin; + else + return -1; } /* -------------------------------------------------------------------------- */ -void SampleChannel::reset(int frame) +void SampleChannel::setBoost(float v) { - //fadeoutTracker = tracker; // store old frame number for xfade - tracker = begin; - mute_i = false; - qWait = false; // Was in qWait mode? Reset occured, no more qWait now. - - /* On reset, if frame > 0 and in play, fill again pChan to create something - like this: + if (v > G_MAX_BOOST_DB) + boost = G_MAX_BOOST_DB; + else + if (v < 0.0f) + boost = 0.0f; + else + boost = v; +} - |abcdefabcdefab*abcdefabcde| - [old data-----]*[new data--] */ - if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING)) - tracker = fillChan(vChan, tracker, frame); +float SampleChannel::getBoost() const +{ + return boost; } @@ -747,18 +392,16 @@ void SampleChannel::reset(int frame) void SampleChannel::empty() { - status = STATUS_OFF; - if (wave != nullptr) { - delete wave; - wave = nullptr; - } - begin = 0; - end = 0; - tracker = 0; - status = STATUS_EMPTY; - volume = G_DEFAULT_VOL; - boost = G_DEFAULT_BOOST; - sendMidiLplay(); + status = ChannelStatus::EMPTY; + begin = 0; + end = 0; + tracker = 0; + volume = G_DEFAULT_VOL; + boost = G_DEFAULT_BOOST; + hasActions = false; + delete wave; + wave = nullptr; + sendMidiLstatus(); } @@ -767,290 +410,86 @@ void SampleChannel::empty() void SampleChannel::pushWave(Wave* w) { - sendMidiLplay(); // FIXME - why here?!?! + status = ChannelStatus::OFF; wave = w; - status = STATUS_OFF; begin = 0; end = wave->getSize() - 1; name = wave->getBasename(); + sendMidiLstatus(); } /* -------------------------------------------------------------------------- */ -void SampleChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) -{ - assert(out.countSamples() == vChan.countSamples()); - assert(in.countSamples() == vChan.countSamples()); - - /* If armed and inbuffer is not nullptr (i.e. input device available) and - input monitor is on, copy input buffer to vChan: this enables the input - monitoring. The vChan will be overwritten later by pluginHost::processStack, - so that you would record "clean" audio (i.e. not plugin-processed). */ - - if (armed && in.isAllocd() && inputMonitor) - for (int i=0; i= 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; igetFrame(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 diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index 11af92e..87aca99 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -31,6 +31,7 @@ #include #include +#include "types.h" #include "channel.h" @@ -40,172 +41,114 @@ class Wave; class SampleChannel : public Channel { -private: - - /* fillChan - Fills 'dest' buffer at point 'offset' with wave data taken from 'start'. If - rewind=false don't rewind internal tracker. Returns new sample position, - in frames. It resamples data if pitch != 1.0f. */ - - int fillChan(giada::m::AudioBuffer& dest, int start, int offset, bool rewind=true); - - /* calcFadeoutStep - How many frames are left before the end of the sample? Is there enough room - for a complete fadeout? Should we shorten it? */ - - void calcFadeoutStep(); - - /* calcVolumeEnv - Computes any changes in volume done via envelope tool. */ - - void calcVolumeEnv(int frame); - - /* reset - Rewinds tracker to the beginning of the sample. */ - - void reset(int frame); - - /* fade methods - Prepare channel for fade, mixer will take care of the process during master - play. */ - - void setFadeIn(bool internal); - void setFadeOut(int actionPostFadeout); - void setXFade(int frame); - - /* rsmp_state, rsmp_data - Structs from libsamplerate. */ - - SRC_STATE* rsmp_state; - SRC_DATA rsmp_data; - - /* pChan, vChanPreview - Extra virtual channel for processing resampled data and for audio preview. */ - - giada::m::AudioBuffer pChan; - giada::m::AudioBuffer vChanPreview; - - /* frameRewind - Exact frame in which a rewind occurs. */ - - int frameRewind; - - /* begin, end - Begin/end point to read wave data from/to. */ - - int begin; - int end; - - float pitch; - float boost; - - bool fadeinOn; - float fadeinVol; - bool fadeoutOn; - float fadeoutVol; // fadeout volume - int fadeoutTracker; // tracker fadeout, xfade only - float fadeoutStep; // fadeout decrease - int fadeoutType; // xfade or fadeout - int fadeoutEnd; // what to do when fadeout ends - public: - SampleChannel(int bufferSize, bool inputMonitor); + SampleChannel(bool inputMonitor, int bufferSize); ~SampleChannel(); void copy(const Channel* src, pthread_mutex_t* pluginMutex) override; - void clear() override; - void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override; - void preview(giada::m::AudioBuffer& out) override; - void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning, - bool forceStart, bool isUserGenerated) override; + void prepareBuffer(bool running) override; + void parseEvents(giada::m::mixer::FrameEvents fe) override; + void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, + bool audible, bool running) override; + void readPatch(const std::string& basePath, int i) override; + void writePatch(int i, bool isProject) override; + + void start(int frame, bool doQuantize, int velocity) override; + void stop() override; void kill(int frame) override; + bool recordStart(bool canQuantize) override; + bool recordKill() override; + void recordStop() override; + void recordMute() override; + void setMute(bool value, giada::EventType eventType) override; + void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override; + void stopReadingActions(bool running, bool treatRecsAsLoops, + bool recsStopOnChanHalt) override; void empty() override; void stopBySeq(bool chansStopOnSeqHalt) override; - void stop() override; - void rewind() override; - void setMute(bool internal) override; - void unsetMute(bool internal) override; - void readPatch(const std::string& basePath, int i) override; - void writePatch(int i, bool isProject) override; - void quantize(int index, int localFrame, int globalFrame) override; - void onZero(int frame, bool recsStopOnChanHalt) override; - void onBar(int frame) override; - void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame, - int quantize, bool mixerIsRunning) override; + void rewindBySeq() override; bool canInputRec() override; - bool allocBuffers() override; + void stopInputRec(int globalFrame) override; + bool hasLogicalData() const override; + bool hasEditedData() const override; + bool hasData() const override; float getBoost() const; int getBegin() const; int getEnd() const; float getPitch() const; - - /* pushWave - Adds a new wave to an existing channel. */ - - void pushWave(Wave* w); + bool isAnyLoopMode() const; + bool isAnySingleMode() const; + bool isOnLastFrame() const; /* getPosition Returns the position of an active sample. If EMPTY o MISSING returns -1. */ - int getPosition(); + int getPosition() const; - /* sum - Adds sample frames to virtual channel. Frame = processed frame in Mixer. - Running == is Mixer in play? */ + /* fillBuffer + Fills 'dest' buffer at point 'offset' with Wave data taken from 'start'. + Returns how many frames have been used from the original Wave data. It also + resamples data if pitch != 1.0f. */ - void sum(int frame, bool running); + int fillBuffer(giada::m::AudioBuffer& dest, int start, int offset); + + /* pushWave + Adds a new wave to an existing channel. */ + + void pushWave(Wave* w); void setPitch(float v); void setBegin(int f); void setEnd(int f); void setBoost(float v); - /* hardStop - Stops the channel immediately, no further checks. */ - - void hardStop(int frame); - - /* setReadActions - If enabled (v == true), recorder will read actions from this channel. If - killOnFalse == true and disabled, will also kill the channel. */ - - void setReadActions(bool v, bool killOnFalse); + void setReadActions(bool v, bool recsStopOnChanHalt); /* onPreviewEnd A callback fired when audio preview ends. */ std::function onPreviewEnd; + /* bufferPreview + Extra buffer for audio preview. */ + + giada::m::AudioBuffer bufferPreview; + + giada::ChannelMode mode; + Wave* wave; int tracker; // chan position int trackerPreview; // chan position for audio preview int shift; - int mode; // mode: see const.h bool qWait; // quantizer wait - bool inputMonitor; + bool inputMonitor; + float boost; + float pitch; - /* midi stuff */ + /* begin, end + Begin/end point to read wave data from/to. */ - bool midiInVeloAsVol; - uint32_t midiInReadActions; - uint32_t midiInPitch; + int begin; + int end; - /* const - what to do when a fadeout ends */ + /* midi stuff */ + + bool midiInVeloAsVol; + uint32_t midiInReadActions; + uint32_t midiInPitch; + +private: - enum { - DO_STOP = 0x01, - DO_MUTE = 0x02, - DO_MUTE_I = 0x04 - }; + /* rsmp_state, rsmp_data + Structs from libsamplerate. */ - /* const - fade types */ + SRC_STATE* rsmp_state; + SRC_DATA rsmp_data; - enum { - FADEOUT = 0x01, - XFADE = 0x02 - }; + int fillBufferResampled(giada::m::AudioBuffer& dest, int start, int offset); + int fillBufferCopy (giada::m::AudioBuffer& dest, int start, int offset); }; #endif diff --git a/src/core/sampleChannelProc.cpp b/src/core/sampleChannelProc.cpp new file mode 100644 index 0000000..546adf7 --- /dev/null +++ b/src/core/sampleChannelProc.cpp @@ -0,0 +1,468 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#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; ibuffer.countFrames(); i++) + for (int j=0; jbuffer.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; icalcVolumeEnvelope(); + if (!ch->mute && !ch->mute_i) + for (int j=0; jbuffer[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; ibufferPreview[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(velocity, 0.0f, 127.0f, 0.0f, 1.0f); + } + + switch (ch->status) { + case ChannelStatus::OFF: + if (ch->isAnyLoopMode()) { + ch->status = ChannelStatus::WAIT; + ch->sendMidiLstatus(); + } + else { + if (doQuantize) + ch->qWait = true; + else { + ch->status = ChannelStatus::PLAY; + ch->sendMidiLstatus(); + } + } + break; + + case ChannelStatus::PLAY: + if (ch->mode == ChannelMode::SINGLE_RETRIG) { + if (doQuantize) + ch->qWait = true; + else + rewind_(ch, localFrame); + } + else + if (ch->isAnyLoopMode() || ch->mode == ChannelMode::SINGLE_ENDLESS) { + ch->status = ChannelStatus::ENDING; + ch->sendMidiLstatus(); + } + else + if (ch->mode == ChannelMode::SINGLE_BASIC) { + rewind_(ch, localFrame); + ch->status = ChannelStatus::OFF; + ch->sendMidiLstatus(); + } + break; + + case ChannelStatus::WAIT: + ch->status = ChannelStatus::OFF; + ch->sendMidiLstatus(); + break; + + case ChannelStatus::ENDING: + ch->status = ChannelStatus::PLAY; + ch->sendMidiLstatus(); + break; + + default: break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void prepareBuffer(SampleChannel* ch, bool running) +{ + if (!ch->hasData()) + return; + ch->buffer.clear(); + if (ch->isPlaying()) { + int framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, 0); + ch->tracker += framesUsed; + if (ch->isOnLastFrame()) + onLastFrame_(ch, framesUsed * (1 / ch->pitch), running); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void parseEvents(SampleChannel* ch, mixer::FrameEvents fe) +{ + quantize_(ch, fe.frameLocal, fe.quantoPassed); + if (fe.onBar) + onBar_(ch, fe.frameLocal); + if (fe.onFirstBeat) + onFirstBeat_(ch, fe.frameLocal); +} + + +/* -------------------------------------------------------------------------- */ + + +void process(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in, + bool audible, bool running) +{ + if (audible) + processData_(ch, out, in, running); + + if (ch->isPreview()) + processPreview_(ch, out); +} +}}}; \ No newline at end of file diff --git a/src/core/sampleChannelProc.h b/src/core/sampleChannelProc.h new file mode 100644 index 0000000..341cbfd --- /dev/null +++ b/src/core/sampleChannelProc.h @@ -0,0 +1,91 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_SAMPLE_CHANNEL_PROC_H +#define G_SAMPLE_CHANNEL_PROC_H + + +#include "mixer.h" +#include "audioBuffer.h" +#include "types.h" + + +class SampleChannel; + + +namespace giada { +namespace m { +namespace sampleChannelProc +{ +/**/ +void prepareBuffer(SampleChannel* ch, bool running); + +/* parseEvents +Parses events gathered by Mixer::masterPlay(). */ + +void parseEvents(SampleChannel* ch, mixer::FrameEvents ev); + +/**/ +void process(SampleChannel* ch, giada::m::AudioBuffer& out, + const giada::m::AudioBuffer& in, bool audible, bool running); + +/* kill +Stops a channel abruptly. */ + +void kill(SampleChannel* ch, int localFrame); + +/* stop +Stops a channel normally (via key or MIDI). */ + +void stop(SampleChannel* ch); + +/* stopInputRec +Prepare a channel for playing when the input recording is done. */ + +void stopInputRec(SampleChannel* ch, int globalFrame); + +/* stopBySeq +Stops a channel when the stop button on main transport is pressed. */ + +void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt); + +/* rewind +Rewinds channel when rewind button on main transport is pressed. */ + +void rewindBySeq(SampleChannel* ch); + +/* start +Starts a channel. doQuantize = false (don't quantize) when Mixer is reading +actions from Recorder. */ + +void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity); + +void setMute(SampleChannel* ch, bool value, EventType eventType); +}}}; + + +#endif \ No newline at end of file diff --git a/src/core/sampleChannelRec.cpp b/src/core/sampleChannelRec.cpp new file mode 100644 index 0000000..ec28bb9 --- /dev/null +++ b/src/core/sampleChannelRec.cpp @@ -0,0 +1,327 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "const.h" +#include "conf.h" +#include "clock.h" +#include "kernelAudio.h" +#include "sampleChannel.h" +#include "sampleChannelRec.h" + + +namespace giada { +namespace m { +namespace sampleChannelRec +{ +namespace +{ +/* onFirstBeat +Things to do when the sequencer is on the first beat. */ + +void onFirstBeat_(SampleChannel* ch, bool recsStopOnChanHalt) +{ + if (ch->wave == nullptr) + return; + + switch (ch->recStatus) { + case ChannelStatus::ENDING: + ch->recStatus = ChannelStatus::OFF; + setReadActions(ch, false, recsStopOnChanHalt); // rec stop + break; + + case ChannelStatus::WAIT: + ch->recStatus = ChannelStatus::PLAY; + setReadActions(ch, true, recsStopOnChanHalt); // rec start + break; + + default: break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +bool recorderCanRec_(SampleChannel* ch) +{ + return recorder::canRec(ch, clock::isRunning(), mixer::recording); +} + + +/* -------------------------------------------------------------------------- */ + + +/* calcVolumeEnv +Computes any changes in volume done via envelope tool. */ + +void calcVolumeEnv_(SampleChannel* ch, int globalFrame) +{ + /* method: check this frame && next frame, then calculate delta */ + + recorder::action* a0 = nullptr; + recorder::action* a1 = nullptr; + int res; + + /* get this action on frame 'frame'. It's unlikely that the action + * is not found. */ + + res = recorder::getAction(ch->index, G_ACTION_VOLUME, globalFrame, &a0); + if (res == 0) + return; + + /* get the action next to this one. + * res == -1: a1 not found, this is the last one. Rewind the search + * and use action at frame number 0 (actions[0]). + * res == -2 G_ACTION_VOLUME not found. This should never happen */ + + res = recorder::getNextAction(ch->index, G_ACTION_VOLUME, globalFrame, &a1); + + if (res == -1) + res = recorder::getAction(ch->index, G_ACTION_VOLUME, 0, &a1); + + ch->volume_i = a0->fValue; + ch->volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f; +} + + +/* -------------------------------------------------------------------------- */ + + +void parseAction_(SampleChannel* ch, const recorder::action* a, int localFrame, + int globalFrame) +{ + if (!ch->readActions) + return; + + switch (a->type) { + case G_ACTION_KEYPRESS: + if (ch->isAnySingleMode()) { + ch->start(localFrame, false, 0); + /* This is not a user-generated event, so fill the first chunk of buffer. + Then, sampleChannelProc::prepareBuffer will take care of filling the + subsequent buffers from the next cycle on. */ + if (ch->status == ChannelStatus::PLAY) + ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame); + } + break; + case G_ACTION_KEYREL: + if (ch->isAnySingleMode()) + ch->stop(); + break; + case G_ACTION_KILL: + if (ch->isAnySingleMode()) + ch->kill(localFrame); + break; + case G_ACTION_MUTEON: + ch->setMute(true, EventType::AUTO); + break; + case G_ACTION_MUTEOFF: + ch->setMute(false, EventType::AUTO); + break; + case G_ACTION_VOLUME: + calcVolumeEnv_(ch, globalFrame); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void recordKeyPressAction_(SampleChannel* ch) +{ + if (!recorderCanRec_(ch)) + return; + + /* SINGLE_PRESS mode needs overdub. Also, disable reading actions while + overdubbing. */ + if (ch->mode == ChannelMode::SINGLE_PRESS) { + recorder::startOverdub(ch->index, G_ACTION_KEYS, clock::getCurrentFrame(), + kernelAudio::getRealBufSize()); + ch->readActions = false; + } + else + recorder::rec(ch->index, G_ACTION_KEYPRESS, clock::getCurrentFrame()); + ch->hasActions = true; +} + + +/* -------------------------------------------------------------------------- */ + + +void quantize_(SampleChannel* ch, bool quantoPassed) +{ + /* Skip if LOOP_ANY or not in quantizer-wait mode. Otherwise the quantize wait + has expired: record the keypress. */ + + if (ch->isAnyLoopMode() || !ch->qWait || !quantoPassed) + return; + recordKeyPressAction_(ch); +} +}; // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +void parseEvents(SampleChannel* ch, mixer::FrameEvents fe) +{ + quantize_(ch, fe.quantoPassed); + if (fe.onFirstBeat) + onFirstBeat_(ch, conf::recsStopOnChanHalt); + for (const recorder::action* action : fe.actions) + if (action->chan == ch->index) + parseAction_(ch, action, fe.frameLocal, fe.frameGlobal); +} + + +/* -------------------------------------------------------------------------- */ + + +void recordMute(SampleChannel* ch) +{ + if (recorderCanRec_(ch)) { + if (!ch->mute) { + recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(), + kernelAudio::getRealBufSize()); + ch->readActions = false; // don't read actions while overdubbing + } + else + recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(), + &mixer::mutex); + } +} + + +/* -------------------------------------------------------------------------- */ + + +bool recordStart(SampleChannel* ch, bool canQuantize) +{ + /* Record a 'start' event if the quantizer is off, otherwise let mixer to + handle it when a quantoWait has passed (see quantize_()). Also skip if + channel is in any loop mode, where KEYPRESS and KEYREL are meaningless. */ + + if (!canQuantize && !ch->isAnyLoopMode() && recorderCanRec_(ch)) + { + recordKeyPressAction_(ch); + + /* Why return here? You record an action and then you call ch->start: + Mixer, which is on another thread, reads your newly recorded action if you + have readActions == true, and then ch->start kicks in right after it. + The result: Mixer plays the channel (due to the new action) but the code + in the switch in start() kills it right away (because the sample is playing). + Fix: start channel only if you are not recording anything, i.e. let + Mixer play it. */ + + if (ch->readActions) + return false; + } + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +bool recordKill(SampleChannel* ch) +{ + /* Don't record G_ACTION_KILL actions for LOOP channels. */ + if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) { + recorder::rec(ch->index, G_ACTION_KILL, clock::getCurrentFrame()); + ch->hasActions = true; + } + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +void recordStop(SampleChannel* ch) +{ + /* Record a stop event only if channel is SINGLE_PRESS. For any other mode + the stop event is meaningless. */ + if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS) { + recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(), + &mixer::mutex); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt) +{ + ch->readActions = v; + if (!ch->readActions && recsStopOnChanHalt) + ch->kill(0); // FIXME - wrong frame value +} + + +/* -------------------------------------------------------------------------- */ + + +void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, bool recsStopOnChanHalt) +{ + if (treatRecsAsLoops) + ch->recStatus = ChannelStatus::WAIT; + else + setReadActions(ch, true, recsStopOnChanHalt); +} + + +/* -------------------------------------------------------------------------- */ + + +void stopReadingActions(SampleChannel* ch, bool isClockRunning, bool treatRecsAsLoops, + bool recsStopOnChanHalt) +{ + /* First of all, if the clock is not running just stop and disable everything. + Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the + channel in REC_ENDING status. */ + + if (!isClockRunning) { + ch->recStatus = ChannelStatus::OFF; + setReadActions(ch, false, false); + } + else + if (ch->recStatus == ChannelStatus::WAIT) + ch->recStatus = ChannelStatus::OFF; + else + if (ch->recStatus == ChannelStatus::ENDING) + ch->recStatus = ChannelStatus::PLAY; + else + if (treatRecsAsLoops) + ch->recStatus = ChannelStatus::ENDING; + else + setReadActions(ch, false, recsStopOnChanHalt); +} +}}}; \ No newline at end of file diff --git a/src/core/sampleChannelRec.h b/src/core/sampleChannelRec.h new file mode 100644 index 0000000..6fe5d48 --- /dev/null +++ b/src/core/sampleChannelRec.h @@ -0,0 +1,73 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_SAMPLE_CHANNEL_REC_H +#define G_SAMPLE_CHANNEL_REC_H + + +class SampleChannel; + + +namespace giada { +namespace m { +namespace sampleChannelRec +{ +void parseEvents(SampleChannel* ch, mixer::FrameEvents fe); + +/* recordStart +Records a G_ACTION_KEYPRESS if capable of. Returns true if a start() call can +be performed. */ + +bool recordStart(SampleChannel* ch, bool doQuantize); + +/* recordKill +Records a G_ACTION_KILL if capable of. Returns true if a kill() call can +be performed. */ + +bool recordKill(SampleChannel* ch); + +/* recordStop +Ends overdub mode SINGLE_PRESS channels. */ + +void recordStop(SampleChannel* ch); + +void recordMute(SampleChannel* ch); + +/* setReadActions +If enabled (v == true), Recorder will read actions from channel 'ch'. If +recsStopOnChanHalt == true and v == false, will also kill the channel. */ + +void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt); + +void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, + bool recsStopOnChanHalt); +void stopReadingActions(SampleChannel* ch, bool isClockRunning, + bool treatRecsAsLoops, bool recsStopOnChanHalt); +}}}; + + +#endif \ No newline at end of file diff --git a/src/core/types.h b/src/core/types.h new file mode 100644 index 0000000..d83fd3c --- /dev/null +++ b/src/core/types.h @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 5c95195..9512308 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -75,14 +75,12 @@ Wave::Wave(const Wave& other) /* -------------------------------------------------------------------------- */ -bool Wave::alloc(int size, int channels, int rate, int bits, const std::string& path) +void Wave::alloc(int size, int channels, int rate, int bits, const std::string& path) { - if (!buffer.alloc(size, channels)) - return false; + buffer.alloc(size, channels); m_rate = rate; m_bits = bits; m_path = path; - return true; } diff --git a/src/core/wave.h b/src/core/wave.h index ecdcc4a..5ea0e71 100644 --- a/src/core/wave.h +++ b/src/core/wave.h @@ -84,7 +84,7 @@ public: void copyData(float* data, int frames, int offset=0); - bool alloc(int size, int channels, int rate, int bits, const std::string& path); + void alloc(int size, int channels, int rate, int bits, const std::string& path); private: diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp index 9154939..d3f147e 100644 --- a/src/core/waveFx.cpp +++ b/src/core/waveFx.cpp @@ -109,10 +109,7 @@ int monoToStereo(Wave& w) return G_RES_OK; AudioBuffer newData; - if (!newData.alloc(w.getSize(), G_MAX_IO_CHANS)) { - gu_log("[wfx::monoToStereo] unable to allocate memory!\n"); - return G_RES_ERR_MEMORY; - } + newData.alloc(w.getSize(), G_MAX_IO_CHANS); for (int i=0; ialloc(header.frames, header.channels, header.samplerate, getBits(header), path)) { - gu_log("[waveManager::create] unable to allocate memory\n"); - delete wave; - return G_RES_ERR_MEMORY; - } + wave->alloc(header.frames, header.channels, header.samplerate, getBits(header), path); if (sf_readf_float(fileIn, wave->getFrame(0), header.frames) != header.frames) gu_log("[waveManager::create] warning: incomplete read!\n"); @@ -122,15 +118,11 @@ int create(const string& path, Wave** out) /* -------------------------------------------------------------------------- */ -int createEmpty(int frames, int channels, int samplerate, const string& name, +void createEmpty(int frames, int channels, int samplerate, const string& name, Wave** out) { Wave* wave = new Wave(); - if (!wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name)) { - gu_log("[waveManager::createEmpty] unable to allocate memory\n"); - delete wave; - return G_RES_ERR_MEMORY; - } + wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name); wave->setLogical(true); @@ -138,25 +130,19 @@ int createEmpty(int frames, int channels, int samplerate, const string& name, gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n", wave->getSize()); - - return G_RES_OK; } /* -------------------------------------------------------------------------- */ -int createFromWave(const Wave* src, int a, int b, Wave** out) +void createFromWave(const Wave* src, int a, int b, Wave** out) { int channels = src->getChannels(); int frames = b - a; Wave* wave = new Wave(); - if (!wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath())) { - gu_log("[waveManager::createFromWave] unable to allocate memory\n"); - delete wave; - return G_RES_ERR_MEMORY; - } + wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath()); wave->copyData(src->getFrame(a), frames); wave->setLogical(true); @@ -164,8 +150,6 @@ int createFromWave(const Wave* src, int a, int b, Wave** out) *out = wave; gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", frames); - - return G_RES_OK; } @@ -178,10 +162,7 @@ int resample(Wave* w, int quality, int samplerate) int newSizeFrames = ceil(w->getSize() * ratio); AudioBuffer newData; - if (!newData.alloc(newSizeFrames, w->getChannels())) { - gu_log("[waveManager::resample] unable to allocate memory\n"); - return G_RES_ERR_MEMORY; - } + newData.alloc(newSizeFrames, w->getChannels()); SRC_DATA src_data; src_data.data_in = w->getFrame(0); diff --git a/src/core/waveManager.h b/src/core/waveManager.h index b350695..0f0b764 100644 --- a/src/core/waveManager.h +++ b/src/core/waveManager.h @@ -47,13 +47,13 @@ int create(const std::string& path, Wave** out); /* createEmpty Creates a new silent Wave object. */ -int createEmpty(int frames, int channels, int samplerate, const std::string& name, +void createEmpty(int frames, int channels, int samplerate, const std::string& name, Wave** out); /* createFromWave Creates a new Wave from an existing one, copying the data in range a - b. */ -int createFromWave(const Wave* src, int a, int b, Wave** out); +void createFromWave(const Wave* src, int a, int b, Wave** out); int resample(Wave* w, int quality, int samplerate); int save(Wave* w, const std::string& path); diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index 4dd0171..cdfe12d 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -79,8 +79,8 @@ int loadChannel(SampleChannel* ch, const string& fname) /* Always stop a channel before loading a new sample in it. This will prevent issues if tracker is outside the boundaries of the new sample -> segfault. */ - if (ch->status & (STATUS_PLAY | STATUS_ENDING)) - ch->hardStop(0); + if (ch->isPlaying()) + ch->kill(0); /* Save the patch and take the last browser's dir in order to re-use it the next time. */ @@ -113,7 +113,7 @@ int loadChannel(SampleChannel* ch, const string& fname) /* -------------------------------------------------------------------------- */ -Channel* addChannel(int column, int type, int size) +Channel* addChannel(int column, ChannelType type, int size) { Channel* ch = m::mh::addChannel(type); geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size); @@ -134,7 +134,7 @@ void deleteChannel(Channel* ch) recorder::clearChan(ch->index); ch->hasActions = false; #ifdef WITH_VST - pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex_plugins, ch); + pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex, ch); #endif Fl::lock(); G_MainWin->keyboard->deleteChannel(ch->guiChannel); @@ -149,7 +149,7 @@ void deleteChannel(Channel* ch) void freeChannel(Channel* ch) { - if (ch->status == STATUS_PLAY) { + if (ch->isPlaying()) { if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) return; } @@ -159,7 +159,6 @@ void freeChannel(Channel* ch) G_MainWin->keyboard->freeChannel(ch->guiChannel); m::recorder::clearChan(ch->index); - ch->hasActions = false; ch->empty(); /* delete any related subwindow */ @@ -204,7 +203,7 @@ int cloneChannel(Channel* src) ch, src->guiChannel->getSize()); ch->guiChannel = gch; - ch->copy(src, &mixer::mutex_plugins); + ch->copy(src, &mixer::mutex); G_MainWin->keyboard->updateChannel(ch->guiChannel); return true; @@ -272,21 +271,7 @@ void setPanning(SampleChannel* ch, float val) void toggleMute(Channel* ch, bool gui) { - using namespace giada::m; - - if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) { - if (!ch->mute) { - recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(), - kernelAudio::getRealBufSize()); - ch->readActions = false; // don't read actions while overdubbing - } - else - recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(), - &mixer::mutex_recs); - } - - ch->mute ? ch->unsetMute(false) : ch->setMute(false); - + ch->setMute(!ch->mute, EventType::MANUAL); if (!gui) { Fl::lock(); ch->guiChannel->mute->value(ch->mute); @@ -300,11 +285,8 @@ void toggleMute(Channel* ch, bool gui) void toggleSolo(Channel* ch, bool gui) { - using namespace giada::m; - ch->solo = !ch->solo; - mh::updateSoloCount(); - + m::mh::updateSoloCount(); if (!gui) { Fl::lock(); ch->guiChannel->solo->value(ch->solo); @@ -350,7 +332,7 @@ void setName(Channel* ch, const string& name) /* -------------------------------------------------------------------------- */ -void toggleReadingRecs(SampleChannel* ch, bool gui) +void toggleReadingActions(Channel* ch, bool gui) { /* When you call startReadingRecs with conf::treatRecsAsLoops, the @@ -361,24 +343,22 @@ void toggleReadingRecs(SampleChannel* ch, bool gui) handle the case of when you press 'R', the channel goes into REC_WAITING and then you press 'R' again to undo the status. */ - if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING)) - stopReadingRecs(ch, gui); + if (ch->readActions || (!ch->readActions && ch->recStatus == ChannelStatus::WAIT)) + stopReadingActions(ch, gui); else - startReadingRecs(ch, gui); + startReadingActions(ch, gui); } /* -------------------------------------------------------------------------- */ -void startReadingRecs(SampleChannel* ch, bool gui) +void startReadingActions(Channel* ch, bool gui) { using namespace giada::m; - if (conf::treatRecsAsLoops) - ch->recStatus = REC_WAITING; - else - ch->setReadActions(true, conf::recsStopOnChanHalt); + ch->startReadingActions(conf::treatRecsAsLoops, conf::recsStopOnChanHalt); + if (!gui) { Fl::lock(); static_cast(ch->guiChannel)->readActions->value(1); @@ -390,29 +370,12 @@ void startReadingRecs(SampleChannel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void stopReadingRecs(SampleChannel* ch, bool gui) +void stopReadingActions(Channel* ch, bool gui) { using namespace giada::m; - /* First of all, if the clock is not running just stop and disable everything. - Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the - channel in REC_ENDING status. */ - - if (!clock::isRunning()) { - ch->recStatus = REC_STOPPED; - ch->setReadActions(false, false); - } - else - if (ch->recStatus == REC_WAITING) - ch->recStatus = REC_STOPPED; - else - if (ch->recStatus == REC_ENDING) - ch->recStatus = REC_READING; - else - if (conf::treatRecsAsLoops) - ch->recStatus = REC_ENDING; - else - ch->setReadActions(false, conf::recsStopOnChanHalt); + ch->stopReadingActions(clock::isRunning(), conf::treatRecsAsLoops, + conf::recsStopOnChanHalt); if (!gui) { Fl::lock(); diff --git a/src/glue/channel.h b/src/glue/channel.h index e5212bb..8f6c1f9 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -30,12 +30,14 @@ #include +#include "../core/types.h" class Channel; class SampleChannel; class gdSampleEditor; + namespace giada { namespace c { namespace channel @@ -43,7 +45,7 @@ namespace channel /* addChannel Adds an empty new channel to the stack. Returns the new channel. */ -Channel* addChannel(int column, int type, int size); +Channel* addChannel(int column, ChannelType type, int size); /* loadChannel Fills an existing channel with a wave. */ @@ -84,9 +86,9 @@ void setBoost(SampleChannel* ch, float val); Handles the 'R' button. If gui == true the signal comes from an user interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ -void toggleReadingRecs(SampleChannel* ch, bool gui=true); -void startReadingRecs(SampleChannel* ch, bool gui=true); -void stopReadingRecs(SampleChannel* ch, bool gui=true); +void toggleReadingActions(Channel* ch, bool gui=true); +void startReadingActions(Channel* ch, bool gui=true); +void stopReadingActions(Channel* ch, bool gui=true); }}}; // giada::c::channel:: diff --git a/src/glue/io.cpp b/src/glue/io.cpp index 2da007d..d978803 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -58,108 +58,22 @@ namespace giada { namespace c { namespace io { -namespace -{ -void ctrlPress(SampleChannel* ch) -{ - c::channel::toggleMute(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void shiftPress(SampleChannel* ch) +void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) { - /* action recording on: - if sequencer is running, rec a killchan - action recording off: - if chan has recorded events: - | if seq is playing OR channel 'c' is stopped, de/activate recs - | else kill chan - else kill chan. */ - - if (m::recorder::active) { - if (!m::clock::isRunning()) - return; - ch->kill(0); // on frame 0: user-generated event - if (m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording) && - !(ch->mode & LOOP_ANY)) - { // don't record killChan actions for LOOP channels - m::recorder::rec(ch->index, G_ACTION_KILL, m::clock::getCurrentFrame()); - ch->hasActions = true; - } + /* Everything occurs on frame 0 here: they are all user-generated events. */ + if (ctrl) { + ch->recordMute(); + c::channel::toggleMute(ch); } - else { - if (ch->hasActions) { - if (m::clock::isRunning() || ch->status == STATUS_OFF) - ch->readActions ? c::channel::stopReadingRecs(ch) : c::channel::startReadingRecs(ch); - else - ch->kill(0); // on frame 0: user-generated event - } - else - ch->kill(0); // on frame 0: user-generated event + else + if (shift) { + if (ch->recordKill()) + ch->kill(0); } -} - - -/* -------------------------------------------------------------------------- */ - - -void cleanPress(SampleChannel* ch, int velocity) -{ - /* Record now if the quantizer is off, otherwise let mixer to handle it when a - quantoWait has passed. Moreover, KEYPRESS and KEYREL are meaningless for loop - modes. */ - - if (m::clock::getQuantize() == 0 && - m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording) && - !(ch->mode & LOOP_ANY)) - { - if (ch->mode == SINGLE_PRESS) { - m::recorder::startOverdub(ch->index, G_ACTION_KEYS, m::clock::getCurrentFrame(), - m::kernelAudio::getRealBufSize()); - ch->readActions = false; // don't read actions while overdubbing - } - else { - m::recorder::rec(ch->index, G_ACTION_KEYPRESS, m::clock::getCurrentFrame()); - ch->hasActions = true; - - /* Why return here? You record an action and then you call ch->start: - Mixer, which is on another thread, reads your newly recorded action if you - have readActions == true, and then ch->start kicks in right after it. - The result: Mixer plays the channel (due to the new action) but ch->start - kills it right away (because the sample is playing). Fix: call ch->start - only if you are not recording anything, i.e. let Mixer play it. */ - - if (ch->readActions) - return; - } + else { + if (ch->recordStart(m::clock::canQuantize())) + ch->start(0, m::clock::canQuantize(), 0); } - - /* This is a user-generated event, so it's on frame 0. For one-shot modes, - velocity drives the internal volume. */ - - if (ch->mode & SINGLE_ANY && ch->midiInVeloAsVol) - ch->setVolumeI(u::math::map((float)velocity, 0.0f, 127.0f, 0.0f, 1.0f)); - - ch->start(0, true, m::clock::getQuantize(), m::clock::isRunning(), false, true); -} - -} // {anonymous} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) -{ - if (ch->type == G_CHANNEL_SAMPLE) - keyPress(static_cast(ch), ctrl, shift, velocity); - else - keyPress(static_cast(ch), ctrl, shift); } @@ -168,61 +82,10 @@ void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) void keyRelease(Channel* ch, bool ctrl, bool shift) { - if (ch->type == G_CHANNEL_SAMPLE) - keyRelease(static_cast(ch), ctrl, shift); -} - - -/* -------------------------------------------------------------------------- */ - - -void keyPress(MidiChannel* ch, bool ctrl, bool shift) -{ - if (ctrl) - c::channel::toggleMute(ch); - else - if (shift) - ch->kill(0); // on frame 0: user-generated event - else - ch->start(0, true, m::clock::getQuantize(), m::clock::isRunning(), false, true); // on frame 0: user-generated event -} - - -/* -------------------------------------------------------------------------- */ - - -void keyPress(SampleChannel* ch, bool ctrl, bool shift, int velocity) -{ - if (ctrl) - ctrlPress(ch); - else if (shift) - shiftPress(ch); - else - cleanPress(ch, velocity); -} - - -/* -------------------------------------------------------------------------- */ - - -void keyRelease(SampleChannel* ch, bool ctrl, bool shift) -{ - using namespace giada::m; - - if (ctrl || shift) - return; - - ch->stop(); - - /* record a key release only if channel is single_press. For any - * other mode the KEY REL is meaningless. */ - - if (ch->mode == SINGLE_PRESS && recorder::canRec(ch, clock::isRunning(), mixer::recording)) - recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(), - &mixer::mutex_recs); - - /* the GUI update is done by gui_refresh() */ - + if (!ctrl && !shift) { + ch->recordStop(); + ch->stop(); + } } @@ -248,7 +111,7 @@ void startActionRec(bool gui) recorder::active = true; if (!clock::isRunning()) - glue_startSeq(false); // update gui ayway + glue_startSeq(false); // update gui if (!gui) { Fl::lock(); @@ -263,19 +126,18 @@ void startActionRec(bool gui) void stopActionRec(bool gui) { - /* stop the recorder and sort new actions */ + /* Stop the recorder and sort newly recorder actions. */ m::recorder::active = false; m::recorder::sortActions(); for (Channel* ch : m::mixer::channels) { - if (ch->type == G_CHANNEL_MIDI) + if (ch->type == ChannelType::MIDI) continue; - SampleChannel* sch = static_cast(ch); - G_MainWin->keyboard->setChannelWithActions(static_cast(sch->guiChannel)); - if (!sch->readActions && sch->hasActions) - c::channel::startReadingRecs(sch, false); + G_MainWin->keyboard->setChannelWithActions(static_cast(ch->guiChannel)); + if (!ch->readActions && ch->hasActions) + c::channel::startReadingActions(ch, false); } if (!gui) { @@ -346,20 +208,6 @@ int stopInputRec(bool gui) mh::stopInputRec(); - /* Start all sample channels in loop mode that were armed, i.e. that were - recording stuff and not yet in play. They are also started in force mode, i.e. - they must start playing right away at the current frame, not at the next first - beat. */ - - for (Channel* ch : mixer::channels) { - if (ch->type == G_CHANNEL_MIDI) - continue; - SampleChannel* sch = static_cast(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); diff --git a/src/glue/io.h b/src/glue/io.h index 0dccf9e..799a56e 100644 --- a/src/glue/io.h +++ b/src/glue/io.h @@ -45,15 +45,12 @@ namespace c { namespace io { /* keyPress / keyRelease - * handle the key pressure, either via mouse/keyboard or MIDI. If gui - * is true it means that the event comes from the main window (mouse, - * keyb or MIDI), otherwise the event comes from the action recorder. */ +Handle the key pressure, either via mouse/keyboard or MIDI. If gui is true the +event comes from the main window (mouse, keyboard or MIDI), otherwise the event +comes from the action recorder. */ -void keyPress (Channel* ch, bool ctrl, bool shift, int velocity); -void keyPress (SampleChannel* ch, bool ctrl, bool shift, int velocity); -void keyPress (MidiChannel* ch, bool ctrl, bool shift); -void keyRelease(Channel* ch, bool ctrl, bool shift); -void keyRelease(SampleChannel* ch, bool ctrl, bool shift); +void keyPress (Channel* ch, bool ctrl, bool shift, int velocity); +void keyRelease(Channel* ch, bool ctrl, bool shift); /* start/stopActionRec Handles the action recording. If gui == true the signal comes from an user diff --git a/src/glue/main.cpp b/src/glue/main.cpp index 4fc529d..95d1138 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -42,6 +42,7 @@ #include "../core/kernelMidi.h" #include "../core/kernelAudio.h" #include "../core/conf.h" +#include "../core/const.h" #ifdef WITH_VST #include "../core/pluginHost.h" #endif @@ -51,57 +52,81 @@ extern gdMainWindow *G_MainWin; +using std::string; using namespace giada::m; -void glue_setBpm(const char *v1, const char *v2) +namespace +{ +void setBpm_(float f, string s) +{ + if (f < G_MIN_BPM) { + f = G_MIN_BPM; + s = G_MIN_BPM_STR; + } + else + if (f > G_MAX_BPM) { + f = G_MAX_BPM; + s = G_MAX_BPM_STR; + } + + float vPre = clock::getBpm(); + clock::setBpm(f); + recorder::updateBpm(vPre, f, clock::getQuanto()); + mixer::allocVirtualInput(clock::getFramesInLoop()); + + gu_refreshActionEditor(); + G_MainWin->mainTimer->setBpm(s.c_str()); + + gu_log("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s.c_str(), clock::getBpm()); +} +} // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +void glue_setBpm(const char* v1, const char* v2) { /* Never change this stuff while recording audio */ if (mixer::recording) return; - char bpmS[6]; - float bpmF = atof(v1) + (atof(v2)/10); - if (bpmF < 20.0f) { - bpmF = 20.0f; - sprintf(bpmS, "20.0"); - } - else - sprintf(bpmS, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2); - - /* a value such as atof("120.1") will never be 120.1 but 120.0999999, - * because of the rounding error. So we pass the real "wrong" value to - * G_Mixer and we show the nice looking (but fake) one to the GUI. */ + /* A value such as atof("120.1") will never be 120.1 but 120.0999999, because + of the rounding error. So we pass the real "wrong" value to mixer and we show + the nice looking (but fake) one to the GUI. + On Linux, let Jack handle the bpm change if its on. */ - float oldBpmF = clock::getBpm(); - clock::setBpm(bpmF); - recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto()); - mixer::allocVirtualInput(clock::getFramesInLoop()); + float f = atof(v1) + (atof(v2)/10); + string s = string(v1) + "." + string(v2); -#ifdef __linux__ - kernelAudio::jackSetBpm(clock::getBpm()); +#ifdef G_OS_LINUX + if (kernelAudio::getAPI() == G_SYS_API_JACK) + kernelAudio::jackSetBpm(f); + else #endif - - gu_refreshActionEditor(); - G_MainWin->mainTimer->setBpm(bpmS); - - gu_log("[glue] Bpm changed to %s (real=%f)\n", bpmS, clock::getBpm()); + setBpm_(f, s); } /* -------------------------------------------------------------------------- */ -void glue_setBpm(float v) +void glue_setBpm(float f) { - if (v < G_MIN_BPM || v > G_MAX_BPM) - v = G_DEFAULT_BPM; - double fIpart; - double fPpart = modf(v, &fIpart); - int iIpart = fIpart; - int iPpart = ceilf(fPpart); - glue_setBpm(gu_iToString(iIpart).c_str(), gu_iToString(iPpart).c_str()); + /* Never change this stuff while recording audio */ + + if (mixer::recording) + return; + + float intpart; + float fracpart = std::round(std::modf(f, &intpart) * 10); + string s = std::to_string((int) intpart) + "." + std::to_string((int)fracpart); + + setBpm_(f, s); } @@ -232,7 +257,7 @@ void glue_resetToInitState(bool resetGui, bool createColumns) mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize()); recorder::init(); #ifdef WITH_VST - pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins); + pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex); #endif G_MainWin->keyboard->clear(); diff --git a/src/glue/main.h b/src/glue/main.h index c684b22..5dc304d 100644 --- a/src/glue/main.h +++ b/src/glue/main.h @@ -36,8 +36,16 @@ #define G_GLUE_MAIN_H +/* glue_setBpm (1) +Sets bpm value from string to float. */ + void glue_setBpm(const char* v1, const char* v2); + +/* glue_setBpm (2) +Sets bpm value. Usually called from the Jack callback or non-UI components. */ + void glue_setBpm(float v); + void glue_setBeats(int beats, int bars, bool expand); void glue_quantize(int val); void glue_setOutVol(float v, bool gui=true); @@ -46,13 +54,13 @@ void glue_clearAllSamples(); void glue_clearAllActions(); /* resetToInitState - * reset Giada to init state. If resetGui also refresh all widgets. If - * createColumns also build initial empty columns. */ +Resets Giada to init state. If resetGui also refresh all widgets. If +createColumns also build initial empty columns. */ void glue_resetToInitState(bool resetGui=true, bool createColumns=true); /* beatsDivide/Multiply - * shrinks or enlarges the number of beats by 2. */ +Shrinks or enlarges the number of beats by 2. */ void glue_beatsMultiply(); void glue_beatsDivide(); diff --git a/src/glue/plugin.cpp b/src/glue/plugin.cpp index 20a0f8b..c5282ac 100644 --- a/src/glue/plugin.cpp +++ b/src/glue/plugin.cpp @@ -28,6 +28,7 @@ #ifdef WITH_VST +#include #include "../core/pluginHost.h" #include "../core/mixer.h" #include "../core/plugin.h" @@ -82,7 +83,7 @@ Plugin* addPlugin(Channel* ch, int index, int stackType) { if (index >= pluginHost::countAvailablePlugins()) return nullptr; - return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch); + return pluginHost::addPlugin(index, stackType, &mixer::mutex, ch); } @@ -91,7 +92,7 @@ Plugin* addPlugin(Channel* ch, int index, int stackType) void swapPlugins(Channel* ch, int index1, int index2, int stackType) { - pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex_plugins, + pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex, ch); } @@ -101,7 +102,7 @@ void swapPlugins(Channel* ch, int index1, int index2, int stackType) void freePlugin(Channel* ch, int index, int stackType) { - pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch); + pluginHost::freePlugin(index, stackType, &mixer::mutex, ch); } diff --git a/src/glue/recorder.cpp b/src/glue/recorder.cpp index 53670c3..f193285 100644 --- a/src/glue/recorder.cpp +++ b/src/glue/recorder.cpp @@ -51,7 +51,7 @@ namespace void updateChannel(geChannel* gch) { gch->ch->hasActions = m::recorder::hasActions(gch->ch->index); - if (gch->ch->type == G_CHANNEL_SAMPLE && !gch->ch->hasActions) + if (gch->ch->type == ChannelType::SAMPLE && !gch->ch->hasActions) static_cast(gch)->hideActionButton(); /* TODO - set mute=false */ gu_refreshActionEditor(); // refresh a.editor window, it could be open diff --git a/src/glue/sampleEditor.cpp b/src/glue/sampleEditor.cpp index 568e508..1793a92 100644 --- a/src/glue/sampleEditor.cpp +++ b/src/glue/sampleEditor.cpp @@ -121,12 +121,7 @@ void copy(SampleChannel* ch, int a, int b) { if (m_waveBuffer != nullptr) delete m_waveBuffer; - - int result = m::waveManager::createFromWave(ch->wave, a, b, &m_waveBuffer); - if (result != G_RES_OK) { - gu_log("[sampleEditor::copy] unable to create wave buffer!\n"); - return; - } + m::waveManager::createFromWave(ch->wave, a, b, &m_waveBuffer); } @@ -243,9 +238,9 @@ void setPlayHead(SampleChannel* ch, int f) /* -------------------------------------------------------------------------- */ -void setPreview(SampleChannel* ch, int mode) +void setPreview(SampleChannel* ch, PreviewMode mode) { - ch->setPreviewMode(mode); + ch->previewMode = mode; gdSampleEditor* gdEditor = getSampleEditorWindow(); gdEditor->play->value(!gdEditor->play->value()); } @@ -270,14 +265,10 @@ void rewindPreview(SampleChannel* ch) void toNewChannel(SampleChannel* ch, int a, int b) { SampleChannel* newCh = static_cast(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(); diff --git a/src/glue/sampleEditor.h b/src/glue/sampleEditor.h index b38cbb2..4933100 100644 --- a/src/glue/sampleEditor.h +++ b/src/glue/sampleEditor.h @@ -29,6 +29,9 @@ #define G_GLUE_SAMPLE_EDITOR_H +#include "../core/types.h" + + class SampleChannel; class geWaveform; @@ -66,7 +69,7 @@ Changes playhead's position. Used in preview. */ void setPlayHead(SampleChannel* ch, int f); -void setPreview(SampleChannel* ch, int mode); +void setPreview(SampleChannel* ch, PreviewMode mode); void rewindPreview(SampleChannel* ch); /* toNewChannel diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index 4f99a00..2a79efc 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -283,7 +283,7 @@ void glue_loadPatch(void* data) unsigned k = 0; for (const patch::channel_t& pch : patch::channels) { if (pch.column == col.index) { - Channel* ch = c::channel::addChannel(pch.column, pch.type, pch.size); + Channel* ch = c::channel::addChannel(pch.column, static_cast(pch.type), pch.size); ch->readPatch(basePath, k); } browser->setStatusBar(steps); @@ -360,7 +360,7 @@ void glue_saveProject(void* data) for (const Channel* ch : mixer::channels) { - if (ch->type == G_CHANNEL_MIDI) + if (ch->type == ChannelType::MIDI) continue; const SampleChannel* sch = static_cast(ch); diff --git a/src/gui/dialogs/gd_about.cpp b/src/gui/dialogs/about.cpp similarity index 92% rename from src/gui/dialogs/gd_about.cpp rename to src/gui/dialogs/about.cpp index eb9228e..79f7996 100644 --- a/src/gui/dialogs/gd_about.cpp +++ b/src/gui/dialogs/about.cpp @@ -36,10 +36,10 @@ #endif #include "../../utils/gui.h" #include "../../utils/string.h" -#include "../../utils/deps.h" +#include "../../utils/ver.h" #include "../elems/basics/button.h" #include "../elems/basics/box.h" -#include "gd_about.h" +#include "about.h" using std::string; @@ -87,9 +87,9 @@ gdAbout::gdAbout() "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() + 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 @@ -132,13 +132,13 @@ gdAbout::~gdAbout() /* -------------------------------------------------------------------------- */ -void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); } +void gdAbout::cb_close(Fl_Widget* w, void* p) { ((gdAbout*)p)->cb_close(); } /* -------------------------------------------------------------------------- */ -void gdAbout::__cb_close() +void gdAbout::cb_close() { do_callback(); } diff --git a/src/gui/dialogs/gd_about.h b/src/gui/dialogs/about.h similarity index 89% rename from src/gui/dialogs/gd_about.h rename to src/gui/dialogs/about.h index aa93094..8d37cda 100644 --- a/src/gui/dialogs/gd_about.h +++ b/src/gui/dialogs/about.h @@ -40,13 +40,13 @@ class gdAbout : public gdWindow { private: - geBox *logo; - geBox *text; - geButton *close; + geBox* logo; + geBox* text; + geButton* close; #ifdef WITH_VST - geBox *vstText; - geBox *vstLogo; + geBox* vstText; + geBox* vstLogo; #endif public: @@ -54,8 +54,8 @@ public: gdAbout(); ~gdAbout(); - static void cb_close(Fl_Widget *w, void *p); - inline void __cb_close(); + static void cb_close(Fl_Widget* w, void* p); + inline void cb_close(); }; #endif diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp index 885f549..499f9a3 100644 --- a/src/gui/dialogs/gd_actionEditor.cpp +++ b/src/gui/dialogs/gd_actionEditor.cpp @@ -47,10 +47,11 @@ 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), @@ -70,7 +71,7 @@ gdActionEditor::gdActionEditor(Channel *chan) upperArea->begin(); - if (chan->type == G_CHANNEL_SAMPLE) { + if (chan->type == ChannelType::SAMPLE) { actionType = new geChoice(8, 8, 80, 20); gridTool = new geGridTool(actionType->x()+actionType->w()+4, 8, this); actionType->add("key press"); @@ -78,8 +79,8 @@ gdActionEditor::gdActionEditor(Channel *chan) actionType->add("kill chan"); actionType->value(0); - SampleChannel *ch = (SampleChannel*) chan; - if (ch->mode == SINGLE_PRESS || ch->mode & LOOP_ANY) + SampleChannel *ch = static_cast(chan); + if (ch->mode == ChannelMode::SINGLE_PRESS || ch->isAnyLoopMode()) actionType->deactivate(); } else { @@ -99,9 +100,9 @@ gdActionEditor::gdActionEditor(Channel *chan) scroller = new geScroll(8, 36, w()-16, h()-44); - if (chan->type == G_CHANNEL_SAMPLE) { + if (chan->type == ChannelType::SAMPLE) { - SampleChannel *ch = (SampleChannel*) chan; + SampleChannel *ch = static_cast(chan); ac = new geActionEditor (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch); mc = new geMuteEditor (scroller->x(), ac->y()+ac->h()+8, this); @@ -120,7 +121,7 @@ gdActionEditor::gdActionEditor(Channel *chan) /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot * hold keypress/keyrelease actions */ - if (ch->mode & LOOP_ANY) + if (ch->isAnyLoopMode()) ac->deactivate(); } else { @@ -188,7 +189,7 @@ void gdActionEditor::__cb_zoomIn() update(); - if (chan->type == G_CHANNEL_SAMPLE) { + if (chan->type == ChannelType::SAMPLE) { ac->size(totalWidth, ac->h()); mc->size(totalWidth, mc->h()); vc->size(totalWidth, vc->h()); @@ -222,7 +223,7 @@ void gdActionEditor::__cb_zoomOut() update(); - if (chan->type == G_CHANNEL_SAMPLE) { + if (chan->type == ChannelType::SAMPLE) { ac->size(totalWidth, ac->h()); mc->size(totalWidth, mc->h()); vc->size(totalWidth, vc->h()); diff --git a/src/gui/dialogs/midiIO/midiInputChannel.cpp b/src/gui/dialogs/midiIO/midiInputChannel.cpp index 3fec317..8656fdb 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.cpp +++ b/src/gui/dialogs/midiIO/midiInputChannel.cpp @@ -47,6 +47,7 @@ using std::string; using std::vector; +using namespace giada; using namespace giada::m; @@ -59,7 +60,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch) label(title.c_str()); size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H); - int extra = ch->type == G_CHANNEL_SAMPLE ? 28 : 0; + int extra = ch->type == ChannelType::SAMPLE ? 28 : 0; Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20 + extra); groupHeader->begin(); @@ -98,7 +99,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch) enable->value(ch->midiIn); enable->callback(cb_enable, (void*)this); - if (ch->type == G_CHANNEL_SAMPLE) { + if (ch->type == ChannelType::SAMPLE) { veloAsVol->value(static_cast(ch)->midiInVeloAsVol); veloAsVol->callback(cb_veloAsVol, (void*)this); } @@ -165,7 +166,7 @@ void gdMidiInputChannel::addChannelLearners() new geMidiLearner(0, 0, LEARNER_WIDTH, "mute", cb_learn, &ch->midiInMute, ch); new geMidiLearner(0, 0, LEARNER_WIDTH, "solo", cb_learn, &ch->midiInSolo, ch); new geMidiLearner(0, 0, LEARNER_WIDTH, "volume", cb_learn, &ch->midiInVolume, ch); - if (ch->type == G_CHANNEL_SAMPLE) { + if (ch->type == ChannelType::SAMPLE) { new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch", cb_learn, &(static_cast(ch))->midiInPitch, ch); new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn, diff --git a/src/gui/dialogs/pluginList.cpp b/src/gui/dialogs/pluginList.cpp index 9ba36c2..ab0f3fe 100644 --- a/src/gui/dialogs/pluginList.cpp +++ b/src/gui/dialogs/pluginList.cpp @@ -28,27 +28,21 @@ #ifdef WITH_VST +#include +#include #include "../../utils/gui.h" -#include "../../utils/fs.h" #include "../../core/conf.h" #include "../../core/const.h" -#include "../../core/graphics.h" #include "../../core/pluginHost.h" -#include "../../core/plugin.h" -#include "../../core/mixer.h" #include "../../core/channel.h" -#include "../../glue/plugin.h" -#include "../../utils/log.h" #include "../../utils/string.h" #include "../elems/basics/boxtypes.h" -#include "../elems/basics/idButton.h" +#include "../elems/basics/button.h" #include "../elems/basics/statusButton.h" -#include "../elems/basics/choice.h" #include "../elems/mainWindow/mainIO.h" #include "../elems/mainWindow/keyboard/channel.h" +#include "../elems/plugin/pluginElement.h" #include "pluginChooser.h" -#include "pluginWindow.h" -#include "pluginWindowGUI.h" #include "gd_mainWindow.h" #include "pluginList.h" @@ -57,13 +51,14 @@ extern gdMainWindow* G_MainWin; using std::string; -using namespace giada::m; -using namespace giada::c; +using namespace giada; gdPluginList::gdPluginList(int stackType, Channel* ch) : gdWindow(468, 204), ch(ch), stackType(stackType) { + using namespace giada::m; + if (conf::pluginListX) resize(conf::pluginListX, conf::pluginListY, w(), h()); @@ -104,8 +99,8 @@ gdPluginList::gdPluginList(int stackType, Channel* ch) gdPluginList::~gdPluginList() { - conf::pluginListX = x(); - conf::pluginListY = y(); + m::conf::pluginListX = x(); + m::conf::pluginListY = y(); } @@ -120,17 +115,17 @@ void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_ void gdPluginList::cb_refreshList(Fl_Widget* v, void* p) { - /* note: this callback is fired by gdBrowser. Close its window first, - * by calling the parent (pluginList) and telling it to delete its - * subwindow (i.e. gdBrowser). */ + /* Note: this callback is fired by gdBrowser. Close its window first, by + calling the parent (pluginList) and telling it to delete its subwindow + (i.e. gdBrowser). */ - gdWindow *child = (gdWindow*) v; + gdWindow* child = static_cast(v); if (child->getParent() != nullptr) (child->getParent())->delSubWindow(child); - /* finally refresh plugin list: void *p is a pointer to gdPluginList. - * This callback works even when you click 'x' to close the window... - * well, who cares */ + /* Finally refresh plugin list: void *p is a pointer to gdPluginList. This + callback works even when you click 'x' to close the window... well, it does + not matter. */ ((gdPluginList*)p)->refreshList(); ((gdPluginList*)p)->redraw(); @@ -142,10 +137,12 @@ void gdPluginList::cb_refreshList(Fl_Widget* v, void* p) void gdPluginList::cb_addPlugin() { - /* the usual callback that gdWindow adds to each subwindow in this case - * is not enough, because when we close the browser the plugin list - * must be redrawn. We have a special callback, cb_refreshList, which - * we add to gdPluginChooser. It does exactly what we need. */ + using namespace giada::m; + + /* The usual callback that gdWindow adds to each subwindow in this case is not + enough, because when we close the browser the plugin list must be redrawn. We + have a special callback, cb_refreshList, which we add to gdPluginChooser. + It does exactly what we need. */ gdPluginChooser* pc = new gdPluginChooser(conf::pluginChooserX, conf::pluginChooserY, conf::pluginChooserW, conf::pluginChooserH, @@ -160,6 +157,8 @@ void gdPluginList::cb_addPlugin() void gdPluginList::refreshList() { + using namespace giada::m; + /* delete the previous list */ list->clear(); @@ -173,9 +172,10 @@ void gdPluginList::refreshList() int i = 0; while (ix(), list->y()-list->yposition()+(i*24), 800); - list->add(gdp); + Plugin* plugin = pluginHost::getPluginByIndex(i, stackType, ch); + gePluginElement* gdpe = new gePluginElement(this, plugin, list->x(), + list->y()-list->yposition()+(i*24), 800); + list->add(gdpe); i++; } @@ -200,13 +200,11 @@ void gdPluginList::refreshList() gdPluginListMaster */ if (stackType == pluginHost::MASTER_OUT) { - G_MainWin->mainIO->setMasterFxOutFull( - pluginHost::countPlugins(stackType, ch) > 0); + G_MainWin->mainIO->setMasterFxOutFull(pluginHost::countPlugins(stackType, ch) > 0); } else if (stackType == pluginHost::MASTER_IN) { - G_MainWin->mainIO->setMasterFxInFull( - pluginHost::countPlugins(stackType, ch) > 0); + G_MainWin->mainIO->setMasterFxInFull(pluginHost::countPlugins(stackType, ch) > 0); } else { ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0; @@ -215,163 +213,4 @@ void gdPluginList::refreshList() } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdPlugin::gdPlugin(gdPluginList* gdp, Plugin* p, int X, int Y, int W) - : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p) -{ - begin(); - button = new geIdButton(8, y(), 220, 20); - program = new geChoice(button->x()+button->w()+4, y(), 132, 20); - bypass = new geIdButton(program->x()+program->w()+4, y(), 20, 20); - shiftUp = new geIdButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm); - shiftDown = new geIdButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm); - remove = new geIdButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm); - end(); - - button->copy_label(pPlugin->getName().c_str()); - button->callback(cb_openPluginWindow, (void*)this); - - program->callback(cb_setProgram, (void*)this); - - for (int i=0; igetNumPrograms(); 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 diff --git a/src/gui/dialogs/pluginList.h b/src/gui/dialogs/pluginList.h index 94e49cd..d63ed05 100644 --- a/src/gui/dialogs/pluginList.h +++ b/src/gui/dialogs/pluginList.h @@ -30,35 +30,31 @@ #ifndef GD_PLUGINLIST_H #define GD_PLUGINLIST_H -#include -#include + #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 @@ -70,41 +66,6 @@ public: }; -/* -------------------------------------------------------------------------- */ - - -class gdPlugin : public Fl_Group -{ -private: - - gdPluginList *pParent; - Plugin *pPlugin; - - static void cb_removePlugin(Fl_Widget *v, void *p); - static void cb_openPluginWindow(Fl_Widget *v, void *p); - static void cb_setBypass(Fl_Widget *v, void *p); - static void cb_shiftUp(Fl_Widget *v, void *p); - static void cb_shiftDown(Fl_Widget *v, void *p); - static void cb_setProgram(Fl_Widget *v, void *p); - void cb_removePlugin(); - void cb_openPluginWindow(); - void cb_setBypass(); - void cb_shiftUp(); - void cb_shiftDown(); - void cb_setProgram(); - -public: - - geIdButton *button; - geChoice *program; - geIdButton *bypass; - geIdButton *shiftUp; - geIdButton *shiftDown; - geIdButton *remove; - - gdPlugin(gdPluginList *gdp, Plugin *p, int x, int y, int w); -}; - #endif #endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/pluginWindowGUI.cpp b/src/gui/dialogs/pluginWindowGUI.cpp index 60b560c..35c9ec6 100644 --- a/src/gui/dialogs/pluginWindowGUI.cpp +++ b/src/gui/dialogs/pluginWindowGUI.cpp @@ -36,7 +36,7 @@ #include "../../core/const.h" #include "pluginWindowGUI.h" -#ifdef __APPLE__ +#ifdef G_OS_MAC #import "../../utils/cocoa.h" // objective-c #endif @@ -44,12 +44,30 @@ using namespace giada::m; -gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) - : gdWindow(450, 300), pPlugin(pPlugin) +gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin) + : gdWindow(450, 300), m_plugin(plugin) { show(); -#ifndef __APPLE__ +#ifdef G_OS_LINUX + + /* Fl_Window::show() is not guaranteed to show and draw the window on all + platforms immediately. Instead this is done in the background; particularly on + X11 it will take a few messages (client server roundtrips) to display the + window. Usually this small delay doesn't matter, but in some cases you may + want to have the window instantiated and displayed synchronously. Currently + (as of FLTK 1.3.4) this method has an effect on X11 and Mac OS. + + http://www.fltk.org/doc-1.3/classFl__Window.html#aafbec14ca8ff8abdaff77a35ebb23dd8 + + NOTE - we should try to macOS as well. */ + + wait_for_expose(); + Fl::flush(); + +#endif + +#ifndef G_OS_MAC Fl::check(); @@ -58,26 +76,26 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n", (void*) this, (void*) fl_xid(this)); -#if defined(__APPLE__) +#ifdef G_OS_MAC - void *cocoaWindow = (void*) fl_xid(this); - cocoa_setWindowSize(cocoaWindow, pPlugin->getEditorW(), pPlugin->getEditorH()); - pPlugin->showEditor(cocoa_getViewFromWindow(cocoaWindow)); + 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()); } @@ -85,17 +103,17 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) /* -------------------------------------------------------------------------- */ -void gdPluginWindowGUI::cb_close(Fl_Widget *v, void *p) { ((gdPluginWindowGUI*)p)->__cb_close(); } -void gdPluginWindowGUI::cb_refresh(void *data) { ((gdPluginWindowGUI*)data)->__cb_refresh(); } +void gdPluginWindowGUI::cb_close(Fl_Widget* v, void* p) { ((gdPluginWindowGUI*)p)->cb_close(); } +void gdPluginWindowGUI::cb_refresh(void* data) { ((gdPluginWindowGUI*)data)->cb_refresh(); } /* -------------------------------------------------------------------------- */ -void gdPluginWindowGUI::__cb_close() +void gdPluginWindowGUI::cb_close() { Fl::remove_timeout(cb_refresh); - pPlugin->closeEditor(); + m_plugin->closeEditor(); gu_log("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this); } @@ -103,7 +121,7 @@ void gdPluginWindowGUI::__cb_close() /* -------------------------------------------------------------------------- */ -void gdPluginWindowGUI::__cb_refresh() +void gdPluginWindowGUI::cb_refresh() { pluginHost::runDispatchLoop(); Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this); @@ -115,7 +133,7 @@ void gdPluginWindowGUI::__cb_refresh() gdPluginWindowGUI::~gdPluginWindowGUI() { - __cb_close(); + cb_close(); } #endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/pluginWindowGUI.h b/src/gui/dialogs/pluginWindowGUI.h index d563aee..004be47 100644 --- a/src/gui/dialogs/pluginWindowGUI.h +++ b/src/gui/dialogs/pluginWindowGUI.h @@ -50,16 +50,16 @@ class gdPluginWindowGUI : public gdWindow { private: - Plugin *pPlugin; + Plugin* m_plugin; - static void cb_close (Fl_Widget *v, void *p); - static void cb_refresh (void *data); - inline void __cb_close (); - inline void __cb_refresh(); + static void cb_close (Fl_Widget* v, void* p); + static void cb_refresh(void* data); + inline void cb_close (); + inline void cb_refresh(); public: - gdPluginWindowGUI(Plugin *pPlugin); + gdPluginWindowGUI(Plugin* p); ~gdPluginWindowGUI(); }; diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index ee000d3..4774df2 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +//#include #include "../../glue/channel.h" #include "../../glue/sampleEditor.h" #include "../../core/waveFx.h" @@ -108,7 +108,7 @@ gdSampleEditor::~gdSampleEditor() m::conf::sampleEditorH = h(); m::conf::sampleEditorGridVal = atoi(grid->text()); m::conf::sampleEditorGridOn = snap->value(); - c::sampleEditor::setPreview(ch, G_PREVIEW_NONE); + c::sampleEditor::setPreview(ch, PreviewMode::NONE); } @@ -280,9 +280,9 @@ void gdSampleEditor::cb_togglePreview() using namespace giada::c; if (play->value()) - sampleEditor::setPreview(ch, G_PREVIEW_NONE); + sampleEditor::setPreview(ch, PreviewMode::NONE); else - sampleEditor::setPreview(ch, loop->value() ? G_PREVIEW_LOOP : G_PREVIEW_NORMAL); + sampleEditor::setPreview(ch, loop->value() ? PreviewMode::LOOP : PreviewMode::NORMAL); } diff --git a/src/gui/elems/actionEditor/action.cpp b/src/gui/elems/actionEditor/action.cpp index ee8c954..9a85940 100644 --- a/src/gui/elems/actionEditor/action.cpp +++ b/src/gui/elems/actionEditor/action.cpp @@ -35,6 +35,7 @@ #include "action.h" +using namespace giada; using namespace giada::m; @@ -68,7 +69,7 @@ geAction::geAction(int X, int Y, int H, int frame_a, unsigned index, * do that after the possible recording, otherwise we don't know which * key_release is associated. */ - if (ch->mode == SINGLE_PRESS && type == G_ACTION_KEYPRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS && type == G_ACTION_KEYPRESS) { recorder::action *a2 = nullptr; recorder::getNextAction(ch->index, G_ACTION_KEYREL, frame_a, &a2); if (a2) { @@ -99,7 +100,7 @@ void geAction::draw() else color = G_COLOR_LIGHT_1; - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { fl_rectf(x(), y(), w(), h(), (Fl_Color) color); } else { @@ -146,7 +147,7 @@ int geAction::handle(int e) /* handling of the two margins, left & right. 4 pixels are good enough */ - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { onLeftEdge = false; onRightEdge = false; if (Fl::event_x() >= x() && Fl::event_x() < x()+4) { @@ -179,7 +180,7 @@ void geAction::addAction() * theory behind the singleshot.press actions; for any other kind the * (b) is just a graphical and meaningless point. */ - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { recorder::rec(parent->chan->index, G_ACTION_KEYPRESS, frame_a); recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_a+4096); //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096); @@ -205,15 +206,15 @@ void geAction::delAction() /* if SINGLE_PRESS you must delete both the keypress and the keyrelease * actions. */ - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { recorder::deleteAction(parent->chan->index, frame_a, G_ACTION_KEYPRESS, - false, &mixer::mutex_recs); + false, &mixer::mutex); recorder::deleteAction(parent->chan->index, frame_b, G_ACTION_KEYREL, - false, &mixer::mutex_recs); + false, &mixer::mutex); } else recorder::deleteAction(parent->chan->index, frame_a, type, false, - &mixer::mutex_recs); + &mixer::mutex); parent->chan->hasActions = recorder::hasActions(parent->chan->index); @@ -249,7 +250,7 @@ void geAction::moveAction(int frame_a) recorder::rec(parent->chan->index, type, this->frame_a); - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { frame_b = xToFrame_b(); recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_b); } diff --git a/src/gui/elems/actionEditor/actionEditor.cpp b/src/gui/elems/actionEditor/actionEditor.cpp index 5adfc13..72a23fc 100644 --- a/src/gui/elems/actionEditor/actionEditor.cpp +++ b/src/gui/elems/actionEditor/actionEditor.cpp @@ -39,6 +39,7 @@ extern gdMainWindow *G_MainWin; +using namespace giada; using namespace giada::m; @@ -69,8 +70,8 @@ geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChan if ((action->chan != pParent->chan->index) || (recorder::frames.at(i) > clock::getFramesInLoop()) || - (action->type == G_ACTION_KILL && ch->mode == SINGLE_PRESS) || - (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS) || + (action->type == G_ACTION_KILL && ch->mode == ChannelMode::SINGLE_PRESS) || + (action->type == G_ACTION_KEYREL && ch->mode == ChannelMode::SINGLE_PRESS) || (action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL)) ) continue; @@ -123,7 +124,7 @@ void geActionEditor::updateActions() a = (geAction*)child(i); int newX = x() + (a->frame_a / pParent->zoom); - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { int newW = ((a->frame_b - a->frame_a) / pParent->zoom); if (newW < geAction::MIN_WIDTH) newW = geAction::MIN_WIDTH; @@ -328,7 +329,7 @@ int geActionEditor::handle(int e) bool noChanges = false; if (actionOriginalX == selected->x()) noChanges = true; - if (ch->mode == SINGLE_PRESS && + if (ch->mode == ChannelMode::SINGLE_PRESS && actionOriginalX+actionOriginalW != selected->x()+selected->w()) noChanges = false; @@ -352,7 +353,7 @@ int geActionEditor::handle(int e) int action_x = ((geAction*)child(i))->x(); int action_w = ((geAction*)child(i))->w(); - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { /* when 2 segments overlap? * start = the highest value between the two starting points @@ -413,7 +414,7 @@ bool geActionEditor::actionCollides(int frame) if (((geAction*) child(i))->frame_a == frame) collision = true; - if (ch->mode == SINGLE_PRESS) { + if (ch->mode == ChannelMode::SINGLE_PRESS) { for (int i=0; iframe_b && frame >= c->frame_a) diff --git a/src/gui/elems/actionEditor/envelopeEditor.cpp b/src/gui/elems/actionEditor/envelopeEditor.cpp index 22b97d5..4e032a8 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.cpp +++ b/src/gui/elems/actionEditor/envelopeEditor.cpp @@ -229,7 +229,7 @@ int geEnvelopeEditor::handle(int e) { } else { recorder::deleteAction(pParent->chan->index, - points.at(selectedPoint).frame, type, false, &mixer::mutex_recs); + points.at(selectedPoint).frame, type, false, &mixer::mutex); pParent->chan->hasActions = recorder::hasActions(pParent->chan->index); recorder::sortActions(); points.erase(points.begin() + selectedPoint); @@ -268,7 +268,7 @@ int geEnvelopeEditor::handle(int e) { /* delete previous point and record a new one */ recorder::deleteAction(pParent->chan->index, - points.at(draggedPoint).frame, type, false, &mixer::mutex_recs); + points.at(draggedPoint).frame, type, false, &mixer::mutex); pParent->chan->hasActions = recorder::hasActions(pParent->chan->index); if (range == G_RANGE_FLOAT) { diff --git a/src/gui/elems/actionEditor/muteEditor.cpp b/src/gui/elems/actionEditor/muteEditor.cpp index 9ad1284..5ee61f2 100644 --- a/src/gui/elems/actionEditor/muteEditor.cpp +++ b/src/gui/elems/actionEditor/muteEditor.cpp @@ -287,9 +287,9 @@ int geMuteEditor::handle(int e) { // a, b, points.at(a).frame, points.at(b).frame); recorder::deleteAction(pParent->chan->index, points.at(a).frame, - points.at(a).type, false, &mixer::mutex_recs); // false = don't check vals + points.at(a).type, false, &mixer::mutex); // false = don't check vals recorder::deleteAction(pParent->chan->index, points.at(b).frame, - points.at(b).type, false, &mixer::mutex_recs); // false = don't check vals + points.at(b).type, false, &mixer::mutex); // false = don't check vals pParent->chan->hasActions = recorder::hasActions(pParent->chan->index); recorder::sortActions(); @@ -316,7 +316,7 @@ int geMuteEditor::handle(int e) { recorder::deleteAction(pParent->chan->index, points.at(draggedPoint).frame, points.at(draggedPoint).type, false, - &mixer::mutex_recs); // don't check values + &mixer::mutex); // don't check values pParent->chan->hasActions = recorder::hasActions(pParent->chan->index); recorder::rec( diff --git a/src/gui/elems/actionEditor/pianoItem.cpp b/src/gui/elems/actionEditor/pianoItem.cpp index acb6442..01e787f 100644 --- a/src/gui/elems/actionEditor/pianoItem.cpp +++ b/src/gui/elems/actionEditor/pianoItem.cpp @@ -117,9 +117,9 @@ void gePianoItem::removeAction() { MidiChannel* ch = static_cast(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. */ diff --git a/src/gui/elems/actionEditor/pianoItemOrphaned.cpp b/src/gui/elems/actionEditor/pianoItemOrphaned.cpp index fc32909..261d3b7 100644 --- a/src/gui/elems/actionEditor/pianoItemOrphaned.cpp +++ b/src/gui/elems/actionEditor/pianoItemOrphaned.cpp @@ -84,8 +84,8 @@ int gePianoItemOrphaned::handle(int e) void gePianoItemOrphaned::remove() { MidiChannel *ch = static_cast(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); diff --git a/src/gui/elems/mainWindow/keyboard/channel.cpp b/src/gui/elems/mainWindow/keyboard/channel.cpp index 48db620..83b37e2 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.cpp +++ b/src/gui/elems/mainWindow/keyboard/channel.cpp @@ -49,10 +49,9 @@ extern gdMainWindow* G_MainWin; using namespace giada; -geChannel::geChannel(int X, int Y, int W, int H, int type, Channel* ch) +geChannel::geChannel(int X, int Y, int W, int H, Channel* ch) : Fl_Group(X, Y, W, H, nullptr), - ch (ch), - type (type) + ch (ch) { } @@ -149,38 +148,39 @@ void geChannel::blink() /* -------------------------------------------------------------------------- */ - -void geChannel::setColorsByStatus(int playStatus, int recStatus) +void geChannel::setColorsByStatus(ChannelStatus playStatus, ChannelStatus recStatus) { switch (playStatus) { - case STATUS_OFF: - case STATUS_EMPTY: + case ChannelStatus::OFF: + case ChannelStatus::EMPTY: mainButton->setDefaultMode(); button->imgOn = channelPlay_xpm; button->imgOff = channelStop_xpm; button->redraw(); break; - case STATUS_PLAY: + case ChannelStatus::PLAY: mainButton->setPlayMode(); button->imgOn = channelStop_xpm; button->imgOff = channelPlay_xpm; button->redraw(); break; - case STATUS_WAIT: + case ChannelStatus::WAIT: blink(); break; - case STATUS_ENDING: + case ChannelStatus::ENDING: mainButton->setEndingMode(); break; + default: break; } switch (recStatus) { - case REC_WAITING: + case ChannelStatus::WAIT: blink(); break; - case REC_ENDING: + case ChannelStatus::ENDING: mainButton->setEndingMode(); break; + default: break; } } diff --git a/src/gui/elems/mainWindow/keyboard/channel.h b/src/gui/elems/mainWindow/keyboard/channel.h index 8a3b6bf..7bff681 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.h +++ b/src/gui/elems/mainWindow/keyboard/channel.h @@ -30,6 +30,7 @@ #include +#include "../../../../core/types.h" class Channel; @@ -86,7 +87,7 @@ protected: /* setColorByStatus * update colors depending on channel status. */ - void setColorsByStatus(int playStatus, int recStatus); + void setColorsByStatus(giada::ChannelStatus chan, giada::ChannelStatus rec); /* handleKey * method wrapped by virtual handle(int e). */ @@ -100,7 +101,7 @@ protected: public: - geChannel(int x, int y, int w, int h, int type, Channel* ch); + geChannel(int x, int y, int w, int h, Channel* ch); /* reset * reset channel to initial status. */ @@ -148,8 +149,6 @@ public: #ifdef WITH_VST geStatusButton* fx; #endif - - int type; }; diff --git a/src/gui/elems/mainWindow/keyboard/channelMode.cpp b/src/gui/elems/mainWindow/keyboard/channelMode.cpp index 045be52..44f8a08 100644 --- a/src/gui/elems/mainWindow/keyboard/channelMode.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelMode.cpp @@ -1,4 +1,4 @@ -/* ----------------------------------------------------------------------------- + /* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * @@ -36,6 +36,9 @@ #include "channelMode.h" +using namespace giada; + + geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch, const char *L) : Fl_Menu_Button(x, y, w, h, L), ch(ch) @@ -45,14 +48,14 @@ geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch, textcolor(G_COLOR_LIGHT_2); color(G_COLOR_GREY_2); - add("Loop . basic", 0, cb_changeMode, (void *)LOOP_BASIC); - add("Loop . once", 0, cb_changeMode, (void *)LOOP_ONCE); - add("Loop . once . bar", 0, cb_changeMode, (void *)LOOP_ONCE_BAR); - add("Loop . repeat", 0, cb_changeMode, (void *)LOOP_REPEAT); - add("Oneshot . basic", 0, cb_changeMode, (void *)SINGLE_BASIC); - add("Oneshot . press", 0, cb_changeMode, (void *)SINGLE_PRESS); - add("Oneshot . retrig", 0, cb_changeMode, (void *)SINGLE_RETRIG); - add("Oneshot . endless", 0, cb_changeMode, (void *)SINGLE_ENDLESS); + add("Loop . basic", 0, cb_changeMode, (void*) ChannelMode::LOOP_BASIC); + add("Loop . once", 0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE); + add("Loop . once . bar", 0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE_BAR); + add("Loop . repeat", 0, cb_changeMode, (void*) ChannelMode::LOOP_REPEAT); + add("Oneshot . basic", 0, cb_changeMode, (void*) ChannelMode::SINGLE_BASIC); + add("Oneshot . press", 0, cb_changeMode, (void*) ChannelMode::SINGLE_PRESS); + add("Oneshot . retrig", 0, cb_changeMode, (void*) ChannelMode::SINGLE_RETRIG); + add("Oneshot . endless", 0, cb_changeMode, (void*) ChannelMode::SINGLE_ENDLESS); } @@ -62,28 +65,28 @@ geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch, void geChannelMode::draw() { fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border switch (ch->mode) { - case LOOP_BASIC: + case ChannelMode::LOOP_BASIC: fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1); break; - case LOOP_ONCE: + case ChannelMode::LOOP_ONCE: fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1); break; - case LOOP_ONCE_BAR: + case ChannelMode::LOOP_ONCE_BAR: fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1); break; - case LOOP_REPEAT: + case ChannelMode::LOOP_REPEAT: fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1); break; - case SINGLE_BASIC: + case ChannelMode::SINGLE_BASIC: fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1); break; - case SINGLE_PRESS: + case ChannelMode::SINGLE_PRESS: fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1); break; - case SINGLE_RETRIG: + case ChannelMode::SINGLE_RETRIG: fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1); break; - case SINGLE_ENDLESS: + case ChannelMode::SINGLE_ENDLESS: fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1); break; } @@ -101,7 +104,7 @@ void geChannelMode::cb_changeMode(Fl_Widget *v, void *p) { ((geChannelMode*)v)-> void geChannelMode::__cb_changeMode(int mode) { - ch->mode = mode; + ch->mode = static_cast(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 diff --git a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp index 6d95b46..cc9f2d8 100644 --- a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp @@ -34,6 +34,7 @@ #include "channelStatus.h" +using namespace giada; using namespace giada::m; @@ -50,33 +51,37 @@ void geChannelStatus::draw() fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // reset border fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // reset background - if (ch != nullptr) { - if (ch->status & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) || - ch->recStatus & (REC_WAITING | REC_ENDING)) - { - fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); - } - else - if (ch->status == STATUS_PLAY) - fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); - else - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty - - - if (mixer::recording && ch->armed) - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress - else - if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE); // action record - - /* equation for the progress bar: - * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ - - int pos = ch->getPosition(); - if (pos == -1) - pos = 0; - else - pos = (pos * (w()-1)) / ((ch->getEnd() - ch->getBegin())); - fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1); + if (ch == nullptr) + return; + + if (ch->status == ChannelStatus::WAIT || + ch->status == ChannelStatus::ENDING || + ch->recStatus == ChannelStatus::WAIT || + ch->recStatus == ChannelStatus::ENDING) + { + fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); } + else + if (ch->status == ChannelStatus::PLAY) + fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); + else + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty + + + if (mixer::recording && ch->armed) + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress + else + if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE); // action record + + /* equation for the progress bar: + * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ + + int pos = ch->getPosition(); + if (pos == -1) + pos = 0; + else + pos = (pos * (w()-1)) / ((ch->getEnd() - ch->getBegin())); + fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1); + } diff --git a/src/gui/elems/mainWindow/keyboard/column.cpp b/src/gui/elems/mainWindow/keyboard/column.cpp index 0f39ec5..7bfdf49 100644 --- a/src/gui/elems/mainWindow/keyboard/column.cpp +++ b/src/gui/elems/mainWindow/keyboard/column.cpp @@ -112,7 +112,7 @@ int geColumn::handle(int e) for (string& path : paths) { gu_log("[geColumn::handle] loading %s...\n", path.c_str()); SampleChannel* c = static_cast(c::channel::addChannel( - m_index, G_CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1)); + m_index, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1)); result = c::channel::loadChannel(c, gu_stripFileUrl(path)); if (result != G_RES_OK) { deleteChannel(c->guiChannel); @@ -217,7 +217,7 @@ geChannel* geColumn::addChannel(Channel* ch, int size) /* All geChannels are added with y=0. That's not a problem, they will be repositioned later on during geColumn::resize(). */ - if (ch->type == G_CHANNEL_SAMPLE) + if (ch->type == ChannelType::SAMPLE) gch = new geSampleChannel(x(), 0, w(), size, static_cast(ch)); else gch = new geMidiChannel(x(), 0, w(), size, static_cast(ch)); @@ -254,17 +254,7 @@ void geColumn::deleteChannel(geChannel* gch) void geColumn::__cb_addChannel() { gu_log("[geColumn::__cb_addChannel] m_index = %d\n", m_index); - int type = openTypeMenu(); - if (type) - c::channel::addChannel(m_index, type, G_GUI_CHANNEL_H_1); -} - - -/* -------------------------------------------------------------------------- */ - -int geColumn::openTypeMenu() -{ Fl_Menu_Item rclick_menu[] = { {"Sample channel"}, {"MIDI channel"}, @@ -278,16 +268,16 @@ int geColumn::openTypeMenu() b->color(G_COLOR_GREY_2); const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return 0; + if (!m) return; if (strcmp(m->label(), "Sample channel") == 0) - return G_CHANNEL_SAMPLE; - if (strcmp(m->label(), "MIDI channel") == 0) - return G_CHANNEL_MIDI; - return 0; + c::channel::addChannel(m_index, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1); + else + c::channel::addChannel(m_index, ChannelType::MIDI, G_GUI_CHANNEL_H_1); } + /* -------------------------------------------------------------------------- */ diff --git a/src/gui/elems/mainWindow/keyboard/column.h b/src/gui/elems/mainWindow/keyboard/column.h index 47f31be..582b052 100644 --- a/src/gui/elems/mainWindow/keyboard/column.h +++ b/src/gui/elems/mainWindow/keyboard/column.h @@ -46,8 +46,6 @@ private: static void cb_addChannel (Fl_Widget* v, void* p); inline void __cb_addChannel(); - int openTypeMenu(); - geButton* m_addChannelBtn; geResizerBar* m_resizer; geKeyboard* m_parent; diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 9f913e7..fb38604 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -148,7 +148,7 @@ void menuCallback(Fl_Widget* w, void* v) geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel* ch) - : geChannel(X, Y, W, H, G_CHANNEL_MIDI, ch) + : geChannel(X, Y, W, H, ch) { begin(); @@ -217,7 +217,7 @@ void geMidiChannel::cb_button() using namespace giada; if (button->value()) - c::io::keyPress(static_cast(ch), Fl::event_ctrl(), Fl::event_shift()); + c::io::keyPress(static_cast(ch), Fl::event_ctrl(), Fl::event_shift(), 0); } diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index d477915..b97d0f8 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -61,6 +61,9 @@ extern gdMainWindow* G_MainWin; +using namespace giada; + + namespace { enum class Menu @@ -208,7 +211,7 @@ void menuCallback(Fl_Widget* w, void* v) geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch) - : geChannel(X, Y, W, H, G_CHANNEL_SAMPLE, ch) + : geChannel(X, Y, W, H, ch) { begin(); @@ -327,7 +330,9 @@ void geSampleChannel::cb_openMenu() {0} }; - if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) { + if (ch->status == ChannelStatus::EMPTY || + ch->status == ChannelStatus::MISSING) + { rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate(); rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate(); rclick_menu[(int) Menu::FREE_CHANNEL].deactivate(); @@ -341,7 +346,7 @@ void geSampleChannel::cb_openMenu() /* No 'clear start/stop actions' for those channels in loop mode: they cannot have start/stop actions. */ - if (static_cast(ch)->mode & LOOP_ANY) + if (static_cast(ch)->isAnyLoopMode()) rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate(); Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); @@ -363,7 +368,7 @@ void geSampleChannel::cb_openMenu() void geSampleChannel::cb_readActions() { using namespace giada::c::channel; - toggleReadingRecs(static_cast(ch)); + toggleReadingActions(static_cast(ch)); } @@ -412,11 +417,11 @@ void geSampleChannel::update() const SampleChannel* sch = static_cast(ch); switch (sch->status) { - case STATUS_EMPTY: + case ChannelStatus::EMPTY: mainButton->label("-- no sample --"); break; - case STATUS_MISSING: - case STATUS_WRONG: + case ChannelStatus::MISSING: + case ChannelStatus::WRONG: mainButton->label("* file not found! *"); break; default: @@ -436,7 +441,7 @@ void geSampleChannel::update() else hideActionButton(); - modeBox->value(sch->mode); + modeBox->value(static_cast(sch->mode)); modeBox->redraw(); vol->value(sch->volume); diff --git a/src/gui/elems/mainWindow/mainMenu.cpp b/src/gui/elems/mainWindow/mainMenu.cpp index ced6cb8..5a165dc 100644 --- a/src/gui/elems/mainWindow/mainMenu.cpp +++ b/src/gui/elems/mainWindow/mainMenu.cpp @@ -39,7 +39,7 @@ #include "../../elems/basics/boxtypes.h" #include "../../elems/basics/button.h" #include "../../dialogs/gd_mainWindow.h" -#include "../../dialogs/gd_about.h" +#include "../../dialogs/about.h" #include "../../dialogs/gd_config.h" #include "../../dialogs/gd_warnings.h" #include "../../dialogs/browser/browserLoad.h" @@ -177,17 +177,17 @@ void geMainMenu::__cb_edit() menu[1].deactivate(); - for (unsigned i=0; ihasActions) { + for (const Channel* ch : mixer::channels) + if (ch->hasActions) { menu[1].activate(); break; } - for (unsigned i=0; itype == 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); diff --git a/src/gui/elems/mainWindow/mainTimer.cpp b/src/gui/elems/mainWindow/mainTimer.cpp index 7a4bf3f..053fa89 100644 --- a/src/gui/elems/mainWindow/mainTimer.cpp +++ b/src/gui/elems/mainWindow/mainTimer.cpp @@ -72,12 +72,12 @@ geMainTimer::geMainTimer(int x, int y) divider->callback(cb_divider, (void*)this); quantizer->add("off", 0, cb_quantizer, (void*)this); - quantizer->add("1b", 0, cb_quantizer, (void*)this); - quantizer->add("2b", 0, cb_quantizer, (void*)this); - quantizer->add("3b", 0, cb_quantizer, (void*)this); - quantizer->add("4b", 0, cb_quantizer, (void*)this); - quantizer->add("6b", 0, cb_quantizer, (void*)this); - quantizer->add("8b", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/1", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/2", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/3", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/4", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/6", 0, cb_quantizer, (void*)this); + quantizer->add("1\\/8", 0, cb_quantizer, (void*)this); quantizer->value(0); // "off" by default } @@ -85,17 +85,17 @@ geMainTimer::geMainTimer(int x, int y) /* -------------------------------------------------------------------------- */ -void geMainTimer::cb_bpm (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_bpm(); } -void geMainTimer::cb_meter (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_meter(); } -void geMainTimer::cb_quantizer (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_quantizer(); } -void geMainTimer::cb_multiplier(Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_multiplier(); } -void geMainTimer::cb_divider (Fl_Widget *v, void *p) { ((geMainTimer*)p)->__cb_divider(); } +void geMainTimer::cb_bpm (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_bpm(); } +void geMainTimer::cb_meter (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_meter(); } +void geMainTimer::cb_quantizer (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_quantizer(); } +void geMainTimer::cb_multiplier(Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_multiplier(); } +void geMainTimer::cb_divider (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_divider(); } /* -------------------------------------------------------------------------- */ -void geMainTimer::__cb_bpm() +void geMainTimer::cb_bpm() { gu_openSubWindow(G_MainWin, new gdBpmInput(bpm->label()), WID_BPM); } @@ -104,7 +104,7 @@ void geMainTimer::__cb_bpm() /* -------------------------------------------------------------------------- */ -void geMainTimer::__cb_meter() +void geMainTimer::cb_meter() { gu_openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS); } @@ -113,7 +113,7 @@ void geMainTimer::__cb_meter() /* -------------------------------------------------------------------------- */ -void geMainTimer::__cb_quantizer() +void geMainTimer::cb_quantizer() { glue_quantize(quantizer->value()); } @@ -122,7 +122,7 @@ void geMainTimer::__cb_quantizer() /* -------------------------------------------------------------------------- */ -void geMainTimer::__cb_multiplier() +void geMainTimer::cb_multiplier() { glue_beatsMultiply(); } @@ -131,7 +131,7 @@ void geMainTimer::__cb_multiplier() /* -------------------------------------------------------------------------- */ -void geMainTimer::__cb_divider() +void geMainTimer::cb_divider() { glue_beatsDivide(); } @@ -140,7 +140,7 @@ void geMainTimer::__cb_divider() /* -------------------------------------------------------------------------- */ -void geMainTimer::setBpm(const char *v) +void geMainTimer::setBpm(const char* v) { bpm->copy_label(v); } @@ -175,6 +175,15 @@ void geMainTimer::setLock(bool v) /* -------------------------------------------------------------------------- */ +void geMainTimer::setQuantizer(int q) +{ + quantizer->value(q); +} + + +/* -------------------------------------------------------------------------- */ + + void geMainTimer::setMeter(int beats, int bars) { string tmp = gu_iToString(beats) + "/" + gu_iToString(bars); diff --git a/src/gui/elems/mainWindow/mainTimer.h b/src/gui/elems/mainWindow/mainTimer.h index c3fe661..917c054 100644 --- a/src/gui/elems/mainWindow/mainTimer.h +++ b/src/gui/elems/mainWindow/mainTimer.h @@ -40,31 +40,31 @@ class geMainTimer : public Fl_Group { private: - geButton *bpm; - geButton *meter; - geChoice *quantizer; - geButton *multiplier; - geButton *divider; - - static void cb_bpm (Fl_Widget *v, void *p); - static void cb_meter (Fl_Widget *v, void *p); - static void cb_quantizer (Fl_Widget *v, void *p); - static void cb_multiplier(Fl_Widget *v, void *p); - static void cb_divider (Fl_Widget *v, void *p); - - inline void __cb_bpm(); - inline void __cb_meter(); - inline void __cb_quantizer(); - inline void __cb_multiplier(); - inline void __cb_divider(); + geButton* bpm; + geButton* meter; + geChoice* quantizer; + geButton* multiplier; + geButton* divider; + + static void cb_bpm (Fl_Widget* v, void* p); + static void cb_meter (Fl_Widget* v, void* p); + static void cb_quantizer (Fl_Widget* v, void* p); + static void cb_multiplier(Fl_Widget* v, void* p); + static void cb_divider (Fl_Widget* v, void* p); + inline void cb_bpm(); + inline void cb_meter(); + inline void cb_quantizer(); + inline void cb_multiplier(); + inline void cb_divider(); public: geMainTimer(int x, int y); - void setBpm(const char *v); + void setBpm(const 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. */ diff --git a/src/gui/elems/plugin/pluginElement.cpp b/src/gui/elems/plugin/pluginElement.cpp new file mode 100644 index 0000000..c2ad697 --- /dev/null +++ b/src/gui/elems/plugin/pluginElement.cpp @@ -0,0 +1,211 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#include +#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; igetNumPrograms(); i++) + program->add(gu_removeFltkChars(m_plugin->getProgramName(i)).c_str()); + + if (program->size() == 0) { + program->add("-- no programs --\0"); + program->deactivate(); + } + else + program->value(m_plugin->getCurrentProgram()); + + bypass->callback(cb_setBypass, (void*)this); + bypass->type(FL_TOGGLE_BUTTON); + bypass->value(m_plugin->isBypassed() ? 0 : 1); + + shiftUp->callback(cb_shiftUp, (void*)this); + shiftDown->callback(cb_shiftDown, (void*)this); + remove->callback(cb_removePlugin, (void*)this); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_removePlugin (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_removePlugin(); } +void gePluginElement::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_openPluginWindow(); } +void gePluginElement::cb_setBypass (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setBypass(); } +void gePluginElement::cb_shiftUp (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftUp(); } +void gePluginElement::cb_shiftDown (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftDown(); } +void gePluginElement::cb_setProgram (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setProgram(); } + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_shiftUp() +{ + /*nothing to do if there's only one plugin */ + + if (m::pluginHost::countPlugins(m_parentWin->stackType, m_parentWin->ch) == 1) + return; + + int pluginIndex = m::pluginHost::getPluginIndex(m_plugin->getId(), + m_parentWin->stackType, m_parentWin->ch); + + if (pluginIndex == 0) // first of the stack, do nothing + return; + + c::plugin::swapPlugins(m_parentWin->ch, pluginIndex, pluginIndex-1, m_parentWin->stackType); + m_parentWin->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_shiftDown() +{ + /*nothing to do if there's only one plugin */ + + if (m::pluginHost::countPlugins(m_parentWin->stackType, m_parentWin->ch) == 1) + return; + + unsigned pluginIndex = m::pluginHost::getPluginIndex(m_plugin->getId(), m_parentWin->stackType, m_parentWin->ch); + unsigned stackSize = (m::pluginHost::getStack(m_parentWin->stackType, m_parentWin->ch))->size(); + + if (pluginIndex == stackSize-1) // last one in the stack, do nothing + return; + + c::plugin::swapPlugins(m_parentWin->ch, pluginIndex, pluginIndex+1, m_parentWin->stackType); + m_parentWin->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_removePlugin() +{ + /* Any subwindow linked to the plugin must be destroyed first. The + pluginWindow has id = id_plugin + 1, because id=0 is reserved for the parent + window 'add plugin' (i.e. this).*/ + + m_parentWin->delSubWindow(m_plugin->getId() + 1); + c::plugin::freePlugin(m_parentWin->ch, m_plugin->getId(), m_parentWin->stackType); + m_parentWin->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_openPluginWindow() +{ + /* The new pluginWindow has id = id_plugin + 1, because id=0 is reserved for + the parent window 'add plugin' (i.e. this). */ + + int pwid = m_plugin->getId() + 1; + + gdWindow* w; + if (m_plugin->hasEditor()) { + if (m_plugin->isEditorOpen()) { + gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has editor but it's already visible\n"); + m_parentWin->getChild(pwid)->show(); // Raise it to top + return; + } + gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has editor, window id=%d\n", pwid); + w = new gdPluginWindowGUI(m_plugin); + } + else { + w = new gdPluginWindow(m_plugin); + gu_log("[gePluginElement::cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid); + } + + if (m_parentWin->hasWindow(pwid)) + m_parentWin->delSubWindow(pwid); + w->setId(pwid); + m_parentWin->addSubWindow(w); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_setBypass() +{ + m_plugin->toggleBypass(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginElement::cb_setProgram() +{ + c::plugin::setProgram(m_plugin, program->value()); +} + + +#endif // #ifdef WITH_VST diff --git a/src/gui/elems/plugin/pluginElement.h b/src/gui/elems/plugin/pluginElement.h new file mode 100644 index 0000000..317cd87 --- /dev/null +++ b/src/gui/elems/plugin/pluginElement.h @@ -0,0 +1,78 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#ifndef GE_PLUGIN_ELEMENT_H +#define GE_PLUGIN_ELEMENT_H + + +#include + + +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 diff --git a/src/gui/elems/sampleEditor/waveTools.cpp b/src/gui/elems/sampleEditor/waveTools.cpp index 84a2824..7fdc453 100644 --- a/src/gui/elems/sampleEditor/waveTools.cpp +++ b/src/gui/elems/sampleEditor/waveTools.cpp @@ -25,18 +25,14 @@ * -------------------------------------------------------------------------- */ -#include "../../../core/const.h" -#ifdef G_OS_MAC // our Clang still doesn't know about cstdint (c++11 stuff) - #include -#else - #include -#endif +#include #include #include #include "../../../core/sampleChannel.h" #include "../../../core/waveFx.h" #include "../../../glue/sampleEditor.h" #include "../basics/boxtypes.h" +#include "../../../core/const.h" #include "waveform.h" #include "waveTools.h" @@ -221,7 +217,7 @@ void geWaveTools::openMenu() {0} }; - if (ch->status == STATUS_PLAY) { + if (ch->status == ChannelStatus::PLAY) { menu[(int)Menu::CUT].deactivate(); menu[(int)Menu::TRIM].deactivate(); } diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp index ec4de69..39a856e 100644 --- a/src/utils/gui.cpp +++ b/src/utils/gui.cpp @@ -123,6 +123,7 @@ void gu_updateControls() G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars()); G_MainWin->mainTimer->setBpm(clock::getBpm()); + G_MainWin->mainTimer->setQuantizer(clock::getQuantize()); G_MainWin->mainTransport->updatePlay(clock::isRunning()); G_MainWin->mainTransport->updateMetronome(mixer::metronome); diff --git a/src/utils/deps.cpp b/src/utils/ver.cpp similarity index 79% rename from src/utils/deps.cpp rename to src/utils/ver.cpp index fc58378..c22089f 100644 --- a/src/utils/deps.cpp +++ b/src/utils/ver.cpp @@ -33,7 +33,7 @@ #endif #include #include "../deps/rtaudio-mod/RtAudio.h" -#include "deps.h" +#include "ver.h" using std::string; @@ -41,7 +41,7 @@ using std::string; namespace giada { namespace u { -namespace deps +namespace ver { string getLibsndfileVersion() { @@ -56,7 +56,11 @@ string getLibsndfileVersion() string getRtAudioVersion() { +#ifdef TESTS + return ""; +#else return RtAudio::getVersion(); +#endif } @@ -65,7 +69,29 @@ string getRtAudioVersion() 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 @@ -34,11 +34,13 @@ namespace giada { namespace u { -namespace deps +namespace ver { std::string getLibsndfileVersion(); std::string getRtAudioVersion(); std::string getRtMidiVersion(); -}}}; // giada::u::deps:: + +bool isLess(int a1, int b1, int c1, int a2, int b2, int c2); +}}}; // giada::u::ver:: #endif \ No newline at end of file diff --git a/tests/audioBuffer.cpp b/tests/audioBuffer.cpp index 254941b..daf869c 100644 --- a/tests/audioBuffer.cpp +++ b/tests/audioBuffer.cpp @@ -3,7 +3,7 @@ #include -TEST_CASE("Test AudioBuffer class") +TEST_CASE("AudioBuffer") { using namespace giada::m; @@ -19,7 +19,7 @@ TEST_CASE("Test AudioBuffer class") { SECTION("test mono") { - REQUIRE(buffer.alloc(BUFFER_SIZE, 1) == 1); + buffer.alloc(BUFFER_SIZE, 1); REQUIRE(buffer.countFrames() == BUFFER_SIZE); REQUIRE(buffer.countSamples() == BUFFER_SIZE); REQUIRE(buffer.countChannels() == 1); @@ -34,7 +34,7 @@ TEST_CASE("Test AudioBuffer class") SECTION("test odd channels count") { - REQUIRE(buffer.alloc(BUFFER_SIZE, 7) == 1); + buffer.alloc(BUFFER_SIZE, 7); REQUIRE(buffer.countFrames() == BUFFER_SIZE); REQUIRE(buffer.countSamples() == BUFFER_SIZE * 7); REQUIRE(buffer.countChannels() == 7); diff --git a/tests/conf.cpp b/tests/conf.cpp index df9113c..a51f216 100644 --- a/tests/conf.cpp +++ b/tests/conf.cpp @@ -7,7 +7,7 @@ using std::string; using namespace giada::m; -TEST_CASE("Test Conf class") +TEST_CASE("conf") { conf::init(); diff --git a/tests/main.cpp b/tests/main.cpp index 37c527d..dc20323 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,3 +1,9 @@ #define CATCH_CONFIG_MAIN #define CATCH_CONFIG_FAST_COMPILE #include + +/* 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 diff --git a/tests/midiMapConf.cpp b/tests/midiMapConf.cpp index 5903fca..f3f2394 100644 --- a/tests/midiMapConf.cpp +++ b/tests/midiMapConf.cpp @@ -7,7 +7,7 @@ using std::string; using namespace giada::m; -TEST_CASE("Test MidiMapConf") +TEST_CASE("midiMapConf") { SECTION("test default values") { diff --git a/tests/patch.cpp b/tests/patch.cpp index 8e302bd..3c52f4e 100644 --- a/tests/patch.cpp +++ b/tests/patch.cpp @@ -1,21 +1,23 @@ #include "../src/core/patch.h" #include "../src/core/const.h" +#include "../src/core/types.h" #include using std::string; using std::vector; +using namespace giada; using namespace giada::m; -TEST_CASE("Test Patch class") +TEST_CASE("patch") { string filename = "./test-patch.json"; SECTION("test write") { + patch::action_t action0; patch::action_t action1; - patch::action_t action2; patch::channel_t channel1; patch::column_t column; #ifdef WITH_VST @@ -24,16 +26,16 @@ TEST_CASE("Test Patch class") patch::plugin_t plugin3; #endif - action1.type = 0; - action1.frame = 50000; - action1.fValue = 0.3f; - action1.iValue = 1000; - action2.type = 2; - action2.frame = 589; - action2.fValue = 1.0f; - action2.iValue = 130; + action0.type = 0; + action0.frame = 50000; + action0.fValue = 0.3f; + action0.iValue = 1000; + action1.type = 2; + action1.frame = 589; + action1.fValue = 1.0f; + action1.iValue = 130; + channel1.actions.push_back(action0); channel1.actions.push_back(action1); - channel1.actions.push_back(action2); #ifdef WITH_VST plugin1.path = "/path/to/plugin1"; @@ -55,12 +57,11 @@ TEST_CASE("Test Patch class") channel1.plugins.push_back(plugin2); #endif - channel1.type = G_CHANNEL_SAMPLE; + channel1.type = static_cast(ChannelType::SAMPLE); channel1.index = 666; channel1.size = G_GUI_CHANNEL_H_1; channel1.column = 0; channel1.mute = 0; - channel1.mute_s = 0; channel1.solo = 0; channel1.volume = 1.0f; channel1.pan = 0.5f; @@ -144,12 +145,11 @@ TEST_CASE("Test Patch class") REQUIRE(column0.width == 500); patch::channel_t channel0 = patch::channels.at(0); - REQUIRE(channel0.type == G_CHANNEL_SAMPLE); + REQUIRE(channel0.type == static_cast(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)); diff --git a/tests/recorder.cpp b/tests/recorder.cpp index 6d29d85..c42e6b1 100644 --- a/tests/recorder.cpp +++ b/tests/recorder.cpp @@ -7,7 +7,7 @@ using std::string; using namespace giada::m; -TEST_CASE("Test Recorder") +TEST_CASE("recorder") { /* Each SECTION the TEST_CASE is executed from the start. The following code is exectuted before each SECTION. */ diff --git a/tests/sampleChannel.cpp b/tests/sampleChannel.cpp new file mode 100644 index 0000000..fe7eee3 --- /dev/null +++ b/tests/sampleChannel.cpp @@ -0,0 +1,129 @@ +#include "../src/core/sampleChannel.h" +#include "../src/core/wave.h" +#include "../src/core/waveManager.h" +#include + + +using namespace giada; +using namespace giada::m; + + +TEST_CASE("sampleChannel") +{ + const int BUFFER_SIZE = 1024; + + std::vector modes = { ChannelMode::LOOP_BASIC, + ChannelMode::LOOP_ONCE, ChannelMode::LOOP_REPEAT, + ChannelMode::LOOP_ONCE_BAR, ChannelMode::SINGLE_BASIC, + ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG, + ChannelMode::SINGLE_ENDLESS }; + + SampleChannel ch(false, BUFFER_SIZE); + Wave* w; + waveManager::create("tests/resources/test.wav", &w); + ch.pushWave(w); + + SECTION("push wave") + { + REQUIRE(ch.status == ChannelStatus::OFF); + REQUIRE(ch.wave == w); + REQUIRE(ch.begin == 0); + REQUIRE(ch.end == w->getSize() - 1); + REQUIRE(ch.name == w->getBasename()); + } + + SECTION("begin/end") + { + ch.setBegin(-100); + + REQUIRE(ch.getBegin() == 0); + REQUIRE(ch.tracker == 0); + REQUIRE(ch.trackerPreview == 0); + + ch.setBegin(100000); + + REQUIRE(ch.getBegin() == w->getSize()); + REQUIRE(ch.tracker == w->getSize()); + REQUIRE(ch.trackerPreview == w->getSize()); + + ch.setBegin(16); + + REQUIRE(ch.getBegin() == 16); + REQUIRE(ch.tracker == 16); + REQUIRE(ch.trackerPreview == 16); + + ch.setEnd(0); + + REQUIRE(ch.getEnd() == 17); + + ch.setEnd(100000); + + REQUIRE(ch.getEnd() == w->getSize() - 1); + + ch.setEnd(32); + + REQUIRE(ch.getEnd() == 32); + + ch.setBegin(64); + + REQUIRE(ch.getBegin() == 31); + } + + SECTION("pitch") + { + ch.setPitch(40.0f); + + REQUIRE(ch.getPitch() == G_MAX_PITCH); + + ch.setPitch(-2.0f); + + REQUIRE(ch.getPitch() == G_MIN_PITCH); + + ch.setPitch(0.8f); + + REQUIRE(ch.getPitch() == 0.8f); + } + + SECTION("position") + { + REQUIRE(ch.getPosition() == -1); // Initially OFF + + ch.status = ChannelStatus::PLAY; + ch.tracker = 1000; + + REQUIRE(ch.getPosition() == 1000); + + ch.begin = 700; + + REQUIRE(ch.getPosition() == 300); + } + + SECTION("empty") + { + ch.empty(); + + REQUIRE(ch.status == ChannelStatus::EMPTY); + REQUIRE(ch.begin == 0); + REQUIRE(ch.end == 0); + REQUIRE(ch.tracker == 0); + REQUIRE(ch.volume == G_DEFAULT_VOL); + REQUIRE(ch.boost == G_DEFAULT_BOOST); + REQUIRE(ch.hasActions == false); + REQUIRE(ch.wave == nullptr); + } + + SECTION("can record audio") + { + REQUIRE(ch.canInputRec() == false); // Can't record if not armed + + ch.armed = true; + + REQUIRE(ch.canInputRec() == false); // Can't record with Wave in it + + ch.empty(); + + REQUIRE(ch.canInputRec() == true); + } + + /* TODO - fillBuffer, isAnyLoopMode, isAnySingleMode, isOnLastFrame */ +} diff --git a/tests/sampleChannelProc.cpp b/tests/sampleChannelProc.cpp new file mode 100644 index 0000000..82f2e07 --- /dev/null +++ b/tests/sampleChannelProc.cpp @@ -0,0 +1,250 @@ +#include "../src/core/sampleChannel.h" +#include "../src/core/sampleChannelProc.h" +#include "../src/core/wave.h" +#include "../src/core/waveManager.h" +#include + + +using namespace giada; +using namespace giada::m; + + +TEST_CASE("sampleChannelProc") +{ + const int BUFFER_SIZE = 1024; + + std::vector 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 statuses = { ChannelStatus::ENDING, + ChannelStatus::WAIT, ChannelStatus::PLAY, ChannelStatus::OFF, + ChannelStatus::EMPTY, ChannelStatus::MISSING, ChannelStatus::WRONG }; + + for (ChannelMode mode : modes) { + for (ChannelStatus status : statuses) { + ch.mode = mode; + ch.status = status; + ch.tracker = 16; // simulate processing + sampleChannelProc::kill(&ch, 0); + + if (ch.status == ChannelStatus::WAIT || + ch.status == ChannelStatus::PLAY || + ch.status == ChannelStatus::ENDING) { + REQUIRE(ch.status == ChannelStatus::OFF); + REQUIRE(ch.tracker == 0); + } + } + } + } + + SECTION("quantized start") + { + for (ChannelMode mode : modes) { + ch.mode = mode; + ch.status = ChannelStatus::OFF; + sampleChannelProc::start(&ch, 0, /*doQuantize=*/true, /*velocity=*/0); + + if (ch.isAnyLoopMode()) + REQUIRE(ch.status == ChannelStatus::WAIT); + else { + REQUIRE(ch.status == ChannelStatus::OFF); + REQUIRE(ch.qWait == true); + } + } + } + } + + SECTION("stop input recordings") + { + /* Start all sample channels in any loop mode that were armed. */ + for (ChannelMode mode : modes) { + ch.mode = mode; + ch.status = ChannelStatus::OFF; + ch.armed = true; + ch.tracker = 16; + + sampleChannelProc::stopInputRec(&ch, /*globalFrame=*/666); + + if (ch.isAnyLoopMode()) { + REQUIRE(ch.status == ChannelStatus::PLAY); + REQUIRE(ch.tracker == 666); + } + else { + REQUIRE(ch.status == ChannelStatus::OFF); + REQUIRE(ch.tracker == 16); + } + } + } + + SECTION("rewind by sequencer") + { + ch.pushWave(w); + + /* Test loop modes. */ + + for (ChannelMode mode : modes) { + ch.mode = mode; + ch.status = ChannelStatus::PLAY; + ch.tracker = 16; // simulate processing + + sampleChannelProc::rewindBySeq(&ch); + + if (ch.isAnyLoopMode()) { + REQUIRE(ch.status == ChannelStatus::PLAY); + REQUIRE(ch.tracker == 0); + } + else { + REQUIRE(ch.status == ChannelStatus::PLAY); + REQUIRE(ch.tracker == 16); + } + } + + /* Test single modes that are reading actions. */ + + for (ChannelMode mode : modes) { + ch.mode = mode; + ch.status = ChannelStatus::PLAY; + ch.tracker = 16; // simulate processing + ch.recStatus = ChannelStatus::PLAY; + + sampleChannelProc::rewindBySeq(&ch); + + if (ch.isAnySingleMode()) { + REQUIRE(ch.status == ChannelStatus::PLAY); + REQUIRE(ch.tracker == 0); + } + } + } +} diff --git a/tests/sampleChannelRec.cpp b/tests/sampleChannelRec.cpp new file mode 100644 index 0000000..c115d07 --- /dev/null +++ b/tests/sampleChannelRec.cpp @@ -0,0 +1,85 @@ +#include "../src/core/sampleChannel.h" +#include "../src/core/sampleChannelRec.h" +#include + + +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); + + } +} diff --git a/tests/utils.cpp b/tests/utils.cpp index c17b51d..79aeb4d 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -1,10 +1,11 @@ #include "../src/utils/fs.h" #include "../src/utils/string.h" #include "../src/utils/math.h" +#include "../src/utils/ver.h" #include -TEST_CASE("Test filesystem utils") +TEST_CASE("u::fs") { REQUIRE(gu_fileExists("README.md") == true); REQUIRE(gu_fileExists("ghost_file") == false); @@ -32,7 +33,7 @@ TEST_CASE("Test filesystem utils") } -TEST_CASE("Test string utils") +TEST_CASE("u::string") { using std::vector; @@ -52,11 +53,22 @@ TEST_CASE("Test string utils") } -TEST_CASE("Test math utils") +TEST_CASE("::math") { using namespace giada::u::math; REQUIRE(map( 0.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 0.0f); REQUIRE(map(30.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 1.0f); REQUIRE(map(15.0f, 0.0f, 30.0f, 0.0f, 1.0f) == Approx(0.5f)); +} + + +TEST_CASE("u::ver") +{ + using namespace giada::u::ver; + + REQUIRE(isLess(6, 6, 6, 0, 15, 0) == false); + REQUIRE(isLess(0, 15, 0, 6, 6, 6) == true); + REQUIRE(isLess(6, 6, 6, 6, 6, 6) == false); + REQUIRE(isLess(6, 6, 5, 6, 6, 6) == true); } \ No newline at end of file diff --git a/tests/wave.cpp b/tests/wave.cpp index 0097720..095a4c4 100644 --- a/tests/wave.cpp +++ b/tests/wave.cpp @@ -6,7 +6,7 @@ using std::string; -TEST_CASE("Test Wave class") +TEST_CASE("Wave") { static const int SAMPLE_RATE = 44100; static const int BUFFER_SIZE = 4096; @@ -20,8 +20,7 @@ TEST_CASE("Test Wave class") SECTION("test allocation") { Wave wave; - - REQUIRE(wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav") == true); + wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"); SECTION("test basename") { diff --git a/tests/waveFx.cpp b/tests/waveFx.cpp index e12c8cb..7473878 100644 --- a/tests/waveFx.cpp +++ b/tests/waveFx.cpp @@ -9,7 +9,7 @@ using std::string; using namespace giada::m; -TEST_CASE("Test waveFx") +TEST_CASE("waveFx") { static const int SAMPLE_RATE = 44100; static const int BUFFER_SIZE = 4000; diff --git a/tests/waveManager.cpp b/tests/waveManager.cpp index d32be1b..f19905c 100644 --- a/tests/waveManager.cpp +++ b/tests/waveManager.cpp @@ -14,7 +14,7 @@ using namespace giada::m; #define G_CHANNELS 2 -TEST_CASE("Test waveManager") +TEST_CASE("waveManager") { /* Each SECTION the TEST_CASE is executed from the start. Any code between this comment and the first SECTION macro is exectuted before each SECTION. */ @@ -35,11 +35,10 @@ TEST_CASE("Test waveManager") SECTION("test recording") { - int res = waveManager::createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE, + waveManager::createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE, "test.wav", &w); std::unique_ptr wave(w); - REQUIRE(res == G_RES_OK); REQUIRE(wave->getRate() == G_SAMPLE_RATE); REQUIRE(wave->getSize() == G_BUFFER_SIZE); REQUIRE(wave->getChannels() == G_CHANNELS); @@ -49,15 +48,12 @@ TEST_CASE("Test waveManager") SECTION("test resampling") { - int res = waveManager::create("tests/resources/test.wav", &w); + waveManager::create("tests/resources/test.wav", &w); std::unique_ptr wave(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); -- 2.30.2