--- /dev/null
+---
+BasedOnStyle: Microsoft
+AccessModifierOffset: -4
+AlignAfterOpenBracket: 'false'
+AlignConsecutiveAssignments: 'true'
+AlignConsecutiveDeclarations: 'true'
+AllowShortFunctionsOnASingleLine: All
+BreakBeforeBraces: Allman
+BreakConstructorInitializers: BeforeComma
+ColumnLimit: 0
+ConstructorInitializerIndentWidth: '0'
+IndentWrappedFunctionNames: 'false'
+Language: Cpp
+NamespaceIndentation: None
+PointerAlignment: Left
+UseTab: ForIndentation
\ No newline at end of file
list(APPEND SOURCES
src/main.cpp
+ src/core/worker.cpp
+ src/core/eventDispatcher.cpp
src/core/midiDispatcher.cpp
src/core/midiMapConf.cpp
src/core/midiEvent.cpp
src/core/kernelAudio.cpp
src/core/mixerHandler.cpp
src/core/sequencer.cpp
+ src/core/metronome.cpp
src/core/init.cpp
src/core/wave.cpp
src/core/waveFx.cpp
src/core/waveManager.cpp
src/core/recManager.cpp
src/core/midiLearnParam.cpp
+ src/core/resampler.cpp
src/core/plugins/pluginHost.cpp
src/core/plugins/pluginManager.cpp
src/core/plugins/plugin.cpp
src/core/plugins/pluginState.cpp
- src/core/channels/state.cpp
src/core/channels/sampleActionRecorder.cpp
src/core/channels/midiActionRecorder.cpp
src/core/channels/waveReader.cpp
src/core/channels/midiController.cpp
- src/core/channels/sampleController.cpp
+ src/core/channels/sampleReactor.cpp
+ src/core/channels/sampleAdvancer.cpp
src/core/channels/samplePlayer.cpp
src/core/channels/audioReceiver.cpp
src/core/channels/midiLighter.cpp
src/glue/recorder.cpp
src/glue/sampleEditor.cpp
src/glue/actionEditor.cpp
+ src/glue/config.cpp
src/gui/dialogs/window.cpp
src/gui/dispatcher.cpp
src/gui/updater.cpp
src/gui/model.cpp
+ src/gui/drawing.cpp
src/gui/dialogs/keyGrabber.cpp
src/gui/dialogs/about.cpp
src/gui/dialogs/mainWindow.cpp
src/gui/dialogs/bpmInput.cpp
src/gui/dialogs/channelNameInput.cpp
src/gui/dialogs/config.cpp
- src/gui/dialogs/devInfo.cpp
src/gui/dialogs/pluginList.cpp
src/gui/dialogs/pluginWindow.cpp
src/gui/dialogs/sampleEditor.cpp
src/gui/elems/mainWindow/mainMenu.cpp
src/gui/elems/mainWindow/mainTimer.cpp
src/gui/elems/mainWindow/mainTransport.cpp
- src/gui/elems/mainWindow/beatMeter.cpp
+ src/gui/elems/mainWindow/sequencer.cpp
src/gui/elems/mainWindow/keyboard/channelMode.cpp
src/gui/elems/mainWindow/keyboard/channelButton.cpp
src/gui/elems/mainWindow/keyboard/channelStatus.cpp
src/gui/elems/basics/slider.cpp
src/gui/elems/basics/progress.cpp
src/gui/elems/basics/check.cpp
- src/gui/elems/basics/radio.cpp
src/utils/log.cpp
src/utils/time.cpp
src/utils/math.cpp
option(WITH_VST3 "Enable VST3 support." OFF)
option(WITH_TESTS "Include the test suite." OFF)
+if(DEFINED OS_LINUX)
+ option(WITH_ALSA "Enable ALSA support (Linux only)." ON)
+ option(WITH_PULSE "Enable PulseAudio support (Linux only)." ON)
+ option(WITH_JACK "Enable JACK support (Linux only)." ON)
+endif()
+
if(WITH_TESTS)
list(APPEND PREPROCESSOR_DEFS
WITH_TESTS
find_package(Threads REQUIRED)
list(APPEND LIBRARIES ${Threads_LIBRARY})
+# pkg-config/pkgconf, required to find some external dependencies on some
+# platforms
+find_package(PkgConfig)
+
# RtMidi
find_package(RtMidi CONFIG)
if (RtMidi_FOUND)
list(APPEND LIBRARIES RtMidi::rtmidi)
-else()
+ message("RtMidi library found in " ${RtMidi_DIR})
+elseif (PkgConfig_FOUND)
+ pkg_check_modules(RtMidi IMPORTED_TARGET rtmidi)
+ if (RtMidi_FOUND)
+ list(APPEND LIBRARIES PkgConfig::RtMidi)
+ message("RtMidi library found")
+ endif()
+endif()
+
+if (NOT RtMidi_FOUND)
# Fallback to find_library mode (in case rtmidi is too old).
find_library(LIBRARY_RTMIDI NAMES rtmidi)
list(APPEND LIBRARIES ${LIBRARY_RTMIDI})
find_path(LIBRARY_RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES rtmidi)
list(APPEND INCLUDE_DIRS ${LIBRARY_RTMIDI_INCLUDE_DIR})
+ message("RtMidi library found in " ${RtMidi_DIR})
endif()
-message("RtMidi library found in " ${RtMidi_DIR})
-
# FLTK
set(FLTK_SKIP_FLUID TRUE) # Don't search for FLTK's fluid
if (SndFile_FOUND)
list(APPEND LIBRARIES SndFile::sndfile)
message("Libsndfile library found in " ${SndFile_DIR})
-else()
+elseif(PkgConfig_FOUND)
+ pkg_check_modules(SndFile IMPORTED_TARGET sndfile)
+ if (SndFile_FOUND)
+ list(APPEND LIBRARIES PkgConfig::SndFile)
+ message("Libsndfile library found")
+ endif()
+endif()
+
+if (NOT SndFile_FOUND)
# Fallback to find_library mode (in case libsndfile is too old).
find_library(LIBRARY_SNDFILE NAMES sndfile libsndfile libsndfile-1)
endif()
# Libsamplerate
-# TODO - new libsamplerate now provides CMake targets. Update it!
-find_library(LIBRARY_SAMPLERATE
- NAMES samplerate libsamplerate libsamplerate-0 liblibsamplerate-0
- PATHS ${_VCPKG_ROOT_DIR}/installed/${VCPKG_TARGET_TRIPLET})
-list(APPEND LIBRARIES ${LIBRARY_SAMPLERATE})
-message("Libsamplerate library found in " ${LIBRARY_SAMPLERATE})
+find_package(SampleRate CONFIG)
+if (SampleRate_FOUND)
+ list(APPEND LIBRARIES SampleRate::samplerate)
+ message("Libsndfile library found in " ${SampleRate_DIR})
+else()
+ # Fallback to find_library mode (in case Libsamplerate is too old).
+ find_library(LIBRARY_SAMPLERATE
+ NAMES samplerate libsamplerate libsamplerate-0 liblibsamplerate-0
+ PATHS ${_VCPKG_ROOT_DIR}/installed/${VCPKG_TARGET_TRIPLET}
+ REQUIRED)
+ list(APPEND LIBRARIES ${LIBRARY_SAMPLERATE})
+ message("Libsamplerate library found in " ${LIBRARY_SAMPLERATE})
+endif()
# Catch (if tests enabled)
find_library(LIBRARY_PULSE NAMES pulse REQUIRED)
find_library(LIBRARY_PULSE_SIMPLE NAMES pulse-simple REQUIRED)
find_library(LIBRARY_FONTCONFIG NAMES fontconfig REQUIRED)
- find_library(LIBRARY_JACK NAMES jack REQUIRED)
+ pkg_check_modules(JACK REQUIRED jack)
list(APPEND LIBRARIES
${X11_LIBRARIES} ${X11_Xrender_LIB} ${X11_Xft_LIB} ${X11_Xfixes_LIB}
${X11_Xinerama_LIB} ${X11_Xcursor_LIB} ${X11_Xpm_LIB} ${LIBRARY_PULSE}
- ${LIBRARY_PULSE_SIMPLE} ${LIBRARY_FONTCONFIG} ${LIBRARY_JACK}
+ ${LIBRARY_PULSE_SIMPLE} ${LIBRARY_FONTCONFIG} ${JACK_LDFLAGS}
${CMAKE_DL_LIBS} ${ALSA_LIBRARIES} pthread stdc++fs)
- list(APPEND PREPROCESSOR_DEFS
- WITH_AUDIO_JACK
- __LINUX_ALSA__
- __LINUX_PULSE__
- __UNIX_JACK__)
+ if (WITH_ALSA)
+ list(APPEND PREPROCESSOR_DEFS __LINUX_ALSA__)
+ endif()
+ if (WITH_PULSE)
+ list(APPEND PREPROCESSOR_DEFS __LINUX_PULSE__)
+ endif()
+ if (WITH_JACK)
+ list(APPEND PREPROCESSOR_DEFS WITH_AUDIO_JACK __UNIX_JACK__)
+ endif()
elseif(DEFINED OS_WINDOWS)
${CMAKE_DL_LIBS} pthread)
list(APPEND PREPROCESSOR_DEFS
+ WITH_AUDIO_JACK
__LINUX_PULSE__
__UNIX_JACK__)
# Install rules
# ------------------------------------------------------------------------------
-install(TARGETS giada DESTINATION ${CMAKE_INSTALL_PREFIX})
+if(DEFINED OS_LINUX)
+ include(GNUInstallDirs)
+ install(TARGETS giada DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
+ install(FILES ${CMAKE_SOURCE_DIR}/extras/com.giadamusic.Giada.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
+ install(FILES ${CMAKE_SOURCE_DIR}/extras/com.giadamusic.Giada.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
+ install(FILES ${CMAKE_SOURCE_DIR}/extras/giada-logo.svg RENAME com.giadamusic.Giada.svg DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps)
+endif()
# ------------------------------------------------------------------------------
# Extra
# ------------------------------------------------------------------------------
-# Enable static linking of the MSVC runtime library on Windows
-# TODO - move this into the 'if windows' conditional (needs smarter list first)
+# TODO - move these into the 'if [OS]' conditionals (needs smarter list first)
if(DEFINED OS_WINDOWS)
+
+ # Enable static linking of the MSVC runtime library on Windows
+
set_target_properties(giada PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
-endif()
+
+elseif(DEFINED OS_MACOS)
+
+ # Enable hardened runtime:
+ # https://developer.apple.com/documentation/security/hardened_runtime
+
+ set_target_properties(giada PROPERTIES
+ XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES)
+
+endif()
\ No newline at end of file
--------------------------------------------------------------------------------
+0.18.1 --- 2021 . 07 . 25
+- New resampler architecture: allows for changing quality also for live rendering (#288)
+- Gracefully shutdown UI on close to random crashes on quit on Windows
+- Fix 'one shot channels with actions as loops' mode not working correctly
+- Fix wrong sequencer signals while starting/stopping action recs with JACK (#397)
+- Fix extra dot in unique audio file name generation
+- Fix sample overflow when looping a sample with pitch != 1.0
+- [CMake, Linux] Detect JACK with pkg-config
+- [CMake, Linux] Install Freedesktop files and icon
+- [CMake, Linux] Add configure switches for ALSA, JACK and PulseAudio
+- [macOS] Enable hardened runtime
+
+
+0.18.0 --- 2021 . 05 . 18
+- New 'free loop-length' audio recording mode (#63)
+- Many AudioBuffer improvements
+- Audio configuration panel refactoring
+- KernelAudio improvements and cleanups
+- Relaxed BPM handling when working with JACK
+- Install executable to FHS compliant location (#450)
+- [CI] Don't UPX binaries on macOS (#459)
+- Fix Overdub protection ON by default not working (#460)
+- Fix crash when moving up from a deleted folder (#455)
+
+
+0.17.2 --- 2021 . 03 . 29
+- New double-buffered audio engine
+- Improved audio sample rendering precision
+- Show tooltips when hovering over UI components
+- Add .clang-format file
+- Removed support for Autotools build system
+- Removed support for old raw patches
+- [CMake] Use find_package command for libsamplerate
+- Improved AudioBuffer move semantics
+- Send time + position information to plug-ins
+- Update JUCE library to version 6.0.7
+- Fix crash when saving project with plug-ins in invalid state
+
+
0.17.1 --- 2021 . 02 . 01
- Better CMake dependency management
- Add CMake install rules (#422)
- Switch to GitHub Actions for CI and release builds (#440)
-- Remove hardcored 'test' folder in test suite (#432)
+- Remove hardcoded 'test' folder in test suite (#432)
- Make sure macOS minimum target is set to 10.14 (#444)
- Fix crash when restarting after setting jack as an audio server (#409, #368)
- Fix crash when clicking "Cancel" button in Browser dialog (#430)
+++ /dev/null
-AUTOMAKE_OPTIONS = foreign
-
-# Define conditional variables -------------------------------------------------
-# WITH_VST, LINUX, WINDOWS and OSX are varibles defined via AM_CONDITIONAL
-# inside configure.ac.
-
-
-cppFlags = -I$(top_srcdir)/src
-cxxFlags = -std=c++17 -Wall
-ldAdd =
-ldFlags =
-sourcesExtra =
-sourcesMain = src/main.cpp
-sourcesCore = \
- src/core/const.h \
- src/core/queue.h \
- src/core/ringBuffer.h \
- src/core/types.h \
- src/core/range.h \
- src/core/action.h \
- src/core/midiDispatcher.h \
- src/core/midiDispatcher.cpp \
- src/core/midiMapConf.h \
- src/core/midiMapConf.cpp \
- src/core/midiEvent.h \
- src/core/midiEvent.cpp \
- src/core/audioBuffer.h \
- src/core/audioBuffer.cpp \
- src/core/quantizer.h \
- src/core/quantizer.cpp \
- src/core/conf.h \
- src/core/conf.cpp \
- src/core/kernelAudio.h \
- src/core/kernelAudio.cpp \
- src/core/plugins/pluginHost.h \
- src/core/plugins/pluginHost.cpp \
- src/core/plugins/pluginManager.h \
- src/core/plugins/pluginManager.cpp \
- src/core/plugins/plugin.h \
- src/core/plugins/plugin.cpp \
- src/core/plugins/pluginState.h \
- src/core/plugins/pluginState.cpp \
- src/core/mixerHandler.h \
- src/core/mixerHandler.cpp \
- src/core/sequencer.h \
- src/core/sequencer.cpp \
- src/core/init.h \
- src/core/init.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/midiLearnParam.cpp \
- src/core/patch.h \
- src/core/patch.cpp \
- src/core/recorderHandler.h \
- src/core/recorderHandler.cpp \
- src/core/recorder.h \
- src/core/recorder.cpp \
- src/core/mixer.h \
- src/core/mixer.cpp \
- src/core/clock.h \
- src/core/clock.cpp \
- src/core/waveManager.h \
- src/core/waveManager.cpp \
- src/core/recManager.h \
- src/core/recManager.cpp \
- src/core/channels/state.h \
- src/core/channels/state.cpp \
- src/core/channels/sampleActionRecorder.h \
- src/core/channels/sampleActionRecorder.cpp \
- src/core/channels/midiActionRecorder.h \
- src/core/channels/midiActionRecorder.cpp \
- src/core/channels/waveReader.h \
- src/core/channels/waveReader.cpp \
- src/core/channels/midiController.h \
- src/core/channels/midiController.cpp \
- src/core/channels/sampleController.h \
- src/core/channels/sampleController.cpp \
- src/core/channels/samplePlayer.h \
- src/core/channels/samplePlayer.cpp \
- src/core/channels/audioReceiver.h \
- src/core/channels/audioReceiver.cpp \
- src/core/channels/midiLighter.h \
- src/core/channels/midiLighter.cpp \
- src/core/channels/midiLearner.h \
- src/core/channels/midiLearner.cpp \
- src/core/channels/midiSender.h \
- src/core/channels/midiSender.cpp \
- src/core/channels/midiReceiver.h \
- src/core/channels/midiReceiver.cpp \
- src/core/channels/channel.h \
- src/core/channels/channel.cpp \
- src/core/channels/channelManager.h \
- src/core/channels/channelManager.cpp \
- src/core/model/model.h \
- src/core/model/model.cpp \
- src/core/model/storage.h \
- src/core/model/storage.cpp \
- src/core/idManager.h \
- src/core/idManager.cpp \
- src/glue/events.h \
- src/glue/events.cpp \
- src/glue/main.h \
- src/glue/main.cpp \
- src/glue/io.h \
- src/glue/io.cpp \
- src/glue/storage.h \
- src/glue/storage.cpp \
- src/glue/channel.h \
- src/glue/channel.cpp \
- src/glue/plugin.h \
- src/glue/plugin.cpp \
- src/glue/recorder.h \
- src/glue/recorder.cpp \
- src/glue/sampleEditor.h \
- src/glue/sampleEditor.cpp \
- src/glue/actionEditor.h \
- src/glue/actionEditor.cpp \
- src/gui/dialogs/window.h \
- src/gui/dialogs/window.cpp \
- src/gui/dispatcher.h \
- src/gui/dispatcher.cpp \
- src/gui/updater.h \
- src/gui/updater.cpp \
- src/gui/model.h \
- src/gui/model.cpp \
- src/gui/dialogs/keyGrabber.h \
- src/gui/dialogs/keyGrabber.cpp \
- src/gui/dialogs/about.h \
- src/gui/dialogs/about.cpp \
- src/gui/dialogs/mainWindow.h \
- src/gui/dialogs/mainWindow.cpp \
- src/gui/dialogs/beatsInput.h \
- src/gui/dialogs/beatsInput.cpp \
- src/gui/dialogs/warnings.h \
- src/gui/dialogs/warnings.cpp \
- src/gui/dialogs/bpmInput.h \
- src/gui/dialogs/bpmInput.cpp \
- src/gui/dialogs/channelNameInput.h \
- src/gui/dialogs/channelNameInput.cpp \
- src/gui/dialogs/config.h \
- src/gui/dialogs/config.cpp \
- src/gui/dialogs/devInfo.h \
- src/gui/dialogs/devInfo.cpp \
- src/gui/dialogs/pluginList.h \
- src/gui/dialogs/pluginList.cpp \
- src/gui/dialogs/pluginWindow.h \
- src/gui/dialogs/pluginWindow.cpp \
- src/gui/dialogs/sampleEditor.h \
- src/gui/dialogs/sampleEditor.cpp \
- src/gui/dialogs/pluginWindowGUI.h \
- src/gui/dialogs/pluginWindowGUI.cpp \
- src/gui/dialogs/pluginChooser.h \
- src/gui/dialogs/pluginChooser.cpp \
- src/gui/dialogs/actionEditor/baseActionEditor.h \
- src/gui/dialogs/actionEditor/baseActionEditor.cpp \
- src/gui/dialogs/actionEditor/sampleActionEditor.h \
- src/gui/dialogs/actionEditor/sampleActionEditor.cpp \
- src/gui/dialogs/actionEditor/midiActionEditor.h \
- src/gui/dialogs/actionEditor/midiActionEditor.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/midiIO/midiLearner.h \
- src/gui/elems/midiIO/midiLearner.cpp \
- src/gui/elems/midiIO/midiLearnerPack.h \
- src/gui/elems/midiIO/midiLearnerPack.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/baseAction.h \
- src/gui/elems/actionEditor/baseAction.cpp \
- src/gui/elems/actionEditor/envelopeEditor.h \
- src/gui/elems/actionEditor/envelopeEditor.cpp \
- src/gui/elems/actionEditor/velocityEditor.h \
- src/gui/elems/actionEditor/velocityEditor.cpp \
- src/gui/elems/actionEditor/envelopePoint.h \
- src/gui/elems/actionEditor/envelopePoint.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/pianoItem.h \
- src/gui/elems/actionEditor/pianoItem.cpp \
- src/gui/elems/actionEditor/sampleActionEditor.h \
- src/gui/elems/actionEditor/sampleActionEditor.cpp \
- src/gui/elems/actionEditor/sampleAction.h \
- src/gui/elems/actionEditor/sampleAction.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/pack.h \
- src/gui/elems/basics/pack.cpp \
- src/gui/elems/basics/group.h \
- src/gui/elems/basics/group.cpp \
- src/gui/elems/basics/scrollPack.h \
- src/gui/elems/basics/scrollPack.cpp \
- src/gui/elems/basics/boxtypes.h \
- src/gui/elems/basics/boxtypes.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/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/vector.h \
- src/utils/ver.h \
- src/utils/ver.cpp \
- src/utils/string.h \
- src/utils/string.cpp \
- src/deps/rtaudio/RtAudio.h \
- src/deps/rtaudio/RtAudio.cpp
-sourcesTests = \
- tests/main.cpp \
- tests/rcuList.cpp \
- tests/wave.cpp \
- tests/waveManager.cpp \
- tests/utils.cpp \
- tests/recorder.cpp \
- tests/waveFx.cpp \
- tests/audioBuffer.cpp
-if WITH_VST
-
-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 \
- src/deps/juce/modules/juce_data_structures/juce_data_structures.cpp \
- src/deps/juce/modules/juce_events/juce_events.cpp \
- src/deps/juce/modules/juce_graphics/juce_graphics.cpp \
- src/deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp \
- src/deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
-
-cppFlags += \
- -I$(top_srcdir)/src/deps/juce/modules \
- -I$(top_srcdir)/src/deps/vst \
- -I/usr/include \
- -I/usr/include/freetype2 \
- -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 \
- -DJUCE_STANDALONE_APPLICATION=1 \
- -DJUCE_PLUGINHOST_VST=1 \
- -DJUCE_PLUGINHOST_VST3=0 \
- -DJUCE_PLUGINHOST_AU=0 \
- -DJUCE_WEB_BROWSER=0
-
-endif
-
-if !WITH_SYSTEM_CATCH
-
-cppFlags += -I$(top_srcdir)/tests/catch2/single_include
-
-endif
-
-if WINDOWS
-
-sourcesExtra += \
- src/deps/rtaudio/include/asio.h \
- src/deps/rtaudio/include/asio.cpp \
- src/deps/rtaudio/include/asiolist.h \
- src/deps/rtaudio/include/asiolist.cpp \
- src/deps/rtaudio/include/asiodrivers.h \
- src/deps/rtaudio/include/asiodrivers.cpp \
- src/deps/rtaudio/include/iasiothiscallresolver.h \
- src/deps/rtaudio/include/iasiothiscallresolver.cpp \
- resource.rc
-
-cppFlags += \
- -I$(top_srcdir)/src/deps/rtaudio/include \
- -D__WINDOWS_ASIO__ \
- -D__WINDOWS_WASAPI__ \
- -D__WINDOWS_DS__
-
-ldAdd += -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 \
- -lrpcrt4 -luuid -lcomctl32 -lws2_32 -lsndfile -lsamplerate -lrtmidi \
- -lsetupapi -limm32 -lglu32 -lshell32 -lversion \
- -lopengl32 -loleaut32 -lshlwapi -lcomdlg32 -lflac -lvorbis -logg -lvorbisenc \
- -lole32 -lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid
-
-# Generate a GUI application (-mwindows), make the build static (-static).
-ldFlags += -mwindows -static
-
-endif
-
-if LINUX
-
-# Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio.
-cppFlags += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
-
-ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
- -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi \
- -lfreetype -lfontconfig -lXrender -lXfixes -lXcursor -lXinerama
-
-endif
-
-if FREEBSD
-
-# Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio.
-cppFlags += -D__LINUX_PULSE__ -D__UNIX_JACK__
-
-ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
- -lpthread -lpulse-simple -lpulse -lsamplerate -lrtmidi \
- -lfreetype
-
-endif
-
-if OSX
-
-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++
-
-ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -lm -lpthread \
- -lFLAC -logg -lvorbis -lvorbisenc -lopus
-
-ldFlags += -framework CoreAudio -framework Cocoa -framework Carbon \
- -framework CoreMIDI -framework CoreFoundation -framework Accelerate \
- -framework WebKit -framework QuartzCore -framework IOKit
-
-endif
-
-# make giada -------------------------------------------------------------------
-
-bin_PROGRAMS = giada
-
-giada_SOURCES = $(sourcesCore) $(sourcesExtra) $(sourcesMain)
-giada_CPPFLAGS = $(cppFlags)
-giada_CXXFLAGS = $(cxxFlags)
-giada_LDADD = $(ldAdd)
-giada_LDFLAGS = $(ldFlags)
-
-# Used only under MinGW to compile the resource.rc file (program icon)
-resource.o:
- windres src/ext/resource.rc -o resource.o
-
-# make check -------------------------------------------------------------------
-
-TESTS = giada_tests
-
-check_PROGRAMS = giada_tests
-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)
-
-# make rename ------------------------------------------------------------------
-
-if LINUX
-rename:
- mv giada giada_lin
-endif
-if WINDOWS
-rename:
- mv giada giada_win.exe
-endif
-if OSX
-rename:
- mv giada giada_osx
-endif
* ALSA, JACK + Transport, CoreAudio, ASIO and DirectSound full support;
* unlimited number of channels (optionally controllable via computer keyboard);
* BPM and beat sync with sample-accurate loop engine;
-* MIDI output support, featuring custom [MIDI lightning messages](https://github.com/monocasual/giada-midimaps);
+* MIDI input and output support, featuring custom [MIDI lightning messages](https://github.com/monocasual/giada-midimaps);
* super-sleek, built-in Wave Editor for audio samples and Piano Roll editor for MIDI messages;
* automatic quantizer;
* portable project storage system, based on super-hackable JSON files;
* support for all major uncompressed file formats;
-* test-driven development style supported by [Travis CI](https://travis-ci.org/monocasual/giada) and [Catch](https://github.com/philsquared/Catch)
+* test-driven development style supported by [GitHub Actions](https://github.com/monocasual/giada/actions) and [Catch](https://github.com/philsquared/Catch)
* under a constant stage of development;
* 100% open-source GPL v3.
## Documentation
-Docs are available online on [the official website](https://www.giadamusic.com/documentation-index).
+Documentation is available online in the [user guide page](https://www.giadamusic.com/documentation-index).
+
+An ever-growing collection of tutorials (both text and video) and live demos is available in the [tutorials & media page](https://www.giadamusic.com/media).
Found a typo or a terrible mistake? Feel free to clone the [website repository](https://github.com/monocasual/giada-www) and send us your pull requests.
## Build Giada from source
-We do our best to make the compilation process as simple as possible. You can find all the information in the [official docs page](https://www.giadamusic.com/documentation-compiling-from-source).
-
-Something went wrong? Try our new [Docker image](https://github.com/monocasual/giada-docker) for building and running Giada without hurdles.
+We do our best to make the compilation process as simple as possible. You can find all the information in the [compiling from source](https://www.giadamusic.com/documentation-compiling-from-source) chapter from the user guide.
## Bugs, requests and questions for non-developers
-Feel free to ask anything on [our end-user forum](https://www.giadamusic.com/forum).
+Feel free to ask anything in the [discussions area](https://github.com/monocasual/giada/discussions).
## Copyright
-Giada is Copyright (C) 2010-2020 by Giovanni A. Zuliani | Monocasual Laboratories
+Giada is Copyright (C) 2010-2021 by Giovanni A. Zuliani | Monocasual Laboratories
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.
+++ /dev/null
-#!/bin/sh
-# a u t o g e n . s h
-#
-# Copyright (c) 2005-2009 United States Government as represented by
-# the U.S. Army Research Laboratory.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials provided
-# with the distribution.
-#
-# 3. The name of the author may not be used to endorse or promote
-# products derived from this software without specific prior written
-# permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-###
-#
-# Script for automatically preparing the sources for compilation by
-# performing the myriad of necessary steps. The script attempts to
-# detect proper version support, and outputs warnings about particular
-# systems that have autotool peculiarities.
-#
-# Basically, if everything is set up and installed correctly, the
-# script will validate that minimum versions of the GNU Build System
-# tools are installed, account for several common configuration
-# issues, and then simply run autoreconf for you.
-#
-# If autoreconf fails, which can happen for many valid configurations,
-# this script proceeds to run manual preparation steps effectively
-# providing a POSIX shell script (mostly complete) reimplementation of
-# autoreconf.
-#
-# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
-# environment variables and corresponding _OPTIONS variables (e.g.
-# AUTORECONF_OPTIONS) may be used to override the default automatic
-# detection behaviors. Similarly the _VERSION variables will override
-# the minimum required version numbers.
-#
-# Examples:
-#
-# To obtain help on usage:
-# ./autogen.sh --help
-#
-# To obtain verbose output:
-# ./autogen.sh --verbose
-#
-# To skip autoreconf and prepare manually:
-# AUTORECONF=false ./autogen.sh
-#
-# To verbosely try running with an older (unsupported) autoconf:
-# AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
-#
-# Author:
-# Christopher Sean Morrison <morrison@brlcad.org>
-#
-# Patches:
-# Sebastian Pipping <sebastian@pipping.org>
-#
-######################################################################
-
-# set to minimum acceptable version of autoconf
-if [ "x$AUTOCONF_VERSION" = "x" ] ; then
- AUTOCONF_VERSION=2.52
-fi
-# set to minimum acceptable version of automake
-if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
- AUTOMAKE_VERSION=1.6.0
-fi
-# set to minimum acceptable version of libtool
-if [ "x$LIBTOOL_VERSION" = "x" ] ; then
- LIBTOOL_VERSION=1.4.2
-fi
-
-
-##################
-# ident function #
-##################
-ident ( ) {
- # extract copyright from header
- __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
- if [ "x$__copyright" = "x" ] ; then
- __copyright="`date +%Y`"
- fi
-
- # extract version from CVS Id string
- __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
- __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
- if [ "x$__version" = "x" ] ; then
- __version=""
- fi
-
- echo "autogen.sh build preparation script by Christopher Sean Morrison"
- echo " + config.guess download patch by Sebastian Pipping (2008-12-03)"
- echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
- echo "script version $__version, ISO/IEC 9945 POSIX shell script"
-}
-
-
-##################
-# USAGE FUNCTION #
-##################
-usage ( ) {
- echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
- echo " --help Help on $NAME_OF_AUTOGEN usage"
- echo " --verbose Verbose progress output"
- echo " --quiet Quiet suppressed progress output"
- echo " --download Download the latest config.guess from gnulib"
- echo " --version Only perform GNU Build System version checks"
- echo
- echo "Description: This script will validate that minimum versions of the"
- echo "GNU Build System tools are installed and then run autoreconf for you."
- echo "Should autoreconf fail, manual preparation steps will be run"
- echo "potentially accounting for several common preparation issues. The"
-
- echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
- echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
- echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
- echo "default automatic detection behavior."
- echo
-
- ident
-
- return 0
-}
-
-
-##########################
-# VERSION_ERROR FUNCTION #
-##########################
-version_error ( ) {
- if [ "x$1" = "x" ] ; then
- echo "INTERNAL ERROR: version_error was not provided a version"
- exit 1
- fi
- if [ "x$2" = "x" ] ; then
- echo "INTERNAL ERROR: version_error was not provided an application name"
- exit 1
- fi
- $ECHO
- $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch,"
- $ECHO " at least version $1 of $2 must be installed."
- $ECHO
- $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
- $ECHO "run configure or make. Either the GNU Autotools will need to be installed"
- $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
- $ECHO "code on another system and then transferred to here. -- Cheers!"
- $ECHO
-}
-
-##########################
-# VERSION_CHECK FUNCTION #
-##########################
-version_check ( ) {
- if [ "x$1" = "x" ] ; then
- echo "INTERNAL ERROR: version_check was not provided a minimum version"
- exit 1
- fi
- _min="$1"
- if [ "x$2" = "x" ] ; then
- echo "INTERNAL ERROR: version check was not provided a comparison version"
- exit 1
- fi
- _cur="$2"
-
- # needed to handle versions like 1.10 and 1.4-p6
- _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
- _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
-
- _min_major="`echo $_min | cut -d. -f1`"
- _min_minor="`echo $_min | cut -d. -f2`"
- _min_patch="`echo $_min | cut -d. -f3`"
-
- _cur_major="`echo $_cur | cut -d. -f1`"
- _cur_minor="`echo $_cur | cut -d. -f2`"
- _cur_patch="`echo $_cur | cut -d. -f3`"
-
- if [ "x$_min_major" = "x" ] ; then
- _min_major=0
- fi
- if [ "x$_min_minor" = "x" ] ; then
- _min_minor=0
- fi
- if [ "x$_min_patch" = "x" ] ; then
- _min_patch=0
- fi
- if [ "x$_cur_minor" = "x" ] ; then
- _cur_major=0
- fi
- if [ "x$_cur_minor" = "x" ] ; then
- _cur_minor=0
- fi
- if [ "x$_cur_patch" = "x" ] ; then
- _cur_patch=0
- fi
-
- $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
-
- if [ $_min_major -lt $_cur_major ] ; then
- return 0
- elif [ $_min_major -eq $_cur_major ] ; then
- if [ $_min_minor -lt $_cur_minor ] ; then
- return 0
- elif [ $_min_minor -eq $_cur_minor ] ; then
- if [ $_min_patch -lt $_cur_patch ] ; then
- return 0
- elif [ $_min_patch -eq $_cur_patch ] ; then
- return 0
- fi
- fi
- fi
- return 1
-}
-
-
-######################################
-# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
-######################################
-locate_configure_template ( ) {
- _pwd="`pwd`"
- if test -f "./configure.ac" ; then
- echo "./configure.ac"
- elif test -f "./configure.in" ; then
- echo "./configure.in"
- elif test -f "$_pwd/configure.ac" ; then
- echo "$_pwd/configure.ac"
- elif test -f "$_pwd/configure.in" ; then
- echo "$_pwd/configure.in"
- elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
- echo "$PATH_TO_AUTOGEN/configure.ac"
- elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
- echo "$PATH_TO_AUTOGEN/configure.in"
- fi
-}
-
-
-##################
-# argument check #
-##################
-ARGS="$*"
-PATH_TO_AUTOGEN="`dirname $0`"
-NAME_OF_AUTOGEN="`basename $0`"
-AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
-
-LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
-
-if [ "x$HELP" = "x" ] ; then
- HELP=no
-fi
-if [ "x$QUIET" = "x" ] ; then
- QUIET=no
-fi
-if [ "x$VERBOSE" = "x" ] ; then
- VERBOSE=no
-fi
-if [ "x$VERSION_ONLY" = "x" ] ; then
- VERSION_ONLY=no
-fi
-if [ "x$DOWNLOAD" = "x" ] ; then
- DOWNLOAD=no
-fi
-if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
- AUTORECONF_OPTIONS="-i -f"
-fi
-if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
- AUTOCONF_OPTIONS="-f"
-fi
-if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
- AUTOMAKE_OPTIONS="-a -c -f"
-fi
-ALT_AUTOMAKE_OPTIONS="-a -c"
-if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
- LIBTOOLIZE_OPTIONS="--automake -c -f"
-fi
-ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
-if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
- ACLOCAL_OPTIONS=""
-fi
-if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
- AUTOHEADER_OPTIONS=""
-fi
-if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
- CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
-fi
-for arg in $ARGS ; do
- case "x$arg" in
- x--help) HELP=yes ;;
- x-[hH]) HELP=yes ;;
- x--quiet) QUIET=yes ;;
- x-[qQ]) QUIET=yes ;;
- x--verbose) VERBOSE=yes ;;
- x-[dD]) DOWNLOAD=yes ;;
- x--download) DOWNLOAD=yes ;;
- x-[vV]) VERBOSE=yes ;;
- x--version) VERSION_ONLY=yes ;;
- *)
- echo "Unknown option: $arg"
- echo
- usage
- exit 1
- ;;
- esac
-done
-
-
-#####################
-# environment check #
-#####################
-
-# sanity check before recursions potentially begin
-if [ ! -f "$AUTOGEN_SH" ] ; then
- echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
- if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
- echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
- fi
- exit 1
-fi
-
-# force locale setting to C so things like date output as expected
-LC_ALL=C
-
-# commands that this script expects
-for __cmd in echo head tail pwd ; do
- echo "test" | $__cmd > /dev/null 2>&1
- if [ $? != 0 ] ; then
- echo "INTERNAL ERROR: '${__cmd}' command is required"
- exit 2
- fi
-done
-echo "test" | grep "test" > /dev/null 2>&1
-if test ! x$? = x0 ; then
- echo "INTERNAL ERROR: grep command is required"
- exit 1
-fi
-echo "test" | sed "s/test/test/" > /dev/null 2>&1
-if test ! x$? = x0 ; then
- echo "INTERNAL ERROR: sed command is required"
- exit 1
-fi
-
-
-# determine the behavior of echo
-case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
- *c*,-n*) ECHO_N= ECHO_C='
-' ECHO_T=' ' ;;
- *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
- *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
-esac
-
-# determine the behavior of head
-case "x`echo 'head' | head -n 1 2>&1`" in
- *xhead*) HEAD_N="n " ;;
- *) HEAD_N="" ;;
-esac
-
-# determine the behavior of tail
-case "x`echo 'tail' | tail -n 1 2>&1`" in
- *xtail*) TAIL_N="n " ;;
- *) TAIL_N="" ;;
-esac
-
-VERBOSE_ECHO=:
-ECHO=:
-if [ "x$QUIET" = "xyes" ] ; then
- if [ "x$VERBOSE" = "xyes" ] ; then
- echo "Verbose output quelled by quiet option. Further output disabled."
- fi
-else
- ECHO=echo
- if [ "x$VERBOSE" = "xyes" ] ; then
- echo "Verbose output enabled"
- VERBOSE_ECHO=echo
- fi
-fi
-
-
-# allow a recursive run to disable further recursions
-if [ "x$RUN_RECURSIVE" = "x" ] ; then
- RUN_RECURSIVE=yes
-fi
-
-
-################################################
-# check for help arg and bypass version checks #
-################################################
-if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
- HELP=yes
-fi
-if [ "x$HELP" = "xyes" ] ; then
- usage
- $ECHO "---"
- $ECHO "Help was requested. No preparation or configuration will be performed."
- exit 0
-fi
-
-
-#######################
-# set up signal traps #
-#######################
-untrap_abnormal ( ) {
- for sig in 1 2 13 15; do
- trap - $sig
- done
-}
-
-# do this cleanup whenever we exit.
-trap '
- # start from the root
- if test -d "$START_PATH" ; then
- cd "$START_PATH"
- fi
-
- # restore/delete backup files
- if test "x$PFC_INIT" = "x1" ; then
- recursive_restore
- fi
-' 0
-
-# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
-for sig in 1 2 13 15; do
- trap '
- $ECHO ""
- $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
-
- # start from the root
- if test -d "$START_PATH" ; then
- cd "$START_PATH"
- fi
-
- # clean up on abnormal exit
- $VERBOSE_ECHO "rm -rf autom4te.cache"
- rm -rf autom4te.cache
-
- if test -f "acinclude.m4.$$.backup" ; then
- $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
- chmod u+w acinclude.m4
- cat acinclude.m4.$$.backup > acinclude.m4
-
- $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
- rm -f acinclude.m4.$$.backup
- fi
-
- { (exit 1); exit 1; }
-' $sig
-done
-
-
-#############################
-# look for a configure file #
-#############################
-if [ "x$CONFIGURE" = "x" ] ; then
- CONFIGURE="`locate_configure_template`"
- if [ ! "x$CONFIGURE" = "x" ] ; then
- $VERBOSE_ECHO "Found a configure template: $CONFIGURE"
- fi
-else
- $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
-fi
-if [ "x$CONFIGURE" = "x" ] ; then
- if [ "x$VERSION_ONLY" = "xyes" ] ; then
- CONFIGURE=/dev/null
- else
- $ECHO
- $ECHO "A configure.ac or configure.in file could not be located implying"
- $ECHO "that the GNU Build System is at least not used in this directory. In"
- $ECHO "any case, there is nothing to do here without one of those files."
- $ECHO
- $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
- exit 1
- fi
-fi
-
-####################
-# get project name #
-####################
-if [ "x$PROJECT" = "x" ] ; then
- PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- if [ "x$PROJECT" = "xAC_INIT" ] ; then
- # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
- PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- fi
- if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
- PROJECT="project"
- fi
- if [ "x$PROJECT" = "x" ] ; then
- PROJECT="project"
- fi
-else
- $ECHO "Using PROJECT environment variable override: $PROJECT"
-fi
-$ECHO "Preparing the $PROJECT build system...please wait"
-$ECHO
-
-
-########################
-# check for autoreconf #
-########################
-HAVE_AUTORECONF=no
-if [ "x$AUTORECONF" = "x" ] ; then
- for AUTORECONF in autoreconf ; do
- $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
- $AUTORECONF --version > /dev/null 2>&1
- if [ $? = 0 ] ; then
- HAVE_AUTORECONF=yes
- break
- fi
- done
-else
- HAVE_AUTORECONF=yes
- $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
-fi
-
-
-##########################
-# autoconf version check #
-##########################
-_acfound=no
-if [ "x$AUTOCONF" = "x" ] ; then
- for AUTOCONF in autoconf ; do
- $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
- $AUTOCONF --version > /dev/null 2>&1
- if [ $? = 0 ] ; then
- _acfound=yes
- break
- fi
- done
-else
- _acfound=yes
- $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
-fi
-
-_report_error=no
-if [ ! "x$_acfound" = "xyes" ] ; then
- $ECHO "ERROR: Unable to locate GNU Autoconf."
- _report_error=yes
-else
- _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
- if [ "x$_version" = "x" ] ; then
- _version="0.0.0"
- fi
- $ECHO "Found GNU Autoconf version $_version"
- version_check "$AUTOCONF_VERSION" "$_version"
- if [ $? -ne 0 ] ; then
- _report_error=yes
- fi
-fi
-if [ "x$_report_error" = "xyes" ] ; then
- version_error "$AUTOCONF_VERSION" "GNU Autoconf"
- exit 1
-fi
-
-
-##########################
-# automake version check #
-##########################
-_amfound=no
-if [ "x$AUTOMAKE" = "x" ] ; then
- for AUTOMAKE in automake ; do
- $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
- $AUTOMAKE --version > /dev/null 2>&1
- if [ $? = 0 ] ; then
- _amfound=yes
- break
- fi
- done
-else
- _amfound=yes
- $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
-fi
-
-
-_report_error=no
-if [ ! "x$_amfound" = "xyes" ] ; then
- $ECHO
- $ECHO "ERROR: Unable to locate GNU Automake."
- _report_error=yes
-else
- _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
- if [ "x$_version" = "x" ] ; then
- _version="0.0.0"
- fi
- $ECHO "Found GNU Automake version $_version"
- version_check "$AUTOMAKE_VERSION" "$_version"
- if [ $? -ne 0 ] ; then
- _report_error=yes
- fi
-fi
-if [ "x$_report_error" = "xyes" ] ; then
- version_error "$AUTOMAKE_VERSION" "GNU Automake"
- exit 1
-fi
-
-
-########################
-# check for libtoolize #
-########################
-HAVE_LIBTOOLIZE=yes
-HAVE_ALT_LIBTOOLIZE=no
-_ltfound=no
-if [ "x$LIBTOOLIZE" = "x" ] ; then
- LIBTOOLIZE=libtoolize
- $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
- $LIBTOOLIZE --version > /dev/null 2>&1
- if [ ! $? = 0 ] ; then
- HAVE_LIBTOOLIZE=no
- $ECHO
- if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
- $ECHO "Warning: libtoolize does not appear to be available."
- else
- $ECHO "Warning: libtoolize does not appear to be available. This means that"
- $ECHO "the automatic build preparation via autoreconf will probably not work."
- $ECHO "Preparing the build by running each step individually, however, should"
- $ECHO "work and will be done automatically for you if autoreconf fails."
- fi
-
- # look for some alternates
- for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
- $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
- _glibtoolize="`$tool --version > /dev/null 2>&1`"
- if [ $? = 0 ] ; then
- $VERBOSE_ECHO "Found $tool --version"
- _glti="`which $tool`"
- if [ "x$_glti" = "x" ] ; then
- $VERBOSE_ECHO "Cannot find $tool with which"
- continue;
- fi
- if test ! -f "$_glti" ; then
- $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
- continue;
- fi
- _gltidir="`dirname $_glti`"
- if [ "x$_gltidir" = "x" ] ; then
- $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
- continue;
- fi
- if test ! -d "$_gltidir" ; then
- $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
- continue;
- fi
- HAVE_ALT_LIBTOOLIZE=yes
- LIBTOOLIZE="$tool"
- $ECHO
- $ECHO "Fortunately, $tool was found which means that your system may simply"
- $ECHO "have a non-standard or incomplete GNU Autotools install. If you have"
- $ECHO "sufficient system access, it may be possible to quell this warning by"
- $ECHO "running:"
- $ECHO
- sudo -V > /dev/null 2>&1
- if [ $? = 0 ] ; then
- $ECHO " sudo ln -s $_glti $_gltidir/libtoolize"
- $ECHO
- else
- $ECHO " ln -s $_glti $_gltidir/libtoolize"
- $ECHO
- $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
- $ECHO
- fi
- _ltfound=yes
- break
- fi
- done
- else
- _ltfound=yes
- fi
-else
- _ltfound=yes
- $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
-fi
-
-
-############################
-# libtoolize version check #
-############################
-_report_error=no
-if [ ! "x$_ltfound" = "xyes" ] ; then
- $ECHO
- $ECHO "ERROR: Unable to locate GNU Libtool."
- _report_error=yes
-else
- _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
- if [ "x$_version" = "x" ] ; then
- _version="0.0.0"
- fi
- $ECHO "Found GNU Libtool version $_version"
- version_check "$LIBTOOL_VERSION" "$_version"
- if [ $? -ne 0 ] ; then
- _report_error=yes
- fi
-fi
-if [ "x$_report_error" = "xyes" ] ; then
- version_error "$LIBTOOL_VERSION" "GNU Libtool"
- exit 1
-fi
-
-
-#####################
-# check for aclocal #
-#####################
-if [ "x$ACLOCAL" = "x" ] ; then
- for ACLOCAL in aclocal ; do
- $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
- $ACLOCAL --version > /dev/null 2>&1
- if [ $? = 0 ] ; then
- break
- fi
- done
-else
- $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
-fi
-
-
-########################
-# check for autoheader #
-########################
-if [ "x$AUTOHEADER" = "x" ] ; then
- for AUTOHEADER in autoheader ; do
- $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
- $AUTOHEADER --version > /dev/null 2>&1
- if [ $? = 0 ] ; then
- break
- fi
- done
-else
- $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
-fi
-
-
-#########################
-# check if version only #
-#########################
-$VERBOSE_ECHO "Checking whether to only output version information"
-if [ "x$VERSION_ONLY" = "xyes" ] ; then
- $ECHO
- ident
- $ECHO "---"
- $ECHO "Version requested. No preparation or configuration will be performed."
- exit 0
-fi
-
-
-#################################
-# PROTECT_FROM_CLOBBER FUNCTION #
-#################################
-protect_from_clobber ( ) {
- PFC_INIT=1
-
- # protect COPYING & INSTALL from overwrite by automake. the
- # automake force option will (inappropriately) ignore the existing
- # contents of a COPYING and/or INSTALL files (depending on the
- # version) instead of just forcing *missing* files like it does
- # for AUTHORS, NEWS, and README. this is broken but extremely
- # prevalent behavior, so we protect against it by keeping a backup
- # of the file that can later be restored.
-
- for file in COPYING INSTALL ; do
- if test -f ${file} ; then
- if test -f ${file}.$$.protect_from_automake.backup ; then
- $VERBOSE_ECHO "Already backed up ${file} in `pwd`"
- else
- $VERBOSE_ECHO "Backing up ${file} in `pwd`"
- $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
- cp -p ${file} ${file}.$$.protect_from_automake.backup
- fi
- fi
- done
-}
-
-
-##############################
-# RECURSIVE_PROTECT FUNCTION #
-##############################
-recursive_protect ( ) {
-
- # for projects using recursive configure, run the build
- # preparation steps for the subdirectories. this function assumes
- # START_PATH was set to pwd before recursion begins so that
- # relative paths work.
-
- # git 'r done, protect COPYING and INSTALL from being clobbered
- protect_from_clobber
-
- if test -d autom4te.cache ; then
- $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
- $VERBOSE_ECHO "rm -rf autom4te.cache"
- rm -rf autom4te.cache
- fi
-
- # find configure template
- _configure="`locate_configure_template`"
- if [ "x$_configure" = "x" ] ; then
- return
- fi
- # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
-
- # look for subdirs
- # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
- _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- CHECK_DIRS=""
- for dir in $_det_config_subdirs ; do
- if test -d "`pwd`/$dir" ; then
- CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
- fi
- done
-
- # process subdirs
- if [ ! "x$CHECK_DIRS" = "x" ] ; then
- $VERBOSE_ECHO "Recursively scanning the following directories:"
- $VERBOSE_ECHO " $CHECK_DIRS"
- for dir in $CHECK_DIRS ; do
- $VERBOSE_ECHO "Protecting files from automake in $dir"
- cd "$START_PATH"
- eval "cd $dir"
-
- # recursively git 'r done
- recursive_protect
- done
- fi
-} # end of recursive_protect
-
-
-#############################
-# RESTORE_CLOBBERED FUNCION #
-#############################
-restore_clobbered ( ) {
-
- # The automake (and autoreconf by extension) -f/--force-missing
- # option may overwrite COPYING and INSTALL even if they do exist.
- # Here we restore the files if necessary.
-
- spacer=no
-
- for file in COPYING INSTALL ; do
- if test -f ${file}.$$.protect_from_automake.backup ; then
- if test -f ${file} ; then
- # compare entire content, restore if needed
- if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
- if test "x$spacer" = "xno" ; then
- $VERBOSE_ECHO
- spacer=yes
- fi
- # restore the backup
- $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
- $VERBOSE_ECHO "rm -f ${file}"
- rm -f ${file}
- $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
- mv ${file}.$$.protect_from_automake.backup ${file}
- fi # check contents
- elif test -f ${file}.$$.protect_from_automake.backup ; then
- $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
- mv ${file}.$$.protect_from_automake.backup ${file}
- fi # -f ${file}
-
- # just in case
- $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
- rm -f ${file}.$$.protect_from_automake.backup
- fi # -f ${file}.$$.protect_from_automake.backup
- done
-
- CONFIGURE="`locate_configure_template`"
- if [ "x$CONFIGURE" = "x" ] ; then
- return
- fi
-
- _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- if test ! -d "$_aux_dir" ; then
- _aux_dir=.
- fi
-
- for file in config.guess config.sub ltmain.sh ; do
- if test -f "${_aux_dir}/${file}" ; then
- $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
- rm -f "${_aux_dir}/${file}.backup"
- fi
- done
-} # end of restore_clobbered
-
-
-##############################
-# RECURSIVE_RESTORE FUNCTION #
-##############################
-recursive_restore ( ) {
-
- # restore COPYING and INSTALL from backup if they were clobbered
- # for each directory recursively.
-
- # git 'r undone
- restore_clobbered
-
- # find configure template
- _configure="`locate_configure_template`"
- if [ "x$_configure" = "x" ] ; then
- return
- fi
-
- # look for subdirs
- _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- CHECK_DIRS=""
- for dir in $_det_config_subdirs ; do
- if test -d "`pwd`/$dir" ; then
- CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
- fi
- done
-
- # process subdirs
- if [ ! "x$CHECK_DIRS" = "x" ] ; then
- $VERBOSE_ECHO "Recursively scanning the following directories:"
- $VERBOSE_ECHO " $CHECK_DIRS"
- for dir in $CHECK_DIRS ; do
- $VERBOSE_ECHO "Checking files for automake damage in $dir"
- cd "$START_PATH"
- eval "cd $dir"
-
- # recursively git 'r undone
- recursive_restore
- done
- fi
-} # end of recursive_restore
-
-
-#######################
-# INITIALIZE FUNCTION #
-#######################
-initialize ( ) {
-
- # this routine performs a variety of directory-specific
- # initializations. some are sanity checks, some are preventive,
- # and some are necessary setup detection.
- #
- # this function sets:
- # CONFIGURE
- # SEARCH_DIRS
- # CONFIG_SUBDIRS
-
- ##################################
- # check for a configure template #
- ##################################
- CONFIGURE="`locate_configure_template`"
- if [ "x$CONFIGURE" = "x" ] ; then
- $ECHO
- $ECHO "A configure.ac or configure.in file could not be located implying"
- $ECHO "that the GNU Build System is at least not used in this directory. In"
- $ECHO "any case, there is nothing to do here without one of those files."
- $ECHO
- $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
- exit 1
- fi
-
- #####################
- # detect an aux dir #
- #####################
- _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- if test ! -d "$_aux_dir" ; then
- _aux_dir=.
- else
- $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
- fi
-
- ################################
- # detect a recursive configure #
- ################################
- CONFIG_SUBDIRS=""
- _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
- for dir in $_det_config_subdirs ; do
- if test -d "`pwd`/$dir" ; then
- $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
- CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
- fi
- done
-
- ###########################################################
- # make sure certain required files exist for GNU projects #
- ###########################################################
- _marker_found=""
- _marker_found_message_intro='Detected non-GNU marker "'
- _marker_found_message_mid='" in '
- for marker in foreign cygnus ; do
- _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
- _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
- if [ ! "x$_marker_found" = "x" ] ; then
- $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
- break
- fi
- if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
- _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
- if [ ! "x$_marker_found" = "x" ] ; then
- $VERBOSE_ECHO "${_marker_found_message}Makefile.am"
- break
- fi
- fi
- done
- if [ "x${_marker_found}" = "x" ] ; then
- _suggest_foreign=no
- for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
- if [ ! -f $file ] ; then
- $VERBOSE_ECHO "Touching ${file} since it does not exist"
- _suggest_foreign=yes
- touch $file
- fi
- done
-
- if [ "x${_suggest_foreign}" = "xyes" ] ; then
- $ECHO
- $ECHO "Warning: Several files expected of projects that conform to the GNU"
- $ECHO "coding standards were not found. The files were automatically added"
- $ECHO "for you since you do not have a 'foreign' declaration specified."
- $ECHO
- $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
- if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
- $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
- fi
- $ECHO
- fi
- fi
-
- ##################################################
- # make sure certain generated files do not exist #
- ##################################################
- for file in config.guess config.sub ltmain.sh ; do
- if test -f "${_aux_dir}/${file}" ; then
- $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
- mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
- fi
- done
-
- ############################
- # search alternate m4 dirs #
- ############################
- SEARCH_DIRS=""
- for dir in m4 ; do
- if [ -d $dir ] ; then
- $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
- SEARCH_DIRS="$SEARCH_DIRS -I $dir"
- fi
- done
-
- ######################################
- # remove any previous build products #
- ######################################
- if test -d autom4te.cache ; then
- $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
- $VERBOSE_ECHO "rm -rf autom4te.cache"
- rm -rf autom4te.cache
- fi
-# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
-# if test -f aclocal.m4 ; then
-# $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
-# $VERBOSE_ECHO "rm -f aclocal.m4"
-# rm -f aclocal.m4
-# fi
-
-} # end of initialize()
-
-
-##############
-# initialize #
-##############
-
-# stash path
-START_PATH="`pwd`"
-
-# Before running autoreconf or manual steps, some prep detection work
-# is necessary or useful. Only needs to occur once per directory, but
-# does need to traverse the entire subconfigure hierarchy to protect
-# files from being clobbered even by autoreconf.
-recursive_protect
-
-# start from where we started
-cd "$START_PATH"
-
-# get ready to process
-initialize
-
-
-#########################################
-# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
-#########################################
-
-# TODO - should make sure wget/curl exist and/or work before trying to
-# use them.
-
-download_gnulib_config_guess () {
- # abuse gitweb to download gnulib's latest config.guess via HTTP
- config_guess_temp="config.guess.$$.download"
- ret=1
- for __cmd in wget curl fetch ; do
- $VERBOSE_ECHO "Checking for command ${__cmd}"
- ${__cmd} --version > /dev/null 2>&1
- ret=$?
- if [ ! $ret = 0 ] ; then
- continue
- fi
-
- __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
- $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
-
- opts=""
- case ${__cmd} in
- wget)
- opts="-O"
- ;;
- curl)
- opts="-o"
- ;;
- fetch)
- opts="-t 5 -f"
- ;;
- esac
-
- $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
- eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
- if [ $? = 0 ] ; then
- mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
- ret=0
- break
- fi
- done
-
- if [ ! $ret = 0 ] ; then
- $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
- rm -f "${config_guess_temp}"
- fi
-}
-
-
-##############################
-# LIBTOOLIZE_NEEDED FUNCTION #
-##############################
-libtoolize_needed () {
- ret=1 # means no, don't need libtoolize
- for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
- $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
- found="`grep \"^$feature.*\" $CONFIGURE`"
- if [ ! "x$found" = "x" ] ; then
- ret=0 # means yes, need to run libtoolize
- break
- fi
- done
- return ${ret}
-}
-
-
-
-############################################
-# prepare build via autoreconf or manually #
-############################################
-reconfigure_manually=no
-if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
- $ECHO
- $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
-
- $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
- autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$autoreconf_output"
-
- if [ ! $ret = 0 ] ; then
- if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
- if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
- $ECHO
- $ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
- $ECHO "misconfiguration issue. This problem is encountered on systems that"
- $ECHO "have installed libtoolize under a different name without providing a"
- $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
- $ECHO
- $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
-
- export LIBTOOLIZE
- RUN_RECURSIVE=no
- export RUN_RECURSIVE
- untrap_abnormal
-
- $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
- sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
- exit $?
- fi
- fi
-
- $ECHO "Warning: $AUTORECONF failed"
-
- if test -f ltmain.sh ; then
- $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
- fi
-
- $ECHO "Attempting to run the preparation steps individually"
- reconfigure_manually=yes
- else
- if [ "x$DOWNLOAD" = "xyes" ] ; then
- if libtoolize_needed ; then
- download_gnulib_config_guess
- fi
- fi
- fi
-else
- reconfigure_manually=yes
-fi
-
-
-############################
-# LIBTOOL_FAILURE FUNCTION #
-############################
-libtool_failure ( ) {
-
- # libtool is rather error-prone in comparison to the other
- # autotools and this routine attempts to compensate for some
- # common failures. the output after a libtoolize failure is
- # parsed for an error related to AC_PROG_LIBTOOL and if found, we
- # attempt to inject a project-provided libtool.m4 file.
-
- _autoconf_output="$1"
-
- if [ "x$RUN_RECURSIVE" = "xno" ] ; then
- # we already tried the libtool.m4, don't try again
- return 1
- fi
-
- if test -f "$LIBTOOL_M4" ; then
- found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
- if test ! "x$found_libtool" = "x" ; then
- if test -f acinclude.m4 ; then
- rm -f acinclude.m4.$$.backup
- $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
- cat acinclude.m4 > acinclude.m4.$$.backup
- fi
- $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
- chmod u+w acinclude.m4
- cat "$LIBTOOL_M4" >> acinclude.m4
-
- # don't keep doing this
- RUN_RECURSIVE=no
- export RUN_RECURSIVE
- untrap_abnormal
-
- $ECHO
- $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
- $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
- sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
- exit $?
- fi
- fi
-}
-
-
-###########################
-# MANUAL_AUTOGEN FUNCTION #
-###########################
-manual_autogen ( ) {
-
- ##################################################
- # Manual preparation steps taken are as follows: #
- # aclocal [-I m4] #
- # libtoolize --automake -c -f #
- # aclocal [-I m4] #
- # autoconf -f #
- # autoheader #
- # automake -a -c -f #
- ##################################################
-
- ###########
- # aclocal #
- ###########
- $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
- aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$aclocal_output"
- if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
-
- ##############
- # libtoolize #
- ##############
- if libtoolize_needed ; then
- if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
- $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
- libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$libtoolize_output"
-
- if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
- else
- if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
- $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
- libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$libtoolize_output"
-
- if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
- fi
- fi
-
- ###########
- # aclocal #
- ###########
- # re-run again as instructed by libtoolize
- $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
- aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$aclocal_output"
-
- # libtoolize might put ltmain.sh in the wrong place
- if test -f ltmain.sh ; then
- if test ! -f "${_aux_dir}/ltmain.sh" ; then
- $ECHO
- $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
- $ECHO
- $ECHO "Fortunately, the problem can be worked around by simply copying the"
- $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you."
- $ECHO
- $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
- cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
- $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
- fi
- fi # ltmain.sh
-
- if [ "x$DOWNLOAD" = "xyes" ] ; then
- download_gnulib_config_guess
- fi
- fi # libtoolize_needed
-
- ############
- # autoconf #
- ############
- $VERBOSE_ECHO
- $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
- autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$autoconf_output"
-
- if [ ! $ret = 0 ] ; then
- # retry without the -f and check for usage of macros that are too new
- ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
- ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
- ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
-
- macros_to_search=""
- ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
- ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
-
- if [ $ac_major -lt 2 ] ; then
- macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
- else
- if [ $ac_minor -lt 54 ] ; then
- macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
- elif [ $ac_minor -lt 55 ] ; then
- macros_to_search="$ac2_59_macros $ac2_55_macros"
- elif [ $ac_minor -lt 59 ] ; then
- macros_to_search="$ac2_59_macros"
- fi
- fi
-
- configure_ac_macros=__none__
- for feature in $macros_to_search ; do
- $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
- found="`grep \"^$feature.*\" $CONFIGURE`"
- if [ ! "x$found" = "x" ] ; then
- if [ "x$configure_ac_macros" = "x__none__" ] ; then
- configure_ac_macros="$feature"
- else
- configure_ac_macros="$feature $configure_ac_macros"
- fi
- fi
- done
- if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
- $ECHO
- $ECHO "Warning: Unsupported macros were found in $CONFIGURE"
- $ECHO
- $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
- $ECHO "unsupported macros are used that exceed the minimum version"
- $ECHO "settings specified within this file. As such, the following macros"
- $ECHO "should be removed from configure.ac or the version numbers in this"
- $ECHO "file should be increased:"
- $ECHO
- $ECHO "$configure_ac_macros"
- $ECHO
- $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
- fi
-
- ###################
- # autoconf, retry #
- ###################
- $VERBOSE_ECHO
- $VERBOSE_ECHO "$AUTOCONF"
- autoconf_output="`$AUTOCONF 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$autoconf_output"
-
- if [ ! $ret = 0 ] ; then
- # test if libtool is busted
- libtool_failure "$autoconf_output"
-
- # let the user know what went wrong
- cat <<EOF
-$autoconf_output
-EOF
- $ECHO "ERROR: $AUTOCONF failed"
- exit 2
- else
- # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
- $ECHO
- $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
- $ECHO " AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
- $ECHO
- $ECHO "Removing those options should not be necessary and indicate some other"
- $ECHO "problem with the build system. The build preparation is highly suspect"
- $ECHO "and may result in configuration or compilation errors. Consider"
- if [ "x$VERBOSE_ECHO" = "x:" ] ; then
- $ECHO "rerunning the build preparation with verbose output enabled."
- $ECHO " $AUTOGEN_SH --verbose"
- else
- $ECHO "reviewing the minimum GNU Autotools version settings contained in"
- $ECHO "this script along with the macros being used in your `basename \"$CONFIGURE\"` file."
- fi
- $ECHO
- $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
- fi # autoconf ret = 0
- fi # autoconf ret = 0
-
- ##############
- # autoheader #
- ##############
- need_autoheader=no
- for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
- $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
- found="`grep \"^$feature.*\" $CONFIGURE`"
- if [ ! "x$found" = "x" ] ; then
- need_autoheader=yes
- break
- fi
- done
- if [ "x$need_autoheader" = "xyes" ] ; then
- $VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
- autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$autoheader_output"
- if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
- fi # need_autoheader
-
- ############
- # automake #
- ############
- need_automake=no
- for feature in AM_INIT_AUTOMAKE ; do
- $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
- found="`grep \"^$feature.*\" $CONFIGURE`"
- if [ ! "x$found" = "x" ] ; then
- need_automake=yes
- break
- fi
- done
-
- if [ "x$need_automake" = "xyes" ] ; then
- $VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
- automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$automake_output"
-
- if [ ! $ret = 0 ] ; then
-
- ###################
- # automake, retry #
- ###################
- ALT_AUTOMAKE_OPTIONS="$ALT_AUTOMAKE_OPTIONS --add-missing"
- $VERBOSE_ECHO
- $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
- # retry without the -f
- automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
- ret=$?
- $VERBOSE_ECHO "$automake_output"
-
- if [ ! $ret = 0 ] ; then
- # test if libtool is busted
- libtool_failure "$automake_output"
-
- # let the user know what went wrong
- cat <<EOF
-$automake_output
-EOF
- $ECHO "ERROR: $AUTOMAKE failed"
- exit 2
- fi # automake retry
- fi # automake ret = 0
- fi # need_automake
-} # end of manual_autogen
-
-
-#####################################
-# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
-#####################################
-recursive_manual_autogen ( ) {
-
- # run the build preparation steps manually for this directory
- manual_autogen
-
- # for projects using recursive configure, run the build
- # preparation steps for the subdirectories.
- if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
- $VERBOSE_ECHO "Recursively configuring the following directories:"
- $VERBOSE_ECHO " $CONFIG_SUBDIRS"
- for dir in $CONFIG_SUBDIRS ; do
- $VERBOSE_ECHO "Processing recursive configure in $dir"
- cd "$START_PATH"
- cd "$dir"
-
- # new directory, prepare
- initialize
-
- # run manual steps for the subdir and any others below
- recursive_manual_autogen
- done
- fi
-}
-
-
-################################
-# run manual preparation steps #
-################################
-if [ "x$reconfigure_manually" = "xyes" ] ; then
- $ECHO
- $ECHO $ECHO_N "Preparing build ... $ECHO_C"
-
- recursive_manual_autogen
-fi
-
-
-#########################
-# restore and summarize #
-#########################
-cd "$START_PATH"
-
-# restore COPYING and INSTALL from backup if necessary
-recursive_restore
-
-# make sure we end up with a configure script
-config_ac="`locate_configure_template`"
-config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
-if [ "x$config" = "x" ] ; then
- $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
-fi
-
-# summarize
-$ECHO "done"
-$ECHO
-if test "x$config" = "x" -o ! -f "$config" ; then
- $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
- $ECHO "does not seem to be a resulting configure file. This is unexpected"
- $ECHO "and likely the result of an error. You should run $NAME_OF_AUTOGEN"
- $ECHO "with the --verbose option to get more details on a potential"
- $ECHO "misconfiguration."
-else
- $ECHO "The $PROJECT build system is now prepared. To build here, run:"
- $ECHO " $config"
- $ECHO " make"
-fi
-
-
-# Local Variables:
-# mode: sh
-# tab-width: 8
-# sh-basic-offset: 4
-# sh-indentation: 4
-# indent-tabs-mode: t
-# End:
-# ex: shiftwidth=4 tabstop=8
+++ /dev/null
-# -*- Autoconf -*-
-# Process this file with autoconf to produce a configure script.
-
-# prereq & init
-
-AC_PREREQ(2.60)
-AC_INIT([giada], [0.16], [giadaloopmachine@gmail.com])
-AC_CONFIG_SRCDIR([src/main.cpp])
-AM_INIT_AUTOMAKE([subdir-objects])
-
-# ------------------------------------------------------------------------------
-
-# test the build environment. These vars are used in Makefile.am during
-# the linking of the libraries.
-# Usage: ./configure --target=[windows | linux | osx]
-
-if test "$target" = ""; then
- AC_MSG_ERROR(["target OS not specified. Please run ./configure --target=<windows | linux | freebsd | osx>"])
-fi
-
-case "$target" in
- linux)
- os=linux
- ;;
- freebsd)
- os=freebsd
- ;;
- windows)
- os=windows
- ;;
- osx)
- os=osx
- ;;
- freebsd)
- os=freebsd
- ;;
- *)
- AC_MSG_ERROR(["Unrecognised target OS: $target"])
- ;;
-esac
-AM_CONDITIONAL(LINUX, test "x$os" = "xlinux")
-AM_CONDITIONAL(WINDOWS, test "x$os" = "xwindows")
-AM_CONDITIONAL(OSX, test "x$os" = "xosx")
-AM_CONDITIONAL(FREEBSD, test "x$os" = "xfreebsd")
-
-# ------------------------------------------------------------------------------
-
-# --enable-vst. VST compilation is disabled by default
-#
-# WITH_VST, if present, will be passed to gcc as -DWITH_VST
-#
-# AC_ARG_ENABLE (
-# feature, [--enable-] + [feature], eg --enable-vst
-# help-string,
-# [action-if-given], == gcc ... -DWITH_VST
-# [action-if-not-given]) not used here
-
-AC_ARG_ENABLE(
- [vst],
- AS_HELP_STRING([--enable-vst], [enable vst support]),
- [AC_DEFINE(WITH_VST) AM_CONDITIONAL(WITH_VST, true)],
- [AM_CONDITIONAL(WITH_VST, false)]
-)
-
-# ------------------------------------------------------------------------------
-
-# --enable-system-catch. If enabled, use the system-provided Catch. Use bundled
-# version otherwise (default mode).
-
-AC_ARG_ENABLE(
- [system-catch],
- AS_HELP_STRING([--enable-system-catch], [use system-provided Catch library]),
- [AC_DEFINE(WITH_SYSTEM_CATCH) AM_CONDITIONAL(WITH_SYSTEM_CATCH, true)],
- [AM_CONDITIONAL(WITH_SYSTEM_CATCH, false)]
-)
-
-# ------------------------------------------------------------------------------
-
-# --debug. Enable debug compilation
-
-AC_ARG_ENABLE(
- [debug],
- AS_HELP_STRING([--enable-debug], [enable debug mode (asserts, ...)]),
- [],
- [AC_DEFINE(NDEBUG)]
-)
-
-# ------------------------------------------------------------------------------
-
-# Check for C++ compiler
-
-AC_PROG_CXX
-
-# Check for Objective-C++ compiler
-
-AC_PROG_OBJCXX
-
-# Check for make
-
-AC_PROG_MAKE_SET
-
-# ------------------------------------------------------------------------------
-
-# Check for libraries.
-
-AC_CHECK_LIB(
- [pthread],
- [pthread_exit],
- [],
- [AC_MSG_ERROR([error: library 'pthread' not found!])]
-)
-
-# ------------------------------------------------------------------------------
-
-# Check for generic headers (fltk, rtaudio and libsndfile are static,
-# we ask if headers are available)
-
-AC_LANG_PUSH([C++])
-AC_CHECK_HEADER(
- [FL/Fl.H],
- [],
- [AC_MSG_ERROR([library 'fltk' not found!])]
-)
-AC_LANG_POP
-
-if test "x$os" = "xosx"; then
- AC_LANG_PUSH([C++])
- AC_CHECK_HEADER(
- [RtMidi.h],
- [],
- [AC_MSG_ERROR([library 'rtMidi' not found!])]
- )
- AC_LANG_POP
-else
- AC_LANG_PUSH([C++])
- AC_CHECK_HEADER(
- [rtmidi/RtMidi.h],
- [],
- [AC_MSG_ERROR([library 'rtMidi' not found!])]
- )
- AC_LANG_POP
-fi
-
-AC_LANG_PUSH([C++])
-AC_CHECK_HEADER(
- [sndfile.h],
- [],
- [AC_MSG_ERROR([library 'libsndfile' not found!])]
-)
-AC_LANG_POP
-
-AC_LANG_PUSH([C++])
-AC_CHECK_HEADER(
- [samplerate.h],
- [],
- [AC_MSG_ERROR([library 'samplerate' not found!])]
-)
-AC_LANG_POP
-
-
-# ------------------------------------------------------------------------------
-
-# Check for linux header files.
-
-if test "x$os" = "xlinux" || test "x$os" = "xfreebsd"; then
-
- AC_LANG_PUSH([C++])
- AC_CHECK_HEADER(
- [X11/xpm.h],
- [],
- [AC_MSG_ERROR([missing xpm.h, maybe you need to install the libxpm-dev package?])]
- )
- AC_LANG_POP
-fi
-
-# ------------------------------------------------------------------------------
-
-# finalizing
-
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
--- /dev/null
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Giada
+Name[es]=Giada
+GenericName=Drum machine and loop sequencer
+GenericName[es]=Caja de ritmos y secuenciador de loops
+Exec=giada %f
+Terminal=false
+Icon=giada-logo
+Categories=Music;AudioVideo;Audio;Midi;X-Digital_Processing;X-Jack;X-MIDI;
+Keywords=Giada;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2010-2021 Giovanni A. Zuliani | Monocasual Laboratories -->
+<component type="desktop">
+ <id>com.giadamusic.Giada</id>
+ <metadata_license>CC0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <name>Giada</name>
+ <summary>Your hardcore loop machine</summary>
+ <description>
+ <p>
+ Giada is an open source, minimalistic and hardcore music
+ production tool. Designed for DJs, live performers and electronic
+ musicians.
+ </p>
+ </description>
+ <content_rating type="oars-1.1" />
+ <launchable type="desktop-id">com.giadamusic.Giada.desktop</launchable>
+ <screenshots>
+ <screenshot type="default">
+ <image>
+ https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-03-large-project.png
+ </image>
+ <caption>A fairly large project with samples and MIDI events</caption>
+ </screenshot>
+ <screenshot>
+ <image>
+ https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-17-midi-action-editor.png
+ </image>
+ <caption>New Action Editor for MIDI events</caption>
+ </screenshot>
+ <screenshot>
+ <image>
+ https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-01-sample-editor.png
+ </image>
+ <caption>Chopping samples in the advanced Sample Editor</caption>
+ </screenshot>
+ </screenshots>
+ <update_contact>giadaloopmachine_AT_gmail.com</update_contact>
+ <url type="homepage">https://www.giadamusic.com/</url>
+ <url type="help">https://www.giadamusic.com/forum</url>
+ <releases>
+ <release version="0.18.0" date="2021-05-19">
+ <description>
+ <ul>
+ <li>New 'free loop-length' audio recording mode (#63)</li>
+ <li>Many AudioBuffer improvements</li>
+ <li>Audio configuration panel refactoring</li>
+ <li>KernelAudio improvements and cleanups</li>
+ <li>Relaxed BPM handling when working with JACK</li>
+ <li>Install executable to FHS compliant location (#450)</li>
+ <li>[CI] Don't UPX binaries on macOS (#459)</li>
+ <li>Fix Overdub protection ON by default not working (#460)</li>
+ <li>Fix crash when moving up from a deleted folder (#455)</li>
+ </ul>
+ </description>
+ </release>
+ </releases>
+</component>
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_ACTION_H
#define G_ACTION_H
-
-#include "types.h"
#include "midiEvent.h"
+#include "types.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
struct Action
{
- ID id = 0; // Invalid
+ ID id = 0; // Invalid
ID channelId;
Frame frame;
MidiEvent event;
ID pluginId = -1;
int pluginParam = -1;
- ID prevId = 0;
- ID nextId = 0;
-
+ ID prevId = 0;
+ ID nextId = 0;
+
const Action* prev = nullptr;
const Action* next = nullptr;
- bool isValid() const
+ bool isValid() const
{
return id != 0;
- }
+ }
bool isVolumeEnvelope() const
- {
- return event.getStatus() == MidiEvent::ENVELOPE && pluginId == -1;
+ {
+ return event.getStatus() == MidiEvent::ENVELOPE && pluginId == -1;
}
};
-
-}} // giada::m::
+} // namespace giada::m
#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <algorithm>
#include "audioBuffer.h"
+#include <algorithm>
+#include <cassert>
-
-namespace giada {
-namespace m
+namespace giada::m
{
AudioBuffer::AudioBuffer()
-: m_data (nullptr)
-, m_size (0)
+: m_data(nullptr)
+, m_size(0)
, m_channels(0)
+, m_viewing(false)
{
}
+/* -------------------------------------------------------------------------- */
AudioBuffer::AudioBuffer(Frame size, int channels)
: AudioBuffer()
alloc(size, channels);
}
-
/* -------------------------------------------------------------------------- */
+AudioBuffer::AudioBuffer(float* data, Frame size, int channels)
+: m_data(data)
+, m_size(size)
+, m_channels(channels)
+, m_viewing(true)
+{
+ assert(channels <= NUM_CHANS);
+}
+
+/* -------------------------------------------------------------------------- */
AudioBuffer::AudioBuffer(const AudioBuffer& o)
-: m_data (new float[o.m_size * o.m_channels])
-, m_size (o.m_size)
-, m_channels(o.m_channels)
{
- std::copy(o.m_data, o.m_data + (o.m_size * o.m_channels), m_data);
+ copy(o);
}
-
/* -------------------------------------------------------------------------- */
+AudioBuffer::AudioBuffer(AudioBuffer&& o)
+{
+ move(std::move(o));
+}
+
+/* -------------------------------------------------------------------------- */
AudioBuffer::~AudioBuffer()
{
- free();
+ if (!m_viewing)
+ free();
}
+/* -------------------------------------------------------------------------- */
+
+AudioBuffer& AudioBuffer::operator=(const AudioBuffer& o)
+{
+ if (this == &o)
+ return *this;
+ copy(o);
+ return *this;
+}
/* -------------------------------------------------------------------------- */
+AudioBuffer& AudioBuffer::operator=(AudioBuffer&& o)
+{
+ if (this == &o)
+ return *this;
+ move(std::move(o));
+ return *this;
+}
+
+/* -------------------------------------------------------------------------- */
-float* AudioBuffer::operator [](Frame offset) const
+float* AudioBuffer::operator[](Frame offset) const
{
assert(m_data != nullptr);
assert(offset < m_size);
return m_data + (offset * m_channels);
}
-
/* -------------------------------------------------------------------------- */
-
void AudioBuffer::clear(Frame a, Frame b)
{
if (m_data == nullptr)
return;
- if (b == -1) b = m_size;
+ if (b == -1)
+ b = m_size;
std::fill_n(m_data + (a * m_channels), (b - a) * m_channels, 0.0);
}
-
/* -------------------------------------------------------------------------- */
-
-Frame AudioBuffer::countFrames() const { return m_size; }
-int AudioBuffer::countSamples() const { return m_size * m_channels; }
+Frame AudioBuffer::countFrames() const { return m_size; }
+int AudioBuffer::countSamples() const { return m_size * m_channels; }
int AudioBuffer::countChannels() const { return m_channels; }
-bool AudioBuffer::isAllocd() const { return m_data != nullptr; }
-
-
+bool AudioBuffer::isAllocd() const { return m_data != nullptr; }
/* -------------------------------------------------------------------------- */
-
float AudioBuffer::getPeak() const
{
float peak = 0.0f;
return peak;
}
-
/* -------------------------------------------------------------------------- */
-
void AudioBuffer::alloc(Frame size, int channels)
{
assert(channels <= NUM_CHANS);
free();
m_size = size;
m_channels = channels;
- m_data = new float[m_size * m_channels];
+ m_data = new float[m_size * m_channels];
clear();
}
-
/* -------------------------------------------------------------------------- */
-
void AudioBuffer::free()
{
+ if (m_data == nullptr)
+ return;
delete[] m_data;
- setData(nullptr, 0, 0);
+ m_data = nullptr;
+ m_size = 0;
+ m_channels = 0;
+ m_viewing = false;
}
-
/* -------------------------------------------------------------------------- */
-
-void AudioBuffer::setData(float* data, Frame size, int channels)
+void AudioBuffer::sum(const AudioBuffer& b, Frame framesToCopy, Frame srcOffset,
+ Frame destOffset, float gain, Pan pan)
{
- assert(channels <= NUM_CHANS);
-
- m_data = data;
- m_size = size;
- m_channels = channels;
+ copyData<Operation::SUM>(b, framesToCopy, srcOffset, destOffset, gain, pan);
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void AudioBuffer::moveData(AudioBuffer& b)
+void AudioBuffer::set(const AudioBuffer& b, Frame framesToCopy, Frame srcOffset,
+ Frame destOffset, float gain, Pan pan)
{
- assert(b.countChannels() <= NUM_CHANS);
+ copyData<Operation::SET>(b, framesToCopy, srcOffset, destOffset, gain, pan);
+}
- free();
- m_data = b[0];
- m_size = b.countFrames();
- m_channels = b.countChannels();
- b.setData(nullptr, 0, 0);
+void AudioBuffer::sum(const AudioBuffer& b, float gain, Pan pan)
+{
+ copyData<Operation::SUM>(b, -1, 0, 0, gain, pan);
}
+void AudioBuffer::set(const AudioBuffer& b, float gain, Pan pan)
+{
+ copyData<Operation::SET>(b, -1, 0, 0, gain, pan);
+}
/* -------------------------------------------------------------------------- */
-
-void AudioBuffer::copyData(const float* data, Frame frames, int channels, int offset)
+template <AudioBuffer::Operation O>
+void AudioBuffer::copyData(const AudioBuffer& b, Frame framesToCopy,
+ Frame srcOffset, Frame destOffset, float gain, Pan pan)
{
- assert(m_data != nullptr);
- assert(frames <= m_size - offset);
+ const int srcChannels = b.countChannels();
+ const int destChannels = countChannels();
+ const bool sameChannels = srcChannels == destChannels;
- if (channels < NUM_CHANS) // i.e. one channel, mono
- for (int i = offset, k = 0; i < m_size; i++, k++)
- for (int j = 0; j < countChannels(); j++)
- (*this)[i][j] = data[k];
- else
- if (channels == NUM_CHANS)
- std::copy_n(data, frames * channels, m_data + (offset * channels));
- else
- assert(false);
+ assert(m_data != nullptr);
+ assert(destOffset >= 0 && destOffset < m_size);
+ assert(srcChannels <= destChannels);
+
+ /* Make sure the amount of frames to copy lies within the current buffer
+ size. */
+
+ framesToCopy = framesToCopy == -1 ? b.countFrames() : framesToCopy;
+ framesToCopy = std::min(framesToCopy, m_size - destOffset);
+
+ /* Case 1) source has less channels than this one: brutally spread source's
+ channel 0 over this one (TODO - maybe mixdown source channels first?)
+ Case 2) source has same amount of channels: copy them 1:1. */
+
+ for (int destF = 0, srcF = srcOffset; destF < framesToCopy && destF < b.countFrames(); destF++, srcF++)
+ {
+ for (int ch = 0; ch < destChannels; ch++)
+ {
+ if constexpr (O == Operation::SUM)
+ sum(destF + destOffset, ch, b[srcF][sameChannels ? ch : 0] * gain * pan[ch]);
+ else
+ set(destF + destOffset, ch, b[srcF][sameChannels ? ch : 0] * gain * pan[ch]);
+ }
+ }
}
+/* -------------------------------------------------------------------------- */
-void AudioBuffer::copyData(const AudioBuffer& b, float gain)
+void AudioBuffer::applyGain(float g)
{
- copyData(b[0], b.countFrames(), b.countChannels());
- if (gain != 1.0f)
- applyGain(gain);
+ for (int i = 0; i < countSamples(); i++)
+ m_data[i] *= g;
}
-
/* -------------------------------------------------------------------------- */
+void AudioBuffer::sum(Frame f, int channel, float val) { (*this)[f][channel] += val; }
+void AudioBuffer::set(Frame f, int channel, float val) { (*this)[f][channel] = val; }
+
+/* -------------------------------------------------------------------------- */
-void AudioBuffer::addData(const AudioBuffer& b, float gain, Pan pan)
+void AudioBuffer::move(AudioBuffer&& o)
{
- assert(m_data != nullptr);
- assert(countFrames() <= b.countFrames());
- assert(b.countChannels() <= NUM_CHANS);
+ assert(o.countChannels() <= NUM_CHANS);
- for (int i = 0; i < countFrames(); i++)
- for (int j = 0; j < countChannels(); j++)
- (*this)[i][j] += b[i][j] * gain * pan[j];
-}
+ m_data = o.m_data;
+ m_size = o.m_size;
+ m_channels = o.m_channels;
+ m_viewing = o.m_viewing;
+ o.m_data = nullptr;
+ o.m_size = 0;
+ o.m_channels = 0;
+ o.m_viewing = false;
+}
/* -------------------------------------------------------------------------- */
-
-void AudioBuffer::applyGain(float g)
+void AudioBuffer::copy(const AudioBuffer& o)
{
- for (int i = 0; i < countSamples(); i++)
- m_data[i] *= g;
+ m_data = new float[o.m_size * o.m_channels];
+ m_size = o.m_size;
+ m_channels = o.m_channels;
+ m_viewing = o.m_viewing;
+
+ std::copy(o.m_data, o.m_data + (o.m_size * o.m_channels), m_data);
}
-}} // giada::m::
\ No newline at end of file
+
+/* -------------------------------------------------------------------------- */
+
+template void AudioBuffer::copyData<AudioBuffer::Operation::SUM>(const AudioBuffer&, Frame, Frame, Frame, float, Pan);
+template void AudioBuffer::copyData<AudioBuffer::Operation::SET>(const AudioBuffer&, Frame, Frame, Frame, float, Pan);
+} // namespace giada::m
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_AUDIO_BUFFER_H
#define G_AUDIO_BUFFER_H
-
-#include <array>
#include "core/types.h"
+#include <array>
-
-namespace giada {
-namespace m
+namespace giada::m
{
/* AudioBuffer
A class that holds a buffer filled with audio data. NOTE: currently it only
class AudioBuffer
{
public:
-
static constexpr int NUM_CHANS = 2;
using Pan = std::array<float, NUM_CHANS>;
AudioBuffer(Frame size, int channels);
+ /* AudioBuffer (3)
+ Creates an audio buffer out of a raw pointer. AudioBuffer created this way
+ is instructed not to free the owned data on destruction. */
+
+ AudioBuffer(float* data, Frame size, int channels);
+
+ /* AudioBuffer(const AudioBuffer&)
+ Copy constructor. */
+
AudioBuffer(const AudioBuffer& o);
+
+ /* AudioBuffer(AudioBuffer&&)
+ Move constructor. */
+
+ AudioBuffer(AudioBuffer&& o);
+
+ /* ~AudioBuffer
+ Destructor. */
+
~AudioBuffer();
+ /* operator = (const AudioBuffer& o)
+ Copy assignment operator. */
+
+ AudioBuffer& operator=(const AudioBuffer& o);
+
+ /* operator = (AudioBuffer&& o)
+ Move assignment operator. */
+
+ AudioBuffer& operator=(AudioBuffer&& o);
+
/* operator []
Given a frame 'offset', returns a pointer to it. This is useful for digging
inside a frame, i.e. parsing each channel. How to use it:
Also note that buffer[0] will give you a pointer to the whole internal data
array. */
- float* operator [](int offset) const;
+ float* operator[](int offset) const;
Frame countFrames() const;
- int countSamples() const;
- int countChannels() const;
- bool isAllocd() const;
+ int countSamples() const;
+ int countChannels() const;
+ bool isAllocd() const;
/* getPeak
Returns the highest value from any channel. */
-
+
float getPeak() const;
void alloc(Frame size, int channels);
void free();
- /* copyData (1)
- Copies 'frames' frames from the new 'data' into m_data, and fills m_data
- starting from frame 'offset'. The new data MUST NOT contain more than
- NUM_CHANS channels. If channels < NUM_CHANS, they will be spread over the
- stereo buffer. */
-
- void copyData(const float* data, Frame frames, int channels=NUM_CHANS, int offset=0);
+ /* sum, set (1)
+ Merges (sum) or copies (set) 'framesToCopy' frames of buffer 'b' onto this
+ one. If 'framesToCopy' is -1 the whole buffer will be copied. If 'b' has
+ less channels than this one, they will be spread over the current ones.
+ Buffer 'b' MUST NOT contain more channels than this one. */
- /* copyData (2)
- Copies buffer 'b' onto this one. If 'b' has less channels than this one,
- they will be spread over the current ones. Buffer 'b' MUST NOT contain more
- channels than this one. */
+ void sum(const AudioBuffer& b, Frame framesToCopy = -1, Frame srcOffset = 0,
+ Frame destOffset = 0, float gain = 1.0f, Pan pan = {1.0f, 1.0f});
+ void set(const AudioBuffer& b, Frame framesToCopy = -1, Frame srcOffset = 0,
+ Frame destOffset = 0, float gain = 1.0f, Pan pan = {1.0f, 1.0f});
- void copyData(const AudioBuffer& b, float gain=1.0f);
+ /* sum, set (2)
+ Same as sum, set (1) without boundaries or offsets: it just copies as much
+ as possibile. */
- /* addData
- Merges audio data from buffer 'b' onto this one. Applies optional gain and
- pan if needed. */
-
- void addData(const AudioBuffer& b, float gain=1.0f, Pan pan={1.0f, 1.0f});
-
- /* setData
- Views 'data' as new m_data. Makes sure not to delete the data 'data' points
- to while using it. Set it back to nullptr when done. */
-
- void setData(float* data, Frame size, int channels);
-
- /* moveData
- Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer.
- TODO - add move constructor instead! */
-
- void moveData(AudioBuffer& b);
+ void sum(const AudioBuffer& b, float gain = 1.0f, Pan pan = {1.0f, 1.0f});
+ void set(const AudioBuffer& b, float gain = 1.0f, Pan pan = {1.0f, 1.0f});
/* clear
Clears the internal data by setting all bytes to 0.0f. Optional parameters
'a' and 'b' set the range. */
-
- void clear(Frame a=0, Frame b=-1);
+
+ void clear(Frame a = 0, Frame b = -1);
void applyGain(float g);
private:
+ enum class Operation
+ {
+ SUM,
+ SET
+ };
+
+ template <Operation O = Operation::SET>
+ void copyData(const AudioBuffer& b, Frame framesToCopy = -1,
+ Frame srcOffset = 0, Frame destOffset = 0, float gain = 1.0f,
+ Pan pan = {1.0f, 1.0f});
+
+ void move(AudioBuffer&& o);
+ void copy(const AudioBuffer& o);
+ void sum(Frame f, int channel, float val);
+ void set(Frame f, int channel, float val);
float* m_data;
Frame m_size;
int m_channels;
+ bool m_viewing;
};
-
-}} // giada::m::
+} // namespace giada::m
#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/channels/state.h"
#include "audioReceiver.h"
+#include "core/channels/channel.h"
-
-namespace giada {
-namespace m
+namespace giada::m::audioReceiver
{
-AudioReceiver::AudioReceiver(ChannelState* c, const conf::Conf& conf)
-: state (std::make_unique<AudioReceiverState>(conf))
-, m_channelState(c)
+Data::Data(const patch::Channel& p)
+: inputMonitor(p.inputMonitor)
+, overdubProtection(p.overdubProtection)
{
}
-
/* -------------------------------------------------------------------------- */
-
-AudioReceiver::AudioReceiver(const patch::Channel& p, ChannelState* c)
-: state (std::make_unique<AudioReceiverState>(p))
-, m_channelState(c)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-AudioReceiver::AudioReceiver(const AudioReceiver& o, ChannelState* c)
-: state (std::make_unique<AudioReceiverState>(*o.state))
-, m_channelState(c)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void AudioReceiver::render(const AudioBuffer& in) const
+void render(const channel::Data& ch, const AudioBuffer& in)
{
/* If armed 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). */
- bool armed = m_channelState->armed.load();
- bool inputMonitor = state->inputMonitor.load();
-
- if (armed && inputMonitor)
- m_channelState->buffer.addData(in); // add, don't overwrite
+ if (ch.armed && ch.audioReceiver->inputMonitor)
+ ch.buffer->audio.set(in, /*gain=*/1.0f); // add, don't overwrite
}
-}} // giada::m::
+} // namespace giada::m::audioReceiver
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_AUDIO_RECEIVER_H
#define G_CHANNEL_AUDIO_RECEIVER_H
-
-#include <memory>
-
-
-namespace giada {
-namespace m
+namespace giada::m
{
-namespace conf
+class AudioBuffer;
+}
+namespace giada::m::channel
{
-struct Conf;
+struct Data;
}
-namespace patch
+namespace giada::m::patch
{
-struct Channel;
+struct Channel;
}
-class AudioBuffer;
-struct ChannelState;
-struct AudioReceiverState;
-
-/* AudioReceiver
-Operates on input audio streams for audio recording and input monitor. */
-
-class AudioReceiver
+namespace giada::m::audioReceiver
{
-public:
-
- AudioReceiver(ChannelState*, const conf::Conf&);
- AudioReceiver(const patch::Channel&, ChannelState*);
- AudioReceiver(const AudioReceiver&, ChannelState* c=nullptr);
-
- void render(const AudioBuffer& in) const;
-
- /* state
- Pointer to mutable AudioReceiverState state. */
-
- std::unique_ptr<AudioReceiverState> state;
-
-private:
+struct Data
+{
+ Data() = default;
+ Data(const patch::Channel& p);
+ Data(const Data& o) = default;
- ChannelState* m_channelState;
+ bool inputMonitor;
+ bool overdubProtection;
};
-}} // giada::m::
+void render(const channel::Data& ch, const AudioBuffer& in);
+} // namespace giada::m::audioReceiver
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/channels/state.h"
+#include "channel.h"
#include "core/mixerHandler.h"
#include "core/plugins/pluginHost.h"
-#include "channel.h"
-
+#include "core/plugins/pluginManager.h"
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-Channel::Channel(ChannelType type, ID id, ID columnId, Frame bufferSize, const conf::Conf& conf)
-: id (id)
-, state (std::make_unique<ChannelState>(id, bufferSize))
-, midiLighter(state.get())
-, m_type (type)
-, m_columnId (columnId)
+namespace
{
- switch (m_type) {
-
- case ChannelType::SAMPLE:
- samplePlayer.emplace(state.get());
- audioReceiver.emplace(state.get(), conf);
- sampleActionRecorder.emplace(state.get(), samplePlayer->state.get());
- break;
-
- case ChannelType::PREVIEW:
- samplePlayer.emplace(state.get());
- break;
-
- case ChannelType::MIDI:
- midiController.emplace(state.get());
-#ifdef WITH_VST
- midiReceiver.emplace(state.get());
-#endif
- midiSender.emplace(state.get());
- midiActionRecorder.emplace(state.get());
- break;
-
- default: break;
- }
-}
+AudioBuffer::Pan calcPanning_(float pan)
+{
+ /* TODO - precompute the AudioBuffer::Pan when pan value changes instead of
+ building it on the fly. */
+ /* Center pan (0.5f)? Pass-through. */
-/* -------------------------------------------------------------------------- */
+ if (pan == 0.5f)
+ return {1.0f, 1.0f};
+ return {1.0f - pan, pan};
+}
+/* -------------------------------------------------------------------------- */
-Channel::Channel(const Channel& o)
-: id (o.id)
-#ifdef WITH_VST
-, pluginIds (o.pluginIds)
-#endif
-, state (std::make_unique<ChannelState>(*o.state))
-, midiLearner (o.midiLearner)
-, midiLighter (o.midiLighter, state.get())
-, m_type (o.m_type)
-, m_columnId (o.m_columnId)
+void react_(Data& d, const eventDispatcher::Event& e)
{
- switch (m_type) {
-
- case ChannelType::SAMPLE:
- samplePlayer.emplace(o.samplePlayer.value(), state.get());
- audioReceiver.emplace(o.audioReceiver.value(), state.get());
- sampleActionRecorder.emplace(o.sampleActionRecorder.value(), state.get(), samplePlayer->state.get());
- break;
-
- case ChannelType::PREVIEW:
- samplePlayer.emplace(o.samplePlayer.value(), state.get());
- break;
-
- case ChannelType::MIDI:
- midiController.emplace(o.midiController.value(), state.get());
-#ifdef WITH_VST
- midiReceiver.emplace(o.midiReceiver.value(), state.get());
-#endif
- midiSender.emplace(o.midiSender.value(), state.get());
- midiActionRecorder.emplace(o.midiActionRecorder.value(), state.get());
- break;
-
- default: break;
+ switch (e.type)
+ {
+ case eventDispatcher::EventType::CHANNEL_VOLUME:
+ d.volume = std::get<float>(e.data);
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_PAN:
+ d.pan = std::get<float>(e.data);
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_MUTE:
+ d.mute = !d.mute;
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_TOGGLE_ARM:
+ d.armed = !d.armed;
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_SOLO:
+ d.solo = !d.solo;
+ m::mh::updateSoloCount();
+ break;
+
+ default:
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
-Channel::Channel(const patch::Channel& p, Frame bufferSize)
-: id (p.id)
-#ifdef WITH_VST
-, pluginIds (p.pluginIds)
-#endif
-, state (std::make_unique<ChannelState>(p, bufferSize))
-, midiLearner (p)
-, midiLighter (p, state.get())
-, m_type (p.type)
-, m_columnId (p.columnId)
+void renderMasterOut_(const Data& d, AudioBuffer& out)
{
- switch (m_type) {
-
- case ChannelType::SAMPLE:
- samplePlayer.emplace(p, state.get());
- audioReceiver.emplace(p, state.get());
- sampleActionRecorder.emplace(state.get(), samplePlayer->state.get());
- break;
-
- case ChannelType::PREVIEW:
- samplePlayer.emplace(p, state.get());
- break;
-
- case ChannelType::MIDI:
- midiController.emplace(state.get());
+ d.buffer->audio.set(out, /*gain=*/1.0f);
#ifdef WITH_VST
- midiReceiver.emplace(p, state.get());
+ if (d.plugins.size() > 0)
+ pluginHost::processStack(d.buffer->audio, d.plugins, nullptr);
#endif
- midiSender.emplace(p, state.get());
- midiActionRecorder.emplace(state.get());
- break;
-
- default: break;
- }
+ out.set(d.buffer->audio, d.volume);
}
-
/* -------------------------------------------------------------------------- */
-
-void Channel::parse(const mixer::EventBuffer& events, bool audible) const
+void renderMasterIn_(const Data& d, AudioBuffer& in)
{
- for (const mixer::Event& e : events) {
-
- if (e.action.channelId > 0 && e.action.channelId != id)
- continue;
-
- parse(e);
- midiLighter.parse(e, audible);
-
- if (midiController) midiController->parse(e);
#ifdef WITH_VST
- if (midiReceiver) midiReceiver->parse(e);
+ if (d.plugins.size() > 0)
+ pluginHost::processStack(in, d.plugins, nullptr);
#endif
- if (midiSender) midiSender->parse(e);
- if (samplePlayer) samplePlayer->parse(e);
- if (midiActionRecorder) midiActionRecorder->parse(e);
- if (sampleActionRecorder && samplePlayer && samplePlayer->hasWave())
- sampleActionRecorder->parse(e);
- }
}
-
/* -------------------------------------------------------------------------- */
-
-void Channel::advance(Frame bufferSize) const
+void renderChannel_(const Data& d, AudioBuffer& out, AudioBuffer& in, bool audible)
{
- /* TODO - this is used only to advance samplePlayer for its quantizer. Use
- this to render actions in the future. */
-
- if (samplePlayer) samplePlayer->advance(bufferSize);
-}
+ d.buffer->audio.clear();
+ if (d.samplePlayer)
+ samplePlayer::render(d);
+ if (d.audioReceiver)
+ audioReceiver::render(d, in);
-/* -------------------------------------------------------------------------- */
+ /* If MidiReceiver exists, let it process the plug-in stack, as it can
+ contain plug-ins that take MIDI events (i.e. synths). Otherwise process the
+ plug-in stack internally with no MIDI events. */
+#ifdef WITH_VST
+ if (d.midiReceiver)
+ midiReceiver::render(d);
+ else if (d.plugins.size() > 0)
+ pluginHost::processStack(d.buffer->audio, d.plugins, nullptr);
+#endif
-void Channel::render(AudioBuffer* out, AudioBuffer* in, bool audible) const
-{
- if (id == mixer::MASTER_OUT_CHANNEL_ID)
- renderMasterOut(*out);
- else
- if (id == mixer::MASTER_IN_CHANNEL_ID)
- renderMasterIn(*in);
- else
- renderChannel(*out, *in, audible);
+ if (audible)
+ out.sum(d.buffer->audio, d.volume * d.volume_i, calcPanning_(d.pan));
}
-
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void Channel::parse(const mixer::Event& e) const
+Buffer::Buffer(Frame bufferSize)
+: audio(bufferSize, G_MAX_IO_CHANS)
{
- switch (e.type) {
-
- case mixer::EventType::CHANNEL_VOLUME:
- state->volume.store(e.action.event.getVelocityFloat()); break;
-
- case mixer::EventType::CHANNEL_PAN:
- state->pan.store(e.action.event.getVelocityFloat()); break;
-
- case mixer::EventType::CHANNEL_MUTE:
- state->mute.store(!state->mute.load()); break;
-
- case mixer::EventType::CHANNEL_TOGGLE_ARM:
- state->armed.store(!state->armed.load()); break;
-
- case mixer::EventType::CHANNEL_SOLO:
- state->solo.store(!state->solo.load());
- m::mh::updateSoloCount();
- break;
-
- default: break;
- }
}
-
/* -------------------------------------------------------------------------- */
-
-void Channel::renderMasterOut(AudioBuffer& out) const
+Data::Data(ChannelType type, ID id, ID columnId, State& state, Buffer& buffer)
+: state(&state)
+, buffer(&buffer)
+, id(id)
+, type(type)
+, columnId(columnId)
+, volume(G_DEFAULT_VOL)
+, volume_i(G_DEFAULT_VOL)
+, pan(G_DEFAULT_PAN)
+, mute(false)
+, solo(false)
+, armed(false)
+, key(0)
+, hasActions(false)
+, height(G_GUI_UNIT)
{
- state->buffer.copyData(out);
+ switch (type)
+ {
+ case ChannelType::SAMPLE:
+ samplePlayer.emplace(&state.resampler.value());
+ sampleReactor.emplace(id);
+ audioReceiver.emplace();
+ sampleActionRecorder.emplace();
+ break;
+
+ case ChannelType::PREVIEW:
+ samplePlayer.emplace(&state.resampler.value());
+ sampleReactor.emplace(id);
+ break;
+
+ case ChannelType::MIDI:
+ midiController.emplace();
+ midiSender.emplace();
+ midiActionRecorder.emplace();
#ifdef WITH_VST
- if (pluginIds.size() > 0)
- pluginHost::processStack(state->buffer, pluginIds, nullptr);
+ midiReceiver.emplace();
#endif
- out.copyData(state->buffer, state->volume.load());
-}
+ break;
+ default:
+ break;
+ }
+}
/* -------------------------------------------------------------------------- */
-
-void Channel::renderMasterIn(AudioBuffer& in) const
-{
+Data::Data(const patch::Channel& p, State& state, Buffer& buffer, float samplerateRatio)
+: state(&state)
+, buffer(&buffer)
+, id(p.id)
+, type(p.type)
+, columnId(p.columnId)
+, volume(p.volume)
+, volume_i(G_DEFAULT_VOL)
+, pan(p.pan)
+, mute(p.mute)
+, solo(p.solo)
+, armed(p.armed)
+, key(p.key)
+, hasActions(p.hasActions)
+, name(p.name)
+, height(p.height)
#ifdef WITH_VST
- if (pluginIds.size() > 0)
- pluginHost::processStack(in, pluginIds, nullptr);
+, plugins(pluginManager::hydratePlugins(p.pluginIds))
#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::renderChannel(AudioBuffer& out, AudioBuffer& in, bool audible) const
+, midiLearner(p)
{
- state->buffer.clear();
-
- if (samplePlayer) samplePlayer->render(out);
- if (audioReceiver) audioReceiver->render(in);
-
- /* If MidiReceiver exists, let it process the plug-in stack, as it can
- contain plug-ins that take MIDI events (i.e. synths). Otherwise process the
- plug-in stack internally with no MIDI events. */
-
+ state.readActions.store(p.readActions);
+ state.recStatus.store(p.readActions ? ChannelStatus::PLAY : ChannelStatus::OFF);
+
+ switch (type)
+ {
+ case ChannelType::SAMPLE:
+ samplePlayer.emplace(p, samplerateRatio, &state.resampler.value());
+ sampleReactor.emplace(id);
+ audioReceiver.emplace(p);
+ sampleActionRecorder.emplace();
+ break;
+
+ case ChannelType::PREVIEW:
+ samplePlayer.emplace(p, samplerateRatio, &state.resampler.value());
+ sampleReactor.emplace(id);
+ break;
+
+ case ChannelType::MIDI:
+ midiController.emplace();
+ midiSender.emplace(p);
+ midiActionRecorder.emplace();
#ifdef WITH_VST
- if (midiReceiver)
- midiReceiver->render(pluginIds);
- else
- if (pluginIds.size() > 0)
- pluginHost::processStack(state->buffer, pluginIds, nullptr);
+ midiReceiver.emplace();
#endif
+ break;
- if (audible)
- out.addData(state->buffer, state->volume.load() * state->volume_i, calcPanning());
+ default:
+ break;
+ }
}
-
/* -------------------------------------------------------------------------- */
-
-AudioBuffer::Pan Channel::calcPanning() const
+bool Data::operator==(const Data& other)
{
- /* TODO - precompute the AudioBuffer::Pan when pan value changes instead of
- building it on the fly. */
-
- float pan = state->pan.load();
-
- /* Center pan (0.5f)? Pass-through. */
-
- if (pan == 0.5f) return { 1.0f, 1.0f };
- return { 1.0f - pan, pan };
+ return id == other.id;
}
-
/* -------------------------------------------------------------------------- */
+bool Data::isInternal() const
+{
+ return type == ChannelType::MASTER || type == ChannelType::PREVIEW;
+}
-ID Channel::getColumnId() const { return m_columnId; }
-ChannelType Channel::getType() const { return m_type; }
+bool Data::isMuted() const
+{
+ /* Internals can't be muted. */
+ return !isInternal() && mute;
+}
+bool Data::canInputRec() const
+{
+ if (type != ChannelType::SAMPLE)
+ return false;
-/* -------------------------------------------------------------------------- */
+ bool hasWave = samplePlayer->hasWave();
+ bool isProtected = audioReceiver->overdubProtection;
+ bool canOverdub = !hasWave || (hasWave && !isProtected);
+ return armed && canOverdub;
+}
-bool Channel::isInternal() const
+bool Data::canActionRec() const
{
- return m_type == ChannelType::MASTER || m_type == ChannelType::PREVIEW;
+ return hasWave() && !samplePlayer->isAnyLoopMode();
}
-
-bool Channel::isMuted() const
+bool Data::hasWave() const
{
- /* Internals can't be muted. */
- return !isInternal() && state->mute.load() == true;
+ return samplePlayer && samplePlayer->hasWave();
}
+bool Data::isPlaying() const
+{
+ ChannelStatus s = state->playStatus.load();
+ return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING;
+}
-bool Channel::canInputRec() const
+bool Data::isReadingActions() const
{
- if (m_type != ChannelType::SAMPLE)
- return false;
+ ChannelStatus s = state->recStatus.load();
+ return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING;
+}
- bool armed = state->armed.load();
- bool hasWave = samplePlayer->hasWave();
- bool isProtected = audioReceiver->state->overdubProtection.load();
- bool canOverdub = !hasWave || (hasWave && !isProtected);
+/* -------------------------------------------------------------------------- */
- return armed && canOverdub;
+void advance(const Data& d, const sequencer::EventBuffer& events)
+{
+ for (const sequencer::Event& e : events)
+ {
+ if (d.midiController)
+ midiController::advance(d, e);
+ if (d.samplePlayer)
+ samplePlayer::advance(d, e);
+ if (d.midiSender)
+ midiSender::advance(d, e);
+#ifdef WITH_VST
+ if (d.midiReceiver)
+ midiReceiver::advance(d, e);
+#endif
+ }
}
+/* -------------------------------------------------------------------------- */
-bool Channel::canActionRec() const
+void react(Data& d, const eventDispatcher::EventBuffer& events, bool audible)
{
- return hasWave() && !samplePlayer->state->isAnyLoopMode();
+ for (const eventDispatcher::Event& e : events)
+ {
+ if (e.channelId > 0 && e.channelId != d.id)
+ continue;
+
+ react_(d, e);
+ midiLighter::react(d, e, audible);
+
+ if (d.midiController)
+ midiController::react(d, e);
+ if (d.midiSender)
+ midiSender::react(d, e);
+ if (d.samplePlayer)
+ samplePlayer::react(d, e);
+ if (d.midiActionRecorder)
+ midiActionRecorder::react(d, e);
+ if (d.sampleActionRecorder)
+ sampleActionRecorder::react(d, e);
+ if (d.sampleReactor)
+ sampleReactor::react(d, e);
+#ifdef WITH_VST
+ if (d.midiReceiver)
+ midiReceiver::react(d, e);
+#endif
+ }
}
+/* -------------------------------------------------------------------------- */
-bool Channel::hasWave() const
+void render(const Data& d, AudioBuffer* out, AudioBuffer* in, bool audible)
{
- return m_type == ChannelType::SAMPLE && samplePlayer->hasWave();
+ if (d.id == mixer::MASTER_OUT_CHANNEL_ID)
+ renderMasterOut_(d, *out);
+ else if (d.id == mixer::MASTER_IN_CHANNEL_ID)
+ renderMasterIn_(d, *in);
+ else
+ renderChannel_(d, *out, *in, audible);
}
-}} // giada::m::
+} // namespace giada::m::channel
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_H
#define G_CHANNEL_H
-
#include <optional>
-#include "core/const.h"
-#include "core/mixer.h"
-#include "core/channels/state.h"
-#include "core/channels/samplePlayer.h"
-#include "core/channels/audioReceiver.h"
#ifdef WITH_VST
-#include "core/channels/midiReceiver.h"
+#include "deps/juce-config.h"
#endif
-#include "core/channels/midiLearner.h"
-#include "core/channels/midiSender.h"
+#include "core/audioBuffer.h"
+#include "core/channels/audioReceiver.h"
+#include "core/channels/midiActionRecorder.h"
#include "core/channels/midiController.h"
+#include "core/channels/midiLearner.h"
#include "core/channels/midiLighter.h"
+#include "core/channels/midiSender.h"
#include "core/channels/sampleActionRecorder.h"
-#include "core/channels/midiActionRecorder.h"
-
+#include "core/channels/samplePlayer.h"
+#include "core/const.h"
+#include "core/eventDispatcher.h"
+#include "core/mixer.h"
+#include "core/resampler.h"
+#include "core/sequencer.h"
+#ifdef WITH_VST
+#include "core/channels/midiReceiver.h"
+#endif
-namespace giada {
-namespace m
-{
-namespace conf
+namespace giada::m
{
-struct Conf;
+class Plugin;
}
-class Channel final
+namespace giada::m::channel
{
-public:
-
- Channel(ChannelType t, ID id, ID columnId, Frame bufferSize, const conf::Conf& c);
- Channel(const Channel&);
- Channel(const patch::Channel& p, Frame bufferSize);
- Channel(Channel&&) = default;
- Channel& operator=(const Channel&) = delete;
- Channel& operator=(Channel&&) = delete;
- ~Channel() = default;
-
- /* parse
- Parses live events. */
-
- void parse(const mixer::EventBuffer& e, bool audible) const;
-
- /* advance
- Processes static events (e.g. actions) in the current block. */
-
- void advance(Frame bufferSize) const;
-
- /* render
- Renders audio data to I/O buffers. */
-
- void render(AudioBuffer* out, AudioBuffer* in, bool audible) const;
+struct State
+{
+ WeakAtomic<Frame> tracker = 0;
+ WeakAtomic<ChannelStatus> playStatus = ChannelStatus::OFF;
+ WeakAtomic<ChannelStatus> recStatus = ChannelStatus::OFF;
+ WeakAtomic<bool> readActions = false;
+ bool rewinding = false;
+ Frame offset = 0;
+
+ /* Optional resampler for sample-based channels. Unfortunately a Resampler
+ object (based on libsamplerate) doesn't like to get copied while rendering
+ audio, so can't live inside WaveReader object (which is copied on model
+ changes by the Swapper mechanism). Let's put it in the shared state here. */
+
+ std::optional<Resampler> resampler = {};
+};
- bool isInternal() const;
- bool isMuted() const;
- bool canInputRec() const;
- bool canActionRec() const;
- bool hasWave() const;
- ID getColumnId() const;
- ChannelType getType() const;
-
- ID id;
+struct Buffer
+{
+ Buffer(Frame bufferSize);
+ AudioBuffer audio;
#ifdef WITH_VST
- std::vector<ID> pluginIds;
+ juce::MidiBuffer midi;
#endif
+};
- /* state
- Pointer to mutable Channel state. */
-
- std::unique_ptr<ChannelState> state;
+struct Data
+{
+ Data(ChannelType t, ID id, ID columnId, State& state, Buffer& buffer);
+ Data(const patch::Channel& p, State& state, Buffer& buffer, float samplerateRatio);
+ Data(const Data& o) = default;
+ Data(Data&& o) = default;
+ Data& operator=(const Data&) = default;
+ Data& operator=(Data&&) = default;
+
+ bool operator==(const Data&);
+
+ bool isPlaying() const;
+ bool isReadingActions() const;
+ bool isInternal() const;
+ bool isMuted() const;
+ bool canInputRec() const;
+ bool canActionRec() const;
+ bool hasWave() const;
+
+ State* state;
+ Buffer* buffer;
+ ID id;
+ ChannelType type;
+ ID columnId;
+ float volume;
+ float volume_i; // Internal volume used for velocity-drives-volume mode on Sample Channels
+ float pan;
+ bool mute;
+ bool solo;
+ bool armed;
+ int key;
+ bool hasActions;
+ std::string name;
+ Pixel height;
+#ifdef WITH_VST
+ std::vector<Plugin*> plugins;
+#endif
- MidiLearner midiLearner;
- MidiLighter midiLighter;
+ midiLearner::Data midiLearner;
+ midiLighter::Data midiLighter;
- std::optional<SamplePlayer> samplePlayer;
- std::optional<AudioReceiver> audioReceiver;
- std::optional<MidiController> midiController;
+ std::optional<samplePlayer::Data> samplePlayer;
+ std::optional<sampleReactor::Data> sampleReactor;
+ std::optional<audioReceiver::Data> audioReceiver;
+ std::optional<midiController::Data> midiController;
#ifdef WITH_VST
- std::optional<MidiReceiver> midiReceiver;
+ std::optional<midiReceiver::Data> midiReceiver;
#endif
- std::optional<MidiSender> midiSender;
- std::optional<SampleActionRecorder> sampleActionRecorder;
- std::optional<MidiActionRecorder> midiActionRecorder;
+ std::optional<midiSender::Data> midiSender;
+ std::optional<sampleActionRecorder::Data> sampleActionRecorder;
+ std::optional<midiActionRecorder::Data> midiActionRecorder;
+};
-private:
+/* advance
+Advances internal state by processing static events (e.g. pre-recorded
+actions or sequencer events) in the current block. */
- void parse(const mixer::Event& e) const;
+void advance(const Data& d, const sequencer::EventBuffer& e);
- void renderMasterOut(AudioBuffer& out) const;
- void renderMasterIn(AudioBuffer& in) const;
- void renderChannel(AudioBuffer& out, AudioBuffer& in, bool audible) const;
+/* react
+Reacts to live events coming from the EventDispatcher (human events) and
+updates itself accordingly. */
- AudioBuffer::Pan calcPanning() const;
+void react(Data& d, const eventDispatcher::EventBuffer& e, bool audible);
- ChannelType m_type;
- ID m_columnId;
-};
-}} // giada::m::
+/* render
+Renders audio data to I/O buffers. */
+void render(const Data& d, AudioBuffer* out, AudioBuffer* in, bool audible);
+} // namespace giada::m::channel
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "utils/fs.h"
+#include "channelManager.h"
+#include "core/action.h"
#include "core/channels/channel.h"
#include "core/channels/samplePlayer.h"
+#include "core/conf.h"
#include "core/const.h"
+#include "core/idManager.h"
#include "core/kernelAudio.h"
-#include "core/patch.h"
#include "core/mixer.h"
-#include "core/idManager.h"
-#include "core/wave.h"
-#include "core/waveManager.h"
+#include "core/model/model.h"
+#include "core/patch.h"
+#include "core/plugins/plugin.h"
#include "core/plugins/pluginHost.h"
#include "core/plugins/pluginManager.h"
-#include "core/plugins/plugin.h"
-#include "core/action.h"
#include "core/recorderHandler.h"
-#include "channelManager.h"
-
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include "utils/fs.h"
+#include <cassert>
-namespace giada {
-namespace m {
-namespace channelManager
+namespace giada::m::channelManager
{
namespace
{
IdManager channelId_;
-} // {anonymous}
+/* -------------------------------------------------------------------------- */
+
+channel::State& makeState_(ChannelType type)
+{
+ std::unique_ptr<channel::State> state = std::make_unique<channel::State>();
+
+ if (type == ChannelType::SAMPLE || type == ChannelType::PREVIEW)
+ state->resampler = Resampler(static_cast<Resampler::Quality>(conf::conf.rsmpQuality), G_MAX_IO_CHANS);
+
+ model::add(std::move(state));
+ return model::back<channel::State>();
+}
+
+/* -------------------------------------------------------------------------- */
+
+channel::Buffer& makeBuffer_()
+{
+ model::add(std::make_unique<channel::Buffer>(kernelAudio::getRealBufSize()));
+ return model::back<channel::Buffer>();
+}
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init()
{
channelId_ = IdManager();
}
-
/* -------------------------------------------------------------------------- */
-
-std::unique_ptr<Channel> create(ChannelType type, ID columnId, const conf::Conf& conf)
+channel::Data create(ID channelId, ChannelType type, ID columnId)
{
- std::unique_ptr<Channel> ch = std::make_unique<Channel>(type,
- channelId_.get(), columnId, kernelAudio::getRealBufSize(), conf);
-
- return ch;
-}
+ channel::Data out = channel::Data(type, channelId_.generate(channelId),
+ columnId, makeState_(type), makeBuffer_());
+ if (out.audioReceiver)
+ out.audioReceiver->overdubProtection = conf::conf.overdubProtectionDefaultOn;
-/* -------------------------------------------------------------------------- */
+ return out;
+}
+/* -------------------------------------------------------------------------- */
-std::unique_ptr<Channel> create(const Channel& o)
+channel::Data create(const channel::Data& o)
{
- std::unique_ptr<Channel> ch = std::make_unique<Channel>(o);
- ID id = channelId_.get();
- ch->id = id;
- ch->state->id = id;
- return ch;
-}
+ channel::Data out = channel::Data(o);
+ out.id = channelId_.generate();
+ out.state = &makeState_(o.type);
+ out.buffer = &makeBuffer_();
-/* -------------------------------------------------------------------------- */
+ return out;
+}
+/* -------------------------------------------------------------------------- */
-std::unique_ptr<Channel> deserializeChannel(const patch::Channel& pch, int bufferSize)
+channel::Data deserializeChannel(const patch::Channel& pch, float samplerateRatio)
{
channelId_.set(pch.id);
- return std::make_unique<Channel>(pch, bufferSize);
+ return channel::Data(pch, makeState_(pch.type), makeBuffer_(), samplerateRatio);
}
-
/* -------------------------------------------------------------------------- */
-
-const patch::Channel serializeChannel(const Channel& c)
+const patch::Channel serializeChannel(const channel::Data& c)
{
patch::Channel pc;
#ifdef WITH_VST
- for (ID pid : c.pluginIds)
- pc.pluginIds.push_back(pid);
+ for (const Plugin* p : c.plugins)
+ pc.pluginIds.push_back(p->id);
#endif
pc.id = c.id;
- pc.type = c.getType();
- pc.columnId = c.getColumnId();
- pc.height = c.state->height;
- pc.name = c.state->name;
- pc.key = c.state->key.load();
- pc.mute = c.state->mute.load();
- pc.solo = c.state->solo.load();
- pc.volume = c.state->volume.load();
- pc.pan = c.state->pan.load();
- pc.hasActions = c.state->hasActions;
- pc.readActions = c.state->readActions.load();
- pc.armed = c.state->armed.load();
- pc.midiIn = c.midiLearner.state->enabled.load();
- pc.midiInFilter = c.midiLearner.state->filter.load();
- pc.midiInKeyPress = c.midiLearner.state->keyPress.getValue();
- pc.midiInKeyRel = c.midiLearner.state->keyRelease.getValue();
- pc.midiInKill = c.midiLearner.state->kill.getValue();
- pc.midiInArm = c.midiLearner.state->arm.getValue();
- pc.midiInVolume = c.midiLearner.state->volume.getValue();
- pc.midiInMute = c.midiLearner.state->mute.getValue();
- pc.midiInSolo = c.midiLearner.state->solo.getValue();
- pc.midiInReadActions = c.midiLearner.state->readActions.getValue();
- pc.midiInPitch = c.midiLearner.state->pitch.getValue();
- pc.midiOutL = c.midiLighter.state->enabled.load();
- pc.midiOutLplaying = c.midiLighter.state->playing.getValue();
- pc.midiOutLmute = c.midiLighter.state->mute.getValue();
- pc.midiOutLsolo = c.midiLighter.state->solo.getValue();
-
- if (c.getType() == ChannelType::SAMPLE) {
+ pc.type = c.type;
+ pc.columnId = c.columnId;
+ pc.height = c.height;
+ pc.name = c.name;
+ pc.key = c.key;
+ pc.mute = c.mute;
+ pc.solo = c.solo;
+ pc.volume = c.volume;
+ pc.pan = c.pan;
+ pc.hasActions = c.hasActions;
+ pc.readActions = c.state->readActions.load();
+ pc.armed = c.armed;
+ pc.midiIn = c.midiLearner.enabled;
+ pc.midiInFilter = c.midiLearner.filter;
+ pc.midiInKeyPress = c.midiLearner.keyPress.getValue();
+ pc.midiInKeyRel = c.midiLearner.keyRelease.getValue();
+ pc.midiInKill = c.midiLearner.kill.getValue();
+ pc.midiInArm = c.midiLearner.arm.getValue();
+ pc.midiInVolume = c.midiLearner.volume.getValue();
+ pc.midiInMute = c.midiLearner.mute.getValue();
+ pc.midiInSolo = c.midiLearner.solo.getValue();
+ pc.midiInReadActions = c.midiLearner.readActions.getValue();
+ pc.midiInPitch = c.midiLearner.pitch.getValue();
+ pc.midiOutL = c.midiLighter.enabled;
+ pc.midiOutLplaying = c.midiLighter.playing.getValue();
+ pc.midiOutLmute = c.midiLighter.mute.getValue();
+ pc.midiOutLsolo = c.midiLighter.solo.getValue();
+
+ if (c.type == ChannelType::SAMPLE)
+ {
pc.waveId = c.samplePlayer->getWaveId();
- pc.mode = c.samplePlayer->state->mode.load();
- pc.begin = c.samplePlayer->state->begin.load();
- pc.end = c.samplePlayer->state->end.load();
- pc.pitch = c.samplePlayer->state->pitch.load();
- pc.shift = c.samplePlayer->state->shift.load();
- pc.midiInVeloAsVol = c.samplePlayer->state->velocityAsVol.load();
- pc.inputMonitor = c.audioReceiver->state->inputMonitor.load();
- pc.overdubProtection = c.audioReceiver->state->overdubProtection.load();
-
+ pc.mode = c.samplePlayer->mode;
+ pc.begin = c.samplePlayer->begin;
+ pc.end = c.samplePlayer->end;
+ pc.pitch = c.samplePlayer->pitch;
+ pc.shift = c.samplePlayer->shift;
+ pc.midiInVeloAsVol = c.samplePlayer->velocityAsVol;
+ pc.inputMonitor = c.audioReceiver->inputMonitor;
+ pc.overdubProtection = c.audioReceiver->overdubProtection;
}
- else
- if (c.getType() == ChannelType::MIDI) {
- pc.midiOut = c.midiSender->state->enabled.load();
- pc.midiOutChan = c.midiSender->state->filter.load();
+ else if (c.type == ChannelType::MIDI)
+ {
+ pc.midiOut = c.midiSender->enabled;
+ pc.midiOutChan = c.midiSender->filter;
}
return pc;
}
-}}} // giada::m::channelManager
+} // namespace giada::m::channelManager
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MANAGER_H
#define G_CHANNEL_MANAGER_H
-
-#include <memory>
#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-namespace conf
+struct Data;
+}
+namespace giada::m::conf
{
struct Conf;
}
-namespace patch
+namespace giada::m::patch
{
struct Channel;
}
-class Channel;
-struct ChannelState;
-namespace channelManager
+namespace giada::m::channelManager
{
/* init
Initializes internal data. */
-
+
void init();
/* create (1)
-Creates a new Channel from scratch. */
+Creates a new channel. If channelId == 0 generates a new ID, reuse the one
+passed in otherwise. */
-std::unique_ptr<Channel> create(ChannelType type, ID columnId, const conf::Conf& conf);
+channel::Data create(ID channelId, ChannelType type, ID columnId);
/* create (2)
-Creates a new Channel given an existing one (i.e. clone). */
+Creates a new channel given an existing one (i.e. clone). */
-std::unique_ptr<Channel> create(const Channel& ch);
+channel::Data create(const channel::Data& ch);
/* (de)serializeWave
-Creates a new Channel given the patch raw data and vice versa. */
-
-std::unique_ptr<Channel> deserializeChannel(const patch::Channel& c, int bufferSize);
-const patch::Channel serializeChannel(const Channel& c);
-}}} // giada::m::channelManager
+Creates a new channel given the patch raw data and vice versa. */
+channel::Data deserializeChannel(const patch::Channel& c, float samplerateRatio);
+const patch::Channel serializeChannel(const channel::Data& c);
+} // namespace giada::m::channelManager
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
+#include "midiActionRecorder.h"
#include "core/action.h"
+#include "core/channels/channel.h"
#include "core/clock.h"
#include "core/conf.h"
+#include "core/eventDispatcher.h"
#include "core/mixer.h"
-#include "core/recorderHandler.h"
#include "core/recManager.h"
-#include "core/channels/state.h"
-#include "midiActionRecorder.h"
-
+#include "core/recorderHandler.h"
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m::midiActionRecorder
{
-MidiActionRecorder::MidiActionRecorder(ChannelState* c)
-: m_channelState(c)
+namespace
{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiActionRecorder::MidiActionRecorder(const MidiActionRecorder& /*o*/, ChannelState* c)
-: MidiActionRecorder(c)
+void record_(channel::Data& ch, const MidiEvent& e)
{
+ MidiEvent flat(e);
+ flat.setChannel(0);
+ recorderHandler::liveRec(ch.id, flat, clock::quantize(clock::getCurrentFrame()));
+ ch.hasActions = true;
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiActionRecorder::parse(const mixer::Event& e) const
+bool canRecord_()
{
- assert(m_channelState != nullptr);
-
- if (e.type == mixer::EventType::MIDI && canRecord())
- record(e.action.event);
+ return recManager::isRecordingAction() &&
+ clock::isRunning() &&
+ !recManager::isRecordingInput();
}
-
+} // namespace
/* -------------------------------------------------------------------------- */
-
-
-void MidiActionRecorder::record(const MidiEvent& e) const
-{
- MidiEvent flat(e);
- flat.setChannel(0);
- recorderHandler::liveRec(m_channelState->id, flat, clock::quantize(clock::getCurrentFrame()));
- m_channelState->hasActions = true;
-}
-
-
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-bool MidiActionRecorder::canRecord() const
+void react(channel::Data& ch, const eventDispatcher::Event& e)
{
- return recManager::isRecordingAction() &&
- clock::isRunning() &&
- !recManager::isRecordingInput();
+ if (e.type == eventDispatcher::EventType::MIDI && canRecord_())
+ record_(ch, std::get<Action>(e.data).event);
}
-}} // giada::m::
-
+} // namespace giada::m::midiActionRecorder
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_ACTION_RECORDER_H
#define G_CHANNEL_MIDI_ACTION_RECORDER_H
-
-#include "core/types.h"
-
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-namespace mixer
+struct Data;
+}
+namespace giada::m::eventDispatcher
{
struct Event;
}
-struct ChannelState;
-class MidiActionRecorder
+namespace giada::m::midiActionRecorder
+{
+struct Data
{
-public:
-
- MidiActionRecorder(ChannelState*);
- MidiActionRecorder(const MidiActionRecorder&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e) const;
-
-private:
-
- bool canRecord() const;
- void record(const MidiEvent& e) const;
-
- ChannelState* m_channelState;
};
-}} // giada::m::
+void react(channel::Data& ch, const eventDispatcher::Event& e);
+} // namespace giada::m::midiActionRecorder
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/conf.h"
-#include "core/channels/state.h"
#include "midiController.h"
+#include "core/channels/channel.h"
+#include "core/conf.h"
+#include <cassert>
-
-namespace giada {
-namespace m
+namespace giada::m::midiController
{
-MidiController::MidiController(ChannelState* c)
-: m_channelState(c)
+namespace
{
-}
-
-
-/* -------------------------------------------------------------------------- */
+ChannelStatus onFirstBeat_(const channel::Data& ch)
+{
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ if (playStatus == ChannelStatus::ENDING)
+ playStatus = ChannelStatus::OFF;
+ else if (playStatus == ChannelStatus::WAIT)
+ playStatus = ChannelStatus::PLAY;
-MidiController::MidiController(const MidiController& /*o*/, ChannelState* c)
-: m_channelState(c)
-{
+ return playStatus;
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiController::parse(const mixer::Event& e) const
+ChannelStatus press_(const channel::Data& ch)
{
- assert(m_channelState != nullptr);
+ ChannelStatus playStatus = ch.state->playStatus.load();
- switch (e.type) {
+ switch (playStatus)
+ {
+ case ChannelStatus::PLAY:
+ playStatus = ChannelStatus::ENDING;
+ break;
- case mixer::EventType::KEY_PRESS:
- press(); break;
-
- case mixer::EventType::KEY_KILL:
- case mixer::EventType::SEQUENCER_STOP:
- kill(); break;
+ case ChannelStatus::ENDING:
+ case ChannelStatus::WAIT:
+ playStatus = ChannelStatus::OFF;
+ break;
- case mixer::EventType::SEQUENCER_FIRST_BEAT:
- case mixer::EventType::SEQUENCER_REWIND:
- onFirstBeat();
+ case ChannelStatus::OFF:
+ playStatus = ChannelStatus::WAIT;
+ break;
- default: break;
+ default:
+ break;
}
-}
+ return playStatus;
+}
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void MidiController::press() const
+void react(channel::Data& ch, const eventDispatcher::Event& e)
{
- ChannelStatus playStatus = m_channelState->playStatus.load();
+ switch (e.type)
+ {
- switch (playStatus) {
- case ChannelStatus::PLAY:
- playStatus = ChannelStatus::ENDING; break;
+ case eventDispatcher::EventType::KEY_PRESS:
+ ch.state->playStatus.store(press_(ch));
+ break;
- case ChannelStatus::ENDING:
- case ChannelStatus::WAIT:
- playStatus = ChannelStatus::OFF; break;
+ case eventDispatcher::EventType::KEY_KILL:
+ case eventDispatcher::EventType::SEQUENCER_STOP:
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ break;
- case ChannelStatus::OFF:
- playStatus = ChannelStatus::WAIT; break;
-
- default: break;
- }
-
- m_channelState->playStatus.store(playStatus);
-}
-
-
-/* -------------------------------------------------------------------------- */
+ case eventDispatcher::EventType::SEQUENCER_REWIND:
+ ch.state->playStatus.store(onFirstBeat_(ch));
-
-void MidiController::kill() const
-{
- m_channelState->playStatus.store(ChannelStatus::OFF);
+ default:
+ break;
+ }
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiController::onFirstBeat() const
+void advance(const channel::Data& ch, const sequencer::Event& e)
{
- ChannelStatus playStatus = m_channelState->playStatus.load();
-
- if (playStatus == ChannelStatus::ENDING)
- playStatus = ChannelStatus::OFF;
- else
- if (playStatus == ChannelStatus::WAIT)
- playStatus = ChannelStatus::PLAY;
-
- m_channelState->playStatus.store(playStatus);
+ if (e.type == sequencer::EventType::FIRST_BEAT)
+ ch.state->playStatus.store(onFirstBeat_(ch));
}
-}} // giada::m::
+} // namespace giada::m::midiController
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_CONTROLLER_H
#define G_CHANNEL_MIDI_CONTROLLER_H
-
-#include "core/types.h"
-#include "core/mixer.h" // TODO - forward declare
-
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-/* MidiController
-Manages events for a MIDI Channel. */
-
-class MidiController
+struct Data;
+}
+namespace giada::m::eventDispatcher
+{
+struct Event;
+}
+namespace giada::m::sequencer
+{
+struct Event;
+}
+namespace giada::m::midiController
+{
+struct Data
{
-public:
-
- MidiController(ChannelState*);
- MidiController(const MidiController&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e) const;
-
-private:
-
- void press() const;
- void kill() const;
- void onFirstBeat() const;
-
- ChannelState* m_channelState;
};
-}} // giada::m::
+void react(channel::Data& ch, const eventDispatcher::Event& e);
+void advance(const channel::Data& ch, const sequencer::Event& e);
+} // namespace giada::m::midiController
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/channels/state.h"
#include "midiLearner.h"
+#include "core/patch.h"
-
-namespace giada {
-namespace m
+namespace giada::m::midiLearner
{
-MidiLearner::MidiLearner()
-: state(std::make_unique<MidiLearnerState>())
+Data::Data(const patch::Channel& p)
+: enabled(p.midiIn)
+, filter(p.midiInFilter)
+, keyPress(p.midiInKeyPress)
+, keyRelease(p.midiInKeyRel)
+, kill(p.midiInKill)
+, arm(p.midiInArm)
+, volume(p.midiInVolume)
+, mute(p.midiInMute)
+, solo(p.midiInSolo)
+, readActions(p.midiInReadActions)
+, pitch(p.midiInPitch)
{
}
-
/* -------------------------------------------------------------------------- */
-
-MidiLearner::MidiLearner(const patch::Channel& p)
-: state(std::make_unique<MidiLearnerState>(p))
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiLearner::MidiLearner(const MidiLearner& o)
-: state(std::make_unique<MidiLearnerState>(*o.state))
+bool Data::isAllowed(int c) const
{
+ return enabled && (filter == -1 || filter == c);
}
-}} // giada::m::
+} // namespace giada::m::midiLearner
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_LEARNER_H
#define G_CHANNEL_MIDI_LEARNER_H
+#include "core/midiLearnParam.h"
-#include <memory>
-
-
-namespace giada {
-namespace m
+namespace giada::m::patch
{
-struct MidiLearnerState;
-class MidiLearner
+struct Channel;
+}
+namespace giada::m::midiLearner
{
-public:
+struct Data
+{
+ Data() = default;
+ Data(const patch::Channel&);
+ Data(const Data&) = default;
- MidiLearner();
- MidiLearner(const patch::Channel&);
- MidiLearner(const MidiLearner&);
+ /* isAllowed
+ Tells whether the MIDI channel 'c' is enabled to receive MIDI data. */
- /* state
- Pointer to mutable MidiLearnerState state. */
+ bool isAllowed(int c) const;
- std::unique_ptr<MidiLearnerState> state;
-};
-}} // giada::m::
+ /* enabled
+ Tells whether MIDI learning is enabled for the current channel. */
+ bool enabled;
+
+ /* filter
+ Which MIDI channel should be filtered out when receiving MIDI messages.
+ If -1 means 'all'. */
+
+ int filter;
+
+ /* MIDI learning fields. */
+
+ MidiLearnParam keyPress;
+ MidiLearnParam keyRelease;
+ MidiLearnParam kill;
+ MidiLearnParam arm;
+ MidiLearnParam volume;
+ MidiLearnParam mute;
+ MidiLearnParam solo;
+ MidiLearnParam readActions; // Sample Channels only
+ MidiLearnParam pitch; // Sample Channels only
+};
+} // namespace giada::m::midiLearner
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/channels/state.h"
-#include "core/mixer.h"
+#include "midiLighter.h"
+#include "core/channels/channel.h"
#include "core/kernelMidi.h"
#include "core/midiMapConf.h"
-#include "midiLighter.h"
-
+#include "core/mixer.h"
-namespace giada {
-namespace m
+namespace giada::m::midiLighter
{
-MidiLighter::MidiLighter(ChannelState* c)
-: state (std::make_unique<MidiLighterState>())
-, m_channelState(c)
+namespace
{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiLighter::MidiLighter(const patch::Channel& p, ChannelState* c)
-: state (std::make_unique<MidiLighterState>(p))
-, m_channelState(c)
+void sendMute_(channel::Data& ch, uint32_t l_mute)
{
+ if (ch.mute)
+ kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOn);
+ else
+ kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOff);
}
-
/* -------------------------------------------------------------------------- */
-
-MidiLighter::MidiLighter(const MidiLighter& o, ChannelState* c)
-: state (std::make_unique<MidiLighterState>(*o.state))
-, m_channelState(c)
+void sendSolo_(channel::Data& ch, uint32_t l_solo)
{
+ if (ch.solo)
+ kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOn);
+ else
+ kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOff);
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiLighter::parse(const mixer::Event& e, bool audible) const
+void sendStatus_(channel::Data& ch, uint32_t l_playing, bool audible)
{
- if (state->enabled.load() == false)
- return;
-
- uint32_t l_playing = state->playing.getValue();
- uint32_t l_mute = state->mute.getValue();
- uint32_t l_solo = state->solo.getValue();
+ switch (ch.state->playStatus.load())
+ {
- switch (e.type) {
+ case ChannelStatus::OFF:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopped);
+ break;
- case mixer::EventType::KEY_PRESS:
- case mixer::EventType::KEY_RELEASE:
- case mixer::EventType::KEY_KILL:
- case mixer::EventType::SEQUENCER_STOP:
- if (l_playing != 0x0) sendStatus(l_playing, audible);
- break;
+ case ChannelStatus::WAIT:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.waiting);
+ break;
- case mixer::EventType::CHANNEL_MUTE:
- if (l_mute != 0x0) sendMute(l_mute);
- break;
+ case ChannelStatus::ENDING:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopping);
+ break;
- case mixer::EventType::CHANNEL_SOLO:
- if (l_solo != 0x0) sendSolo(l_solo);
- break;
+ case ChannelStatus::PLAY:
+ kernelMidi::sendMidiLightning(l_playing, audible ? midimap::midimap.playing : midimap::midimap.playingInaudible);
+ break;
- default: break;
- }
+ default:
+ break;
+ }
}
-
+} // namespace
/* -------------------------------------------------------------------------- */
-
-
-void MidiLighter::sendMute(uint32_t l_mute) const
-{
- if (m_channelState->mute.load() == true)
- kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOn);
- else
- kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOff);
-}
-
-
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void MidiLighter::sendSolo(uint32_t l_solo) const
+Data::Data(const patch::Channel& p)
+: enabled(p.midiOutL)
+, playing(p.midiOutLplaying)
+, mute(p.midiOutLmute)
+, solo(p.midiOutLsolo)
{
- if (m_channelState->solo.load() == true)
- kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOn);
- else
- kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOff);
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiLighter::sendStatus(uint32_t l_playing, bool audible) const
+void react(channel::Data& ch, const eventDispatcher::Event& e, bool audible)
{
- switch (m_channelState->playStatus.load()) {
-
- case ChannelStatus::OFF:
- kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopped);
- break;
-
- case ChannelStatus::WAIT:
- kernelMidi::sendMidiLightning(l_playing, midimap::midimap.waiting);
- break;
-
- case ChannelStatus::ENDING:
- kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopping);
- break;
-
- case ChannelStatus::PLAY:
- kernelMidi::sendMidiLightning(l_playing, audible ? midimap::midimap.playing : midimap::midimap.playingInaudible);
- break;
-
- default: break;
- }
+ if (!ch.midiLighter.enabled)
+ return;
+
+ uint32_t l_playing = ch.midiLighter.playing.getValue();
+ uint32_t l_mute = ch.midiLighter.mute.getValue();
+ uint32_t l_solo = ch.midiLighter.solo.getValue();
+
+ switch (e.type)
+ {
+
+ case eventDispatcher::EventType::KEY_PRESS:
+ case eventDispatcher::EventType::KEY_RELEASE:
+ case eventDispatcher::EventType::KEY_KILL:
+ case eventDispatcher::EventType::SEQUENCER_STOP:
+ if (l_playing != 0x0)
+ sendStatus_(ch, l_playing, audible);
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_MUTE:
+ if (l_mute != 0x0)
+ sendMute_(ch, l_mute);
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_SOLO:
+ if (l_solo != 0x0)
+ sendSolo_(ch, l_solo);
+ break;
+
+ default:
+ break;
+ }
}
-}} // giada::m::
+} // namespace giada::m::midiLighter
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_LIGHTER_H
#define G_CHANNEL_MIDI_LIGHTER_H
+#include "core/midiLearnParam.h"
-#include <memory>
-
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-namespace mixer
+struct Data;
+}
+namespace giada::m::patch
+{
+struct Channel;
+}
+namespace giada::m::eventDispatcher
{
struct Event;
}
-struct MidiLighterState;
-
-/* MidiLighter
-Learns and emits MIDI lightning messages to physical hardware on events. */
-
-class MidiLighter
+namespace giada::m::midiLighter
{
-public:
-
- MidiLighter(ChannelState*);
- MidiLighter(const patch::Channel&, ChannelState*);
- MidiLighter(const MidiLighter&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e, bool audible) const;
-
- /* state
- Pointer to mutable MidiLighterState state. */
+struct Data
+{
+ Data() = default;
+ Data(const patch::Channel& p);
+ Data(const Data& o) = default;
- std::unique_ptr<MidiLighterState> state;
+ /* enabled
+ Tells whether MIDI ligthing is enabled or not. */
-private:
+ bool enabled;
- void sendMute(uint32_t l_mute) const;
- void sendSolo(uint32_t l_solo) const;
- void sendStatus(uint32_t l_playing, bool audible) const;
+ /* MIDI learning fields for MIDI ligthing. */
- ChannelState* m_channelState;
+ MidiLearnParam playing;
+ MidiLearnParam mute;
+ MidiLearnParam solo;
};
-}} // giada::m::
+void react(channel::Data& ch, const eventDispatcher::Event& e, bool audible);
+} // namespace giada::m::midiLighter
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
+#include "midiReceiver.h"
+#include "core/channels/channel.h"
+#include "core/eventDispatcher.h"
#include "core/mixer.h"
#include "core/plugins/pluginHost.h"
-#include "core/channels/state.h"
-#include "midiReceiver.h"
-
-namespace giada {
-namespace m
+namespace giada::m::midiReceiver
{
-MidiReceiver::MidiReceiver(ChannelState* c)
-: state (std::make_unique<MidiReceiverState>())
-, m_channelState (c)
+namespace
{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiReceiver::MidiReceiver(const patch::Channel& /*p*/, ChannelState* c)
-: state (std::make_unique<MidiReceiverState>())
-, m_channelState (c)
+void sendToPlugins_(const channel::Data& ch, const MidiEvent& e, Frame localFrame)
{
+ juce::MidiMessage message = juce::MidiMessage(
+ e.getStatus(),
+ e.getNote(),
+ e.getVelocity());
+ ch.buffer->midi.addEvent(message, localFrame);
}
-
/* -------------------------------------------------------------------------- */
-
-MidiReceiver::MidiReceiver(const MidiReceiver& /*o*/, ChannelState* c)
-: state (std::make_unique<MidiReceiverState>())
-, m_channelState (c)
+void parseMidi_(const channel::Data& ch, const MidiEvent& e)
{
-}
+ /* Now all messages are turned into Channel-0 messages. Giada doesn't care
+ about holding MIDI channel information. Moreover, having all internal
+ messages on channel 0 is way easier. Then send it to plug-ins. */
+ MidiEvent flat(e);
+ flat.setChannel(0);
+ sendToPlugins_(ch, flat, /*delta=*/0);
+}
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void MidiReceiver::parse(const mixer::Event& e) const
+void react(const channel::Data& ch, const eventDispatcher::Event& e)
{
- switch (e.type) {
-
- case mixer::EventType::MIDI:
- parseMidi(e.action.event); break;
-
- case mixer::EventType::ACTION:
- if (m_channelState->isPlaying())
- sendToPlugins(e.action.event, e.delta);
- break;
-
- case mixer::EventType::KEY_KILL:
- case mixer::EventType::SEQUENCER_STOP:
- case mixer::EventType::SEQUENCER_REWIND:
- sendToPlugins(MidiEvent(G_MIDI_ALL_NOTES_OFF), 0); break;
-
- default: break;
- }
-}
+ switch (e.type)
+ {
+ case eventDispatcher::EventType::MIDI:
+ parseMidi_(ch, std::get<Action>(e.data).event);
+ break;
-/* -------------------------------------------------------------------------- */
+ case eventDispatcher::EventType::KEY_KILL:
+ case eventDispatcher::EventType::SEQUENCER_STOP:
+ case eventDispatcher::EventType::SEQUENCER_REWIND:
+ sendToPlugins_(ch, MidiEvent(G_MIDI_ALL_NOTES_OFF), 0);
+ break;
-
-void MidiReceiver::render(const std::vector<ID>& pluginIds) const
-{
- pluginHost::processStack(m_channelState->buffer, pluginIds, &state->midiBuffer);
- state->midiBuffer.clear();
+ default:
+ break;
+ }
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiReceiver::parseMidi(const MidiEvent& e) const
+void advance(const channel::Data& ch, const sequencer::Event& e)
{
- /* Now all messages are turned into Channel-0 messages. Giada doesn't care
- about holding MIDI channel information. Moreover, having all internal
- messages on channel 0 is way easier. Then send it to plug-ins. */
-
- MidiEvent flat(e);
- flat.setChannel(0);
- sendToPlugins(flat, /*delta=*/0);
+ if (e.type == sequencer::EventType::ACTIONS && ch.isPlaying())
+ for (const Action& action : *e.actions)
+ if (action.channelId == ch.id)
+ sendToPlugins_(ch, action.event, e.delta);
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiReceiver::sendToPlugins(const MidiEvent& e, Frame localFrame) const
+void render(const channel::Data& ch)
{
- juce::MidiMessage message = juce::MidiMessage(
- e.getStatus(),
- e.getNote(),
- e.getVelocity());
- state->midiBuffer.addEvent(message, localFrame);
+ pluginHost::processStack(ch.buffer->audio, ch.plugins, &ch.buffer->midi);
+ ch.buffer->midi.clear();
}
-}} // giada::m::
-
+} // namespace giada::m::midiReceiver
-#endif // WITH_VST
\ No newline at end of file
+#endif // WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_RECEIVER_H
#define G_CHANNEL_MIDI_RECEIVER_H
-
#ifdef WITH_VST
-
-#include <memory>
-
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-namespace mixer
+struct Data;
+}
+namespace giada::m::eventDispatcher
{
struct Event;
}
-struct MidiReceiverState;
-
-/* MidiReceiver
-Takes live action gestures AND recorded actions and redirect them as MIDI events
-to plug-in soft synths. */
-
-class MidiReceiver
+namespace giada::m::sequencer
+{
+struct Event;
+}
+namespace giada::m::midiReceiver
+{
+struct Data
{
-public:
-
- MidiReceiver(ChannelState*);
- MidiReceiver(const patch::Channel&, ChannelState*);
- MidiReceiver(const MidiReceiver&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e) const;
- void render(const std::vector<ID>& pluginIds) const;
-
- /* state
- Pointer to mutable MidiReceiverState state. */
-
- std::unique_ptr<MidiReceiverState> state;
-
-private:
-
- /* parseMidi
- Takes a live message (e.g. from a MIDI keyboard), strips it and sends it
- to plug-ins. */
-
- void parseMidi(const MidiEvent& e) const;
-
- /* sendToPlugins
- Enqueues the MIDI event for plug-ins processing. This will be read later on
- by the PluginHost. */
-
- void sendToPlugins(const MidiEvent& e, Frame localFrame) const;
-
- ChannelState* m_channelState;
};
-}} // giada::m::
+void react(const channel::Data& ch, const eventDispatcher::Event& e);
+void advance(const channel::Data& ch, const sequencer::Event& e);
+void render(const channel::Data& ch);
+} // namespace giada::m::midiReceiver
#endif // WITH_VST
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/mixer.h"
-#include "core/kernelMidi.h"
-#include "core/channels/state.h"
#include "midiSender.h"
+#include "core/channels/channel.h"
+#include "core/kernelMidi.h"
+#include "core/mixer.h"
-
-namespace giada {
-namespace m
+namespace giada::m::midiSender
{
-MidiSender::MidiSender(ChannelState* c)
-: state(std::make_unique<MidiSenderState>())
-, m_channelState(c)
+namespace
{
+void send_(const channel::Data& ch, MidiEvent e)
+{
+ e.setChannel(ch.midiSender->filter);
+ kernelMidi::send(e.getRaw());
}
-
/* -------------------------------------------------------------------------- */
-
-MidiSender::MidiSender(const patch::Channel& p, ChannelState* c)
-: state(std::make_unique<MidiSenderState>(p))
-, m_channelState(c)
+void parseActions_(const channel::Data& ch, const std::vector<Action>& as)
{
+ for (const Action& a : as)
+ if (a.channelId == ch.id)
+ send_(ch, a.event);
}
-
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-MidiSender::MidiSender(const MidiSender& o, ChannelState* c)
-: state(std::make_unique<MidiSenderState>(*o.state))
-, m_channelState(c)
+Data::Data(const patch::Channel& p)
+: enabled(p.midiOut)
+, filter(p.midiOutChan)
{
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiSender::parse(const mixer::Event& e) const
+void react(const channel::Data& ch, const eventDispatcher::Event& e)
{
- bool isPlaying = m_channelState->isPlaying();
- bool isEnabled = state->enabled.load();
-
- if (!isPlaying || !isEnabled)
+ if (!ch.isPlaying() || !ch.midiSender->enabled)
return;
- if (e.type == mixer::EventType::KEY_KILL ||
- e.type == mixer::EventType::SEQUENCER_STOP)
- send(MidiEvent(G_MIDI_ALL_NOTES_OFF));
- else
- if (e.type == mixer::EventType::ACTION)
- send(e.action.event);
+ if (e.type == eventDispatcher::EventType::KEY_KILL ||
+ e.type == eventDispatcher::EventType::SEQUENCER_STOP)
+ send_(ch, MidiEvent(G_MIDI_ALL_NOTES_OFF));
}
-
/* -------------------------------------------------------------------------- */
-
-void MidiSender::send(MidiEvent e) const
+void advance(const channel::Data& ch, const sequencer::Event& e)
{
- e.setChannel(state->filter.load());
- kernelMidi::send(e.getRaw());
+ if (!ch.midiSender->enabled)
+ return;
+ if (e.type == sequencer::EventType::ACTIONS)
+ parseActions_(ch, *e.actions);
}
-}} // giada::m::
+} // namespace giada::m::midiSender
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_MIDI_SENDER_H
#define G_CHANNEL_MIDI_SENDER_H
-
-namespace giada {
-namespace m
+namespace giada::m::channel
+{
+struct Data;
+}
+namespace giada::m::patch
{
-namespace mixer
+struct Channel;
+}
+namespace giada::m::eventDispatcher
{
struct Event;
}
-struct ChannelState;
-struct MidiSenderState;
-class MidiSender
+namespace giada::m::sequencer
{
-public:
-
- MidiSender(ChannelState*);
- MidiSender(const patch::Channel&, ChannelState*);
- MidiSender(const MidiSender&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e) const;
-
- /* state
- Pointer to mutable MidiSenderState state. */
+struct Event;
+}
+namespace giada::m::midiSender
+{
+struct Data
+{
+ Data() = default;
+ Data(const patch::Channel& p);
+ Data(const Data& o) = default;
- std::unique_ptr<MidiSenderState> state;
+ /* enabled
+ Tells whether MIDI output is enabled or not. */
-private:
+ bool enabled;
- void send(MidiEvent e) const;
+ /* filter
+ Which MIDI channel data should be sent to. */
- ChannelState* m_channelState;
+ int filter;
};
-}} // giada::m::
+void react(const channel::Data& ch, const eventDispatcher::Event& e);
+void advance(const channel::Data& ch, const sequencer::Event& e);
+} // namespace giada::m::midiSender
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
+#include "sampleActionRecorder.h"
#include "core/action.h"
+#include "core/channels/channel.h"
#include "core/clock.h"
#include "core/conf.h"
+#include "core/eventDispatcher.h"
#include "core/mixer.h"
-#include "core/recorderHandler.h"
#include "core/recManager.h"
-#include "core/channels/state.h"
-#include "sampleActionRecorder.h"
-
+#include "core/recorderHandler.h"
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m::sampleActionRecorder
{
-SampleActionRecorder::SampleActionRecorder(ChannelState* c, SamplePlayerState* sc)
-: m_channelState(c)
-, m_samplePlayerState(sc)
+namespace
{
-}
-
+void record_(channel::Data& ch, int note);
+void onKeyPress_(channel::Data& ch);
+void toggleReadActions_(channel::Data& ch);
+void startReadActions_(channel::Data& ch);
+void stopReadActions_(channel::Data& ch, ChannelStatus curRecStatus);
+void killReadActions_(channel::Data& ch);
+bool canRecord_(const channel::Data& ch);
/* -------------------------------------------------------------------------- */
-
-SampleActionRecorder::SampleActionRecorder(const SampleActionRecorder& /*o*/,
- ChannelState* c, SamplePlayerState* sc)
-: SampleActionRecorder(c, sc)
+bool canRecord_(const channel::Data& ch)
{
+ return recManager::isRecordingAction() &&
+ clock::isRunning() &&
+ !recManager::isRecordingInput() &&
+ !ch.samplePlayer->isAnyLoopMode();
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleActionRecorder::parse(const mixer::Event& e) const
-{
- assert(m_channelState != nullptr);
-
- switch (e.type) {
-
- case mixer::EventType::KEY_PRESS:
- onKeyPress(); break;
-
- /* Record a stop event only if channel is SINGLE_PRESS. For any other
- mode the key release event is meaningless. */
-
- case mixer::EventType::KEY_RELEASE:
- if (canRecord() && m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS)
- record(MidiEvent::NOTE_OFF);
- break;
-
- case mixer::EventType::KEY_KILL:
- if (canRecord())
- record(MidiEvent::NOTE_KILL);
- break;
-
- case mixer::EventType::SEQUENCER_FIRST_BEAT:
- onFirstBeat(); break;
-
- case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
- toggleReadActions(); break;
-
- case mixer::EventType::CHANNEL_KILL_READ_ACTIONS:
- killReadActions(); break;
-
- default: break;
- }
-}
-
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::record(int note) const
+void onKeyPress_(channel::Data& ch)
{
- recorderHandler::liveRec(m_channelState->id, MidiEvent(note, 0, 0),
- clock::quantize(clock::getCurrentFrame()));
-
- m_channelState->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
+ if (!canRecord_(ch))
+ return;
+ record_(ch, MidiEvent::NOTE_ON);
+ /* Skip reading actions when recording on ChannelMode::SINGLE_PRESS to
+ prevent existing actions to interfere with the keypress/keyrel combo. */
-void SampleActionRecorder::startReadActions() const
-{
- if (conf::conf.treatRecsAsLoops)
- m_channelState->recStatus.store(ChannelStatus::WAIT);
- else {
- m_channelState->recStatus.store(ChannelStatus::PLAY);
- m_channelState->readActions.store(true);
- }
+ if (ch.samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS)
+ ch.state->readActions.store(false);
}
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::stopReadActions(ChannelStatus curRecStatus) const
+void record_(channel::Data& ch, int note)
{
- /* First of all, if the clock is not running or treatRecsAsLoops is off,
- just stop and disable everything. Otherwise make sure a channel with actions
- behave like a dynamic one. */
+ recorderHandler::liveRec(ch.id, MidiEvent(note, 0, 0),
+ clock::quantize(clock::getCurrentFrame()));
- if (!clock::isRunning() || !conf::conf.treatRecsAsLoops) {
- m_channelState->recStatus.store(ChannelStatus::OFF);
- m_channelState->readActions.store(false);
- }
- else
- if (curRecStatus == ChannelStatus::WAIT)
- m_channelState->recStatus.store(ChannelStatus::OFF);
- else
- if (curRecStatus == ChannelStatus::ENDING)
- m_channelState->recStatus.store(ChannelStatus::PLAY);
- else
- m_channelState->recStatus.store(ChannelStatus::ENDING);
+ ch.hasActions = true;
}
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::toggleReadActions() const
+void toggleReadActions_(channel::Data& ch)
{
/* When you start reading actions while conf::treatRecsAsLoops is true, the
value ch.state->readActions actually is not set to true immediately, because
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 (!m_channelState->hasActions)
+ if (!ch.hasActions)
return;
- bool readActions = m_channelState->readActions.load();
- ChannelStatus recStatus = m_channelState->recStatus.load();
+ const bool readActions = ch.state->readActions.load();
+ const ChannelStatus recStatus = ch.state->recStatus.load();
if (readActions || (!readActions && recStatus == ChannelStatus::WAIT))
- stopReadActions(recStatus);
+ stopReadActions_(ch, recStatus);
else
- startReadActions();
+ startReadActions_(ch);
}
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::killReadActions() const
+void startReadActions_(channel::Data& ch)
{
- /* Killing Read Actions, i.e. shift + click on 'R' button is meaninful only
- when the conf::treatRecsAsLoops is true. */
-
- if (!conf::conf.treatRecsAsLoops)
- return;
- m_channelState->recStatus.store(ChannelStatus::OFF);
- m_channelState->readActions.store(false);
+ if (conf::conf.treatRecsAsLoops)
+ ch.state->recStatus.store(ChannelStatus::WAIT);
+ else
+ {
+ ch.state->recStatus.store(ChannelStatus::PLAY);
+ ch.state->readActions.store(true);
+ }
}
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::onKeyPress() const
+void stopReadActions_(channel::Data& ch, ChannelStatus curRecStatus)
{
- if (!canRecord())
- return;
- record(MidiEvent::NOTE_ON);
-
- /* Skip reading actions when recording on ChannelMode::SINGLE_PRESS to
- prevent existing actions to interfere with the keypress/keyrel combo. */
+ /* First of all, if the clock is not running or treatRecsAsLoops is off,
+ just stop and disable everything. Otherwise make sure a channel with actions
+ behave like a dynamic one. */
- if (m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS)
- m_channelState->readActions = false;
+ if (!clock::isRunning() || !conf::conf.treatRecsAsLoops)
+ {
+ ch.state->recStatus.store(ChannelStatus::OFF);
+ ch.state->readActions.store(false);
+ }
+ else if (curRecStatus == ChannelStatus::WAIT)
+ ch.state->recStatus.store(ChannelStatus::OFF);
+ else if (curRecStatus == ChannelStatus::ENDING)
+ ch.state->recStatus.store(ChannelStatus::PLAY);
+ else
+ ch.state->recStatus.store(ChannelStatus::ENDING);
}
-
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::onKeyRelease() const
+void killReadActions_(channel::Data& ch)
{
- if (canRecord() && m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS) {
- record(MidiEvent::NOTE_OFF);
- m_channelState->readActions = true;
- }
-}
+ /* Killing Read Actions, i.e. shift + click on 'R' button is meaninful only
+ when the conf::treatRecsAsLoops is true. */
+ if (!conf::conf.treatRecsAsLoops)
+ return;
+ ch.state->recStatus.store(ChannelStatus::OFF);
+ ch.state->readActions.store(false);
+}
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void SampleActionRecorder::onFirstBeat() const
+void react(channel::Data& ch, const eventDispatcher::Event& e)
{
- ChannelStatus recStatus = m_channelState->recStatus.load();
+ if (!ch.hasWave())
+ return;
- switch (recStatus) {
+ switch (e.type)
+ {
- case ChannelStatus::ENDING:
- m_channelState->recStatus.store(ChannelStatus::OFF);
- m_channelState->readActions = false;
- break;
+ case eventDispatcher::EventType::KEY_PRESS:
+ onKeyPress_(ch);
+ break;
- case ChannelStatus::WAIT:
- m_channelState->recStatus.store(ChannelStatus::PLAY);
- m_channelState->readActions = true;
- break;
+ /* Record a stop event only if channel is SINGLE_PRESS. For any other
+ mode the key release event is meaningless. */
- default: break;
- }
-}
+ case eventDispatcher::EventType::KEY_RELEASE:
+ if (canRecord_(ch) && ch.samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS)
+ record_(ch, MidiEvent::NOTE_OFF);
+ break;
+ case eventDispatcher::EventType::KEY_KILL:
+ if (canRecord_(ch))
+ record_(ch, MidiEvent::NOTE_KILL);
+ break;
-/* -------------------------------------------------------------------------- */
+ case eventDispatcher::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
+ toggleReadActions_(ch);
+ break;
+ case eventDispatcher::EventType::CHANNEL_KILL_READ_ACTIONS:
+ killReadActions_(ch);
+ break;
-bool SampleActionRecorder::canRecord() const
-{
- return recManager::isRecordingAction() &&
- clock::isRunning() &&
- !recManager::isRecordingInput() &&
- !m_samplePlayerState->isAnyLoopMode();
+ default:
+ break;
+ }
}
-}} // giada::m::
-
+} // namespace giada::m::sampleActionRecorder
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_SAMPLE_ACTION_RECORDER_H
#define G_CHANNEL_SAMPLE_ACTION_RECORDER_H
-
-#include "core/types.h"
-
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-namespace mixer
+struct Data;
+}
+namespace giada::m::eventDispatcher
{
struct Event;
}
-struct ChannelState;
-
-/* SampleActionRecorder
-Records actions for channels and optionally manages the 'read action' state ('R'
-button on Sample Channels). */
-
-class SampleActionRecorder
+namespace giada::m::sampleActionRecorder
+{
+struct Data
{
-public:
-
- SampleActionRecorder(ChannelState*, SamplePlayerState*);
- SampleActionRecorder(const SampleActionRecorder&, ChannelState* c=nullptr,
- SamplePlayerState* sc=nullptr);
-
- void parse(const mixer::Event& e) const;
-
-private:
- void record(int note) const;
- void onKeyPress() const;
- void onKeyRelease() const;
- void onFirstBeat() const;
-
- void toggleReadActions() const;
- void startReadActions() const;
- void stopReadActions(ChannelStatus curRecStatus) const;
- void killReadActions() const;
-
- bool canRecord() const;
-
- ChannelState* m_channelState;
- SamplePlayerState* m_samplePlayerState;
};
-}} // giada::m::
+void react(channel::Data& ch, const eventDispatcher::Event& e);
+} // namespace giada::m::sampleActionRecorder
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "sampleAdvancer.h"
+#include "core/channels/channel.h"
+#include "core/clock.h"
+#include <cassert>
+
+namespace giada::m::sampleAdvancer
+{
+namespace
+{
+void rewind_(const channel::Data& ch, Frame localFrame)
+{
+ ch.state->rewinding = true;
+ ch.state->offset = localFrame;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void stop_(const channel::Data& ch, Frame localFrame)
+{
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ ch.state->tracker.store(ch.samplePlayer->begin);
+
+ /* Clear data in range [localFrame, (buffer.size)) if the event occurs in
+ the middle of the buffer. TODO - samplePlayer should be responsible for this*/
+
+ if (localFrame != 0)
+ ch.buffer->audio.clear(localFrame);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void play_(const channel::Data& ch, Frame localFrame)
+{
+ ch.state->playStatus.store(ChannelStatus::PLAY);
+ ch.state->offset = localFrame;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void onFirstBeat_(const channel::Data& ch, Frame localFrame)
+{
+ G_DEBUG("onFirstBeat ch=" << ch.id << ", localFrame=" << localFrame);
+
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ ChannelStatus recStatus = ch.state->recStatus.load();
+ bool isLoop = ch.samplePlayer->isAnyLoopMode();
+
+ switch (playStatus)
+ {
+ case ChannelStatus::PLAY:
+ if (isLoop)
+ rewind_(ch, localFrame);
+ break;
+
+ case ChannelStatus::WAIT:
+ play_(ch, localFrame);
+ break;
+
+ case ChannelStatus::ENDING:
+ if (isLoop)
+ stop_(ch, localFrame);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (recStatus)
+ {
+ case ChannelStatus::WAIT:
+ ch.state->recStatus.store(ChannelStatus::PLAY);
+ ch.state->readActions.store(true);
+ break;
+
+ case ChannelStatus::ENDING:
+ ch.state->recStatus.store(ChannelStatus::OFF);
+ ch.state->readActions.store(false);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void onBar_(const channel::Data& ch, Frame localFrame)
+{
+ G_DEBUG("onBar ch=" << ch.id << ", localFrame=" << localFrame);
+
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ SamplePlayerMode mode = ch.samplePlayer->mode;
+
+ if (playStatus == ChannelStatus::PLAY && (mode == SamplePlayerMode::LOOP_REPEAT ||
+ mode == SamplePlayerMode::LOOP_ONCE_BAR))
+ rewind_(ch, localFrame);
+ else if (playStatus == ChannelStatus::WAIT && mode == SamplePlayerMode::LOOP_ONCE_BAR)
+ ch.state->playStatus.store(ChannelStatus::PLAY);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void onNoteOn_(const channel::Data& ch, Frame localFrame)
+{
+ ChannelStatus playStatus = ch.state->playStatus.load();
+
+ if (playStatus == ChannelStatus::OFF)
+ {
+ playStatus = ChannelStatus::PLAY;
+ }
+ else if (playStatus == ChannelStatus::PLAY)
+ {
+ if (ch.samplePlayer->mode == SamplePlayerMode::SINGLE_RETRIG)
+ rewind_(ch, localFrame);
+ else
+ playStatus = ChannelStatus::OFF;
+ }
+
+ ch.state->playStatus.store(playStatus);
+ ch.state->offset = localFrame;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void onNoteOff_(const channel::Data& ch, Frame localFrame)
+{
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ ch.state->tracker.store(ch.samplePlayer->begin);
+
+ /* Clear data in range [localFrame, (buffer.size)) if the kill event occurs
+ in the middle of the buffer. */
+
+ if (localFrame != 0)
+ ch.buffer->audio.clear(localFrame);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void parseActions_(const channel::Data& ch, const std::vector<Action>& as, Frame localFrame)
+{
+ if (ch.samplePlayer->isAnyLoopMode() || !ch.isReadingActions())
+ return;
+
+ for (const Action& a : as)
+ {
+ if (a.channelId != ch.id)
+ continue;
+
+ switch (a.event.getStatus())
+ {
+ case MidiEvent::NOTE_ON:
+ onNoteOn_(ch, localFrame);
+ break;
+
+ case MidiEvent::NOTE_OFF:
+ onNoteOff_(ch, localFrame);
+ break;
+
+ case MidiEvent::NOTE_KILL:
+ onNoteOff_(ch, localFrame);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+void onLastFrame(const channel::Data& ch)
+{
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ SamplePlayerMode mode = ch.samplePlayer->mode;
+ bool isLoop = ch.samplePlayer->isAnyLoopMode();
+ bool running = clock::isRunning();
+
+ if (playStatus == ChannelStatus::PLAY)
+ {
+ /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for
+ SINGLE_ENDLESS, which runs forever unless it's in ENDING mode.
+ Other loop once modes are put in wait mode. */
+ if ((mode == SamplePlayerMode::SINGLE_BASIC ||
+ mode == SamplePlayerMode::SINGLE_PRESS ||
+ mode == SamplePlayerMode::SINGLE_RETRIG) ||
+ (isLoop && !running))
+ playStatus = ChannelStatus::OFF;
+ else if (mode == SamplePlayerMode::LOOP_ONCE || mode == SamplePlayerMode::LOOP_ONCE_BAR)
+ playStatus = ChannelStatus::WAIT;
+ }
+ else if (playStatus == ChannelStatus::ENDING)
+ playStatus = ChannelStatus::OFF;
+
+ ch.state->playStatus.store(playStatus);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void advance(const channel::Data& ch, const sequencer::Event& e)
+{
+ switch (e.type)
+ {
+ case sequencer::EventType::FIRST_BEAT:
+ onFirstBeat_(ch, e.delta);
+ break;
+
+ case sequencer::EventType::BAR:
+ onBar_(ch, e.delta);
+ break;
+
+ case sequencer::EventType::REWIND:
+ rewind_(ch, e.delta);
+ break;
+
+ case sequencer::EventType::ACTIONS:
+ if (ch.state->readActions.load() == true)
+ parseActions_(ch, *e.actions, e.delta);
+ break;
+
+ default:
+ break;
+ }
+}
+} // namespace giada::m::sampleAdvancer
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_CHANNEL_SAMPLE_ADVANCER_H
+#define G_CHANNEL_SAMPLE_ADVANCER_H
+
+#include "core/sequencer.h"
+
+namespace giada::m::channel
+{
+struct Data;
+}
+namespace giada::m::sampleAdvancer
+{
+void onLastFrame(const channel::Data& ch);
+void advance(const channel::Data& ch, const sequencer::Event& e);
+} // namespace giada::m::sampleAdvancer
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "core/conf.h"
-#include "core/clock.h"
-#include "core/action.h"
-#include "core/mixer.h"
-#include "core/channels/state.h"
-#include "utils/math.h"
-#include "sampleController.h"
-
-
-namespace giada {
-namespace m
-{
-namespace
-{
-constexpr int Q_ACTION_PLAY = 0;
-constexpr int Q_ACTION_REWIND = 1;
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-SampleController::SampleController(ChannelState* c, SamplePlayerState* s)
-: m_channelState (c)
-, m_samplePlayerState(s)
-{
- m_samplePlayerState->quantizer.schedule(Q_ACTION_PLAY,
- [&status = m_channelState->playStatus, &offset = m_samplePlayerState->offset]
- (Frame delta)
- {
- offset = delta;
- status = ChannelStatus::PLAY;
- });
-
- m_samplePlayerState->quantizer.schedule(Q_ACTION_REWIND, [this] (Frame delta)
- {
- rewind(delta);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleController::SampleController(const SampleController& /*o*/, ChannelState* c, SamplePlayerState* s)
-: SampleController(c, s)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::parse(const mixer::Event& e) const
-{
- assert(m_channelState != nullptr);
- assert(m_samplePlayerState != nullptr);
-
- switch (e.type) {
- case mixer::EventType::KEY_PRESS:
- press(e.delta, e.action.event.getVelocity(), /*manual=*/true); break;
-
- case mixer::EventType::KEY_RELEASE:
- release(e.delta); break;
-
- case mixer::EventType::KEY_KILL:
- kill(e.delta); break;
-
- case mixer::EventType::SEQUENCER_FIRST_BEAT:
- if (clock::isRunning())
- onFirstBeat(e.delta);
- break;
-
- case mixer::EventType::SEQUENCER_BAR:
- onBar(e.delta); break;
-
- case mixer::EventType::SEQUENCER_STOP:
- onStopBySeq(); break;
-
- case mixer::EventType::ACTION:
- if (m_channelState->readActions.load() == true)
- parseAction(e.action, e.delta);
- break;
-
- case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
- toggleReadActions(); break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::onLastFrame() const
-{
- ChannelStatus playStatus = m_channelState->playStatus.load();
- SamplePlayerMode mode = m_samplePlayerState->mode.load();
- bool running = clock::isRunning();
-
- if (playStatus == ChannelStatus::PLAY) {
- /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for
- SINGLE_ENDLESS, which runs forever unless it's in ENDING mode.
- Other loop once modes are put in wait mode. */
- if ((mode == SamplePlayerMode::SINGLE_BASIC ||
- mode == SamplePlayerMode::SINGLE_PRESS ||
- mode == SamplePlayerMode::SINGLE_RETRIG) ||
- (m_samplePlayerState->isAnyLoopMode() && !running))
- playStatus = ChannelStatus::OFF;
- else
- if (mode == SamplePlayerMode::LOOP_ONCE || mode == SamplePlayerMode::LOOP_ONCE_BAR)
- playStatus = ChannelStatus::WAIT;
- }
- else
- if (playStatus == ChannelStatus::ENDING)
- playStatus = ChannelStatus::OFF;
-
- m_channelState->playStatus.store(playStatus);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::advance(Frame bufferSize) const
-{
- Range<Frame> block(clock::getCurrentFrame(), clock::getCurrentFrame() + bufferSize);
- m_samplePlayerState->quantizer.advance(block, clock::getQuantizerStep());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::press(Frame localFrame, int velocity, bool manual) const
-{
- ChannelStatus playStatus = m_channelState->playStatus.load();
- SamplePlayerMode mode = m_samplePlayerState->mode.load();
- bool isLoop = m_samplePlayerState->isAnyLoopMode();
-
- switch (playStatus) {
- case ChannelStatus::OFF:
- playStatus = pressWhileOff(localFrame, velocity, isLoop, manual); break;
-
- case ChannelStatus::PLAY:
- playStatus = pressWhilePlay(localFrame, mode, isLoop, manual); break;
-
- case ChannelStatus::WAIT:
- playStatus = ChannelStatus::OFF; break;
-
- case ChannelStatus::ENDING:
- playStatus = ChannelStatus::PLAY; break;
-
- default: break;
- }
-
- m_channelState->playStatus.store(playStatus);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::release(Frame localFrame) const
-{
- /* Key release is meaningful only for SINGLE_PRESS modes. */
-
- if (m_samplePlayerState->mode.load() != SamplePlayerMode::SINGLE_PRESS)
- return;
-
- /* Kill it if it's SINGLE_PRESS is playing. Otherwise there might be a
- quantization step in progress that would play the channel later on:
- disable it. */
-
- if (m_channelState->playStatus.load() == ChannelStatus::PLAY)
- kill(localFrame);
- else
- if (m_samplePlayerState->quantizer.isTriggered())
- m_samplePlayerState->quantizer.clear();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::kill(Frame localFrame) const
-{
- m_channelState->playStatus.store(ChannelStatus::OFF);
- m_samplePlayerState->tracker.store(m_samplePlayerState->begin.load());
- m_samplePlayerState->quantizing = false;
-
- /* Clear data in range [localFrame, (buffer.size)) if the kill event occurs
- in the middle of the buffer. */
-
- if (localFrame != 0)
- m_channelState->buffer.clear(localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::rewind(Frame localFrame) const
-{
- /* Quantization stops on rewind. */
-
- m_samplePlayerState->quantizer.clear();
-
- if (m_channelState->isPlaying()) {
- m_samplePlayerState->rewinding = true;
- m_samplePlayerState->offset = localFrame;
- }
- else
- m_samplePlayerState->tracker.store(m_samplePlayerState->begin.load());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-ChannelStatus SampleController::pressWhileOff(Frame localFrame, int velocity, bool isLoop, bool manual) const
-{
- m_samplePlayerState->offset = localFrame;
-
- if (isLoop)
- return ChannelStatus::WAIT;
-
- if (m_samplePlayerState->velocityAsVol.load() == true)
- m_channelState->volume_i = u::math::map(velocity, G_MAX_VELOCITY, G_MAX_VOLUME);
-
- if (clock::canQuantize() && manual) { // manual: don't quantize recorded actions
- m_samplePlayerState->quantizer.trigger(Q_ACTION_PLAY);
- return ChannelStatus::OFF;
- }
- else
- return ChannelStatus::PLAY;
-}
-
-
-ChannelStatus SampleController::pressWhilePlay(Frame localFrame, SamplePlayerMode mode, bool isLoop, bool manual) const
-{
- if (mode == SamplePlayerMode::SINGLE_RETRIG) {
- if (clock::canQuantize() && manual) // manual: don't quantize recorded actions
- m_samplePlayerState->quantizer.trigger(Q_ACTION_REWIND);
- else
- rewind(localFrame);
- return ChannelStatus::PLAY;
- }
-
- if (isLoop || mode == SamplePlayerMode::SINGLE_ENDLESS)
- return ChannelStatus::ENDING;
-
- if (mode == SamplePlayerMode::SINGLE_BASIC) {
- rewind(localFrame);
- return ChannelStatus::OFF;
- }
-
- return ChannelStatus::OFF;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::onBar(Frame localFrame) const
-{
- G_DEBUG("onBar ch=" << m_channelState->id);
-
- ChannelStatus playStatus = m_channelState->playStatus.load();
- SamplePlayerMode mode = m_samplePlayerState->mode.load();
-
- if (playStatus == ChannelStatus::PLAY && (mode == SamplePlayerMode::LOOP_REPEAT ||
- mode == SamplePlayerMode::LOOP_ONCE_BAR))
- rewind(localFrame);
- else
- if (playStatus == ChannelStatus::WAIT && mode == SamplePlayerMode::LOOP_ONCE_BAR) {
- m_channelState->playStatus.store(ChannelStatus::PLAY);
- m_samplePlayerState->offset = localFrame;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::onFirstBeat(Frame localFrame) const
-{
-G_DEBUG("onFirstBeat ch=" << m_channelState->id << ", localFrame=" << localFrame);
-
- ChannelStatus playStatus = m_channelState->playStatus.load();
- bool isLoop = m_samplePlayerState->isAnyLoopMode();
-
- switch (playStatus) {
-
- case ChannelStatus::PLAY:
- if (isLoop)
- rewind(localFrame);
- break;
-
- case ChannelStatus::WAIT:
- m_samplePlayerState->offset = localFrame;
- m_channelState->playStatus.store(ChannelStatus::PLAY);
- break;
-
- case ChannelStatus::ENDING:
- if (isLoop)
- kill(localFrame);
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::onStopBySeq() const
-{
- G_DEBUG("onStopBySeq ch=" << m_channelState->id);
-
- ChannelStatus playStatus = m_channelState->playStatus.load();
- bool isReadingActions = m_channelState->readActions.load() == true;
- bool isLoop = m_samplePlayerState->isAnyLoopMode();
-
- switch (playStatus) {
-
- case ChannelStatus::WAIT:
- /* Loop-mode channels in wait status get stopped right away. */
- if (isLoop)
- m_channelState->playStatus.store(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 (conf::conf.chansStopOnSeqHalt)
- if (isLoop || isReadingActions)
- kill(0);
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::parseAction(const Action& a, Frame localFrame) const
-{
- bool isLoop = m_samplePlayerState->isAnyLoopMode();
-
- switch (a.event.getStatus()) {
- case MidiEvent::NOTE_ON:
- if (!isLoop)
- press(localFrame, /*velocity=*/G_MAX_VELOCITY, /*manual=*/false);
- break;
- case MidiEvent::NOTE_OFF:
- if (!isLoop)
- release(localFrame);
- break;
- case MidiEvent::NOTE_KILL:
- if (!isLoop)
- kill(localFrame);
- break;
- case MidiEvent::ENVELOPE:
- //calcVolumeEnv_(ch, a); TODO
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleController::toggleReadActions() const
-{
- ChannelStatus recStatus = m_channelState->recStatus.load();
- if (clock::isRunning() && recStatus == ChannelStatus::PLAY && !conf::conf.treatRecsAsLoops)
- kill(0);
-}
-}} // giada::m::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_CHANNEL_SAMPLE_CONTROLLER_H
-#define G_CHANNEL_SAMPLE_CONTROLLER_H
-
-
-#include "core/types.h"
-
-
-namespace giada {
-namespace m
-{
-namespace mixer
-{
-struct Event;
-}
-struct SamplePlayerState;
-class SampleController
-{
-public:
-
- SampleController(ChannelState*, SamplePlayerState*);
- SampleController(const SampleController&, ChannelState* c=nullptr, SamplePlayerState* s=nullptr);
-
- void parse(const mixer::Event& e) const;
- void onLastFrame() const;
- void advance(Frame bufferSize) const;
-
-private:
-
- void press(Frame localFrame, int velocity, bool manual) const;
- void release(Frame localFrame) const;
- void kill(Frame localFrame) const;
- void rewind(Frame localFrame) const;
-
- ChannelStatus pressWhileOff(Frame localFrame, int velocity, bool isLoop, bool manual) const;
- ChannelStatus pressWhilePlay(Frame localFrame, SamplePlayerMode mode, bool isLoop, bool manual) const;
- void toggleReadActions() const;
-
- void onBar(Frame localFrame) const;
- void onFirstBeat(Frame localFrame) const;
- void onStopBySeq() const;
- void parseAction(const Action& a, Frame localFrame) const;
-
- ChannelState* m_channelState;
- SamplePlayerState* m_samplePlayerState;
-};
-}} // giada::m::
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <algorithm>
-#include <cassert>
+#include "samplePlayer.h"
#include "core/channels/channel.h"
-#include "core/channels/state.h"
-#include "core/wave.h"
#include "core/clock.h"
-#include "samplePlayer.h"
-
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include <algorithm>
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m::samplePlayer
{
-SamplePlayer::SamplePlayer(ChannelState* c)
-: state (std::make_unique<SamplePlayerState>())
-, m_waveId (0)
-, m_sampleController(c, state.get())
-, m_channelState (c)
+namespace
{
-}
+bool shouldLoop_(const channel::Data& ch)
+{
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ SamplePlayerMode mode = ch.samplePlayer->mode;
+ return (mode == SamplePlayerMode::LOOP_BASIC ||
+ mode == SamplePlayerMode::LOOP_REPEAT ||
+ mode == SamplePlayerMode::SINGLE_ENDLESS) &&
+ playStatus == ChannelStatus::PLAY;
+}
/* -------------------------------------------------------------------------- */
-
-SamplePlayer::SamplePlayer(const SamplePlayer& o, ChannelState* c)
-: state (std::make_unique<SamplePlayerState>(*o.state))
-, m_waveId (o.m_waveId)
-, m_waveReader (o.m_waveReader)
-, m_sampleController(o.m_sampleController, c, state.get())
-, m_channelState (c)
+WaveReader::Result fillBuffer_(const channel::Data& ch, Frame start, Frame offset)
{
-}
+ AudioBuffer& buffer = ch.buffer->audio;
+ const WaveReader& waveReader = ch.samplePlayer->waveReader;
+ return waveReader.fill(buffer, start, ch.samplePlayer->end, offset, ch.samplePlayer->pitch);
+}
/* -------------------------------------------------------------------------- */
-
-SamplePlayer::SamplePlayer(const patch::Channel& p, ChannelState* c)
-: state (std::make_unique<SamplePlayerState>(p))
-, m_waveId (p.waveId)
-, m_sampleController(c, state.get())
-, m_channelState (c)
+bool isPlaying_(const channel::Data& ch)
{
+ return ch.samplePlayer->waveReader.wave != nullptr && ch.isPlaying();
}
-
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::parse(const mixer::Event& e) const
+void setWave_(samplePlayer::Data& sp, Wave* w, float samplerateRatio)
{
- if (e.type == mixer::EventType::CHANNEL_PITCH)
- state->pitch.store(e.action.event.getVelocityFloat());
-
- if (hasWave())
- m_sampleController.parse(e);
+ if (w == nullptr)
+ {
+ sp.waveReader.wave = nullptr;
+ return;
+ }
+
+ sp.waveReader.wave = w;
+
+ if (samplerateRatio != 1.0f)
+ {
+ sp.begin *= samplerateRatio;
+ sp.end *= samplerateRatio;
+ sp.shift *= samplerateRatio;
+ }
}
-
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::advance(Frame bufferSize) const
+Data::Data(Resampler* r)
+: pitch(G_DEFAULT_PITCH)
+, mode(SamplePlayerMode::SINGLE_BASIC)
+, velocityAsVol(false)
+, waveReader(r)
{
- m_sampleController.advance(bufferSize);
}
-
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::render(AudioBuffer& /*out*/) const
+Data::Data(const patch::Channel& p, float samplerateRatio, Resampler* r)
+: pitch(p.pitch)
+, mode(p.mode)
+, shift(p.shift)
+, begin(p.begin)
+, end(p.end)
+, velocityAsVol(p.midiInVeloAsVol)
+, waveReader(r)
{
- assert(m_channelState != nullptr);
-
- if (m_waveReader.wave == nullptr || !m_channelState->isPlaying())
- return;
-
- Frame begin = state->begin.load();
- Frame end = state->end.load();
- Frame tracker = state->tracker.load();
- float pitch = state->pitch.load();
- Frame used = 0;
-
- /* Audio data is temporarily stored to the working audio buffer. */
+ setWave_(*this, waveManager::hydrateWave(p.waveId), samplerateRatio);
+}
- AudioBuffer& buffer = m_channelState->buffer;
+/* -------------------------------------------------------------------------- */
- /* Adjust tracker in case someone has changed the begin/end points in the
- meantime. */
-
- if (tracker < begin || tracker >= end)
- tracker = begin;
+bool Data::hasWave() const { return waveReader.wave != nullptr; }
+bool Data::hasLogicalWave() const { return hasWave() && waveReader.wave->isLogical(); }
+bool Data::hasEditedWave() const { return hasWave() && waveReader.wave->isEdited(); }
- /* If rewinding, fill the tail first, then reset the tracker to the begin
- point. The rest is performed as usual. */
+/* -------------------------------------------------------------------------- */
- if (state->rewinding) {
- if (tracker < end)
- m_waveReader.fill(buffer, tracker, 0, pitch);
- state->rewinding = false;
- tracker = begin;
- }
-
- used = m_waveReader.fill(buffer, tracker, state->offset, pitch);
- tracker += used;
-
-G_DEBUG ("block=[" << tracker - used << ", " << tracker << ")" <<
- ", used=" << used << ", range=[" << begin << ", " << end << ")" <<
- ", tracker=" << tracker <<
- ", offset=" << state->offset << ", globalFrame=" << clock::getCurrentFrame());
-
- if (tracker >= end) {
-G_DEBUG ("last frame tracker=" << tracker);
- tracker = begin;
- m_sampleController.onLastFrame();
- if (shouldLoop()) {
- Frame offset = std::min(static_cast<Frame>(used / pitch), buffer.countFrames() - 1);
- tracker += m_waveReader.fill(buffer, tracker, offset, pitch);
- }
- }
-
- state->offset = 0;
- state->tracker.store(tracker);
+bool Data::isAnyLoopMode() const
+{
+ return mode == SamplePlayerMode::LOOP_BASIC ||
+ mode == SamplePlayerMode::LOOP_ONCE ||
+ mode == SamplePlayerMode::LOOP_REPEAT ||
+ mode == SamplePlayerMode::LOOP_ONCE_BAR;
}
-
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::loadWave(const Wave* w)
+Wave* Data::getWave() const
{
- m_waveReader.wave = w;
-
- state->tracker.store(0);
- state->shift.store(0);
- state->begin.store(0);
-
- if (w != nullptr) {
- m_waveId = w->id;
- m_channelState->playStatus.store(ChannelStatus::OFF);
- m_channelState->name = w->getBasename(/*ext=*/false);
- state->end.store(w->getSize() - 1);
- }
- else {
- m_waveId = 0;
- m_channelState->playStatus.store(ChannelStatus::EMPTY);
- m_channelState->name = "";
- state->end.store(0);
- }
+ return waveReader.wave;
}
+ID Data::getWaveId() const
+{
+ if (hasWave())
+ return waveReader.wave->id;
+ return 0;
+}
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::setWave(const Wave& w, float samplerateRatio)
+Frame Data::getWaveSize() const
{
- m_waveReader.wave = &w;
- m_waveId = w.id;
-
- if (samplerateRatio != 1.0f) {
- Frame begin = state->begin.load();
- Frame end = state->end.load();
- Frame shift = state->shift.load();
- state->begin.store(begin * samplerateRatio);
- state->end.store(end * samplerateRatio);
- state->shift.store(shift * samplerateRatio);
- }
+ return hasWave() ? waveReader.wave->getBuffer().countFrames() : 0;
}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
-void SamplePlayer::setInvalidWave()
+void react(channel::Data& ch, const eventDispatcher::Event& e)
{
- m_waveReader.wave = nullptr;
- m_waveId = 0;
+ if (e.type == eventDispatcher::EventType::CHANNEL_PITCH)
+ ch.samplePlayer->pitch = std::get<float>(e.data);
}
-
/* -------------------------------------------------------------------------- */
-
-void SamplePlayer::kickIn(Frame f)
+void advance(const channel::Data& ch, const sequencer::Event& e)
{
- assert(hasWave());
-
- state->tracker.store(f);
- m_channelState->playStatus.store(ChannelStatus::PLAY);
+ sampleAdvancer::advance(ch, e);
}
-
/* -------------------------------------------------------------------------- */
-
-bool SamplePlayer::shouldLoop() const
+void render(const channel::Data& ch)
{
- ChannelStatus playStatus = m_channelState->playStatus.load();
- SamplePlayerMode mode = state->mode.load();
-
- return (mode == SamplePlayerMode::LOOP_BASIC ||
- mode == SamplePlayerMode::LOOP_REPEAT ||
- mode == SamplePlayerMode::SINGLE_ENDLESS) && playStatus == ChannelStatus::PLAY;
-}
+ if (!isPlaying_(ch))
+ return;
+ const Frame begin = ch.samplePlayer->begin;
+ const Frame end = ch.samplePlayer->end;
-/* -------------------------------------------------------------------------- */
+ /* Make sure tracker stays within begin-end range. */
+ Frame tracker = std::clamp(ch.state->tracker.load(), begin, end);
-bool SamplePlayer::hasWave() const { return m_waveReader.wave != nullptr; }
-bool SamplePlayer::hasLogicalWave() const { return hasWave() && m_waveReader.wave->isLogical(); }
-bool SamplePlayer::hasEditedWave() const { return hasWave() && m_waveReader.wave->isEdited(); }
+ /* If rewinding, fill the tail first, then reset the tracker to the begin
+ point. The rest is performed as usual. */
+ if (ch.state->rewinding)
+ {
+ if (tracker < end)
+ {
+ fillBuffer_(ch, tracker, 0);
+ ch.samplePlayer->waveReader.last();
+ }
+ ch.state->rewinding = false;
+ tracker = begin;
+ }
+
+ WaveReader::Result res = fillBuffer_(ch, tracker, ch.state->offset);
+ tracker += res.used;
+
+ /* If tracker has looped, special care is needed for the rendering. If the
+ channel is in loop mode, fill the second part of the buffer with data
+ coming from the sample's head. */
+
+ if (tracker >= end)
+ {
+ ch.samplePlayer->waveReader.last();
+ tracker = begin;
+ sampleAdvancer::onLastFrame(ch); // TODO - better moving this to samplerAdvancer::advance
+ if (shouldLoop_(ch) && res.generated < ch.buffer->audio.countFrames())
+ tracker += fillBuffer_(ch, tracker, res.generated).used;
+ }
-/* -------------------------------------------------------------------------- */
+ ch.state->offset = 0;
+ ch.state->tracker.store(tracker);
+}
+/* -------------------------------------------------------------------------- */
-ID SamplePlayer::getWaveId() const
+void loadWave(channel::Data& ch, Wave* w)
{
- return m_waveId;
+ ch.samplePlayer->waveReader.wave = w;
+
+ ch.state->tracker.store(0);
+ ch.samplePlayer->shift = 0;
+ ch.samplePlayer->begin = 0;
+
+ if (w != nullptr)
+ {
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ ch.name = w->getBasename(/*ext=*/false);
+ ch.samplePlayer->end = w->getBuffer().countFrames() - 1;
+ }
+ else
+ {
+ ch.state->playStatus.store(ChannelStatus::EMPTY);
+ ch.name = "";
+ ch.samplePlayer->end = 0;
+ }
}
-
/* -------------------------------------------------------------------------- */
+void setWave(channel::Data& ch, Wave* w, float samplerateRatio)
+{
+ setWave_(ch.samplePlayer.value(), w, samplerateRatio);
+}
+
+/* -------------------------------------------------------------------------- */
-Frame SamplePlayer::getWaveSize() const
+void kickIn(channel::Data& ch, Frame f)
{
- return hasWave() ? m_waveReader.wave->getSize() : 0;
+ ch.state->tracker.store(f);
+ ch.state->playStatus.store(ChannelStatus::PLAY);
}
-}} // giada::m::
+} // namespace giada::m::samplePlayer
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_SAMPLE_PLAYER_H
#define G_CHANNEL_SAMPLE_PLAYER_H
-
-#include "core/types.h"
-#include "core/const.h"
-#include "core/mixer.h" // TODO - forward declare
-#include "core/audioBuffer.h" // TODO - forward declare
+#include "core/channels/sampleAdvancer.h"
+#include "core/channels/sampleReactor.h"
#include "core/channels/waveReader.h"
-#include "core/channels/sampleController.h"
-
+#include "core/const.h"
+#include "core/types.h"
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-class Wave;
-struct SamplePlayerState;
-class SamplePlayer
+struct Data;
+}
+namespace giada::m::patch
{
-public:
-
- SamplePlayer(ChannelState*);
- SamplePlayer(const patch::Channel& p, ChannelState*);
- SamplePlayer(const SamplePlayer&, ChannelState* c=nullptr);
-
- void parse(const mixer::Event& e) const;
- void advance(Frame bufferSize) const;
- void render(AudioBuffer& out) const;
-
- bool hasWave() const;
- bool hasLogicalWave() const;
- bool hasEditedWave() const;
- ID getWaveId() const;
- Frame getWaveSize() const;
-
- /* loadWave
- Loads Wave 'w' into this channel and sets it up (name, markers, ...). */
-
- void loadWave(const Wave* w);
-
- /* setWave
- Just sets the pointer to a Wave object. Used during de-serialization. The
- ratio is used to adjust begin/end points in case of patch vs. conf sample
- rate mismatch. */
-
- void setWave(const Wave& w, float samplerateRatio);
-
- /* setInvalidWave
- Same as setWave(nullptr) plus the invalid ID (i.e. 0). */
-
- void setInvalidWave();
-
- /* kickIn
- Starts the player right away at frame 'f'. Used when launching a loop after
- being live recorded. */
-
- void kickIn(Frame f);
-
-
- /* state
- Pointer to mutable SamplePlayerState state. */
-
- std::unique_ptr<SamplePlayerState> state;
-
-private:
-
- bool shouldLoop() const;
-
- ID m_waveId;
+struct Channel;
+}
+namespace giada::m::samplePlayer
+{
+struct Data
+{
+ Data(Resampler* r);
+ Data(const patch::Channel& p, float samplerateRatio, Resampler* r);
+ Data(const Data& o) = default;
+ Data(Data&& o) = default;
+ Data& operator=(const Data&) = default;
+ Data& operator=(Data&&) = default;
+
+ bool hasWave() const;
+ bool hasLogicalWave() const;
+ bool hasEditedWave() const;
+ bool isAnyLoopMode() const;
+ ID getWaveId() const;
+ Frame getWaveSize() const;
+ Wave* getWave() const;
+
+ float pitch;
+ SamplePlayerMode mode;
+ Frame shift;
+ Frame begin;
+ Frame end;
+ bool velocityAsVol; // Velocity drives volume
+ WaveReader waveReader;
+};
- /* m_waveReader
- Used to read data from Wave and fill incoming buffer. */
+void react(channel::Data& ch, const eventDispatcher::Event& e);
+void advance(const channel::Data& ch, const sequencer::Event& e);
+void render(const channel::Data& ch);
- WaveReader m_waveReader;
+/* loadWave
+Loads Wave 'w' into channel ch and sets it up (name, markers, ...). */
- /* m_sampleController
- Managers events for this Sample Player. */
+void loadWave(channel::Data& ch, Wave* w);
- SampleController m_sampleController;
+/* setWave
+Just sets the pointer to a Wave object. Used during de-serialization. The
+ratio is used to adjust begin/end points in case of patch vs. conf sample
+rate mismatch. If nullptr, set the wave to invalid. */
- /* m_channelState
- Pointer to Channel state. Needed to alter the playStatus status when the
- sample is over. */
+void setWave(channel::Data& ch, Wave* w, float samplerateRatio);
- ChannelState* m_channelState;
-};
-}} // giada::m::
+/* kickIn
+Starts the player right away at frame 'f'. Used when launching a loop after
+being live recorded. */
+void kickIn(channel::Data& ch, Frame f);
+} // namespace giada::m::samplePlayer
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "sampleReactor.h"
+#include "core/channels/channel.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "src/core/model/model.h"
+#include "utils/math.h"
+#include <cassert>
+
+namespace giada::m::sampleReactor
+{
+namespace
+{
+constexpr int Q_ACTION_PLAY = 0;
+constexpr int Q_ACTION_REWIND = 1;
+
+void press_(channel::Data& ch, int velocity);
+void release_(channel::Data& ch);
+void kill_(channel::Data& ch);
+void onStopBySeq_(channel::Data& ch);
+void toggleReadActions_(channel::Data& ch);
+ChannelStatus pressWhileOff_(channel::Data& ch, int velocity, bool isLoop);
+ChannelStatus pressWhilePlay_(channel::Data& ch, SamplePlayerMode mode, bool isLoop);
+void rewind_(channel::Data& ch, Frame localFrame = 0);
+
+/* -------------------------------------------------------------------------- */
+
+void press_(channel::Data& ch, int velocity)
+{
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ SamplePlayerMode mode = ch.samplePlayer->mode;
+ bool isLoop = ch.samplePlayer->isAnyLoopMode();
+
+ switch (playStatus)
+ {
+ case ChannelStatus::OFF:
+ playStatus = pressWhileOff_(ch, velocity, isLoop);
+ break;
+
+ case ChannelStatus::PLAY:
+ playStatus = pressWhilePlay_(ch, mode, isLoop);
+ break;
+
+ case ChannelStatus::WAIT:
+ playStatus = ChannelStatus::OFF;
+ break;
+
+ case ChannelStatus::ENDING:
+ playStatus = ChannelStatus::PLAY;
+ break;
+
+ default:
+ break;
+ }
+
+ ch.state->playStatus.store(playStatus);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void release_(channel::Data& ch)
+{
+ /* Key release is meaningful only for SINGLE_PRESS modes. */
+
+ if (ch.samplePlayer->mode != SamplePlayerMode::SINGLE_PRESS)
+ return;
+
+ /* Kill it if it's SINGLE_PRESS is playing. Otherwise there might be a
+ quantization step in progress that would play the channel later on:
+ disable it. */
+
+ if (ch.state->playStatus.load() == ChannelStatus::PLAY)
+ kill_(ch);
+ else if (sequencer::quantizer.hasBeenTriggered())
+ sequencer::quantizer.clear();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void kill_(channel::Data& ch)
+{
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ ch.state->tracker.store(ch.samplePlayer->begin);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void onStopBySeq_(channel::Data& ch)
+{
+ G_DEBUG("onStopBySeq ch=" << ch.id);
+
+ ChannelStatus playStatus = ch.state->playStatus.load();
+ bool isReadingActions = ch.state->readActions.load();
+ bool isLoop = ch.samplePlayer->isAnyLoopMode();
+
+ switch (playStatus)
+ {
+
+ case ChannelStatus::WAIT:
+ /* Loop-mode channels in wait status get stopped right away. */
+ if (isLoop)
+ ch.state->playStatus.store(ChannelStatus::OFF);
+ break;
+
+ case ChannelStatus::PLAY:
+ if (conf::conf.chansStopOnSeqHalt && (isLoop || isReadingActions))
+ kill_(ch);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+ChannelStatus pressWhileOff_(channel::Data& ch, int velocity, bool isLoop)
+{
+ if (isLoop)
+ return ChannelStatus::WAIT;
+
+ if (ch.samplePlayer->velocityAsVol)
+ ch.volume_i = u::math::map(velocity, G_MAX_VELOCITY, G_MAX_VOLUME);
+
+ if (clock::canQuantize())
+ {
+ sequencer::quantizer.trigger(Q_ACTION_PLAY + ch.id);
+ return ChannelStatus::OFF;
+ }
+ else
+ return ChannelStatus::PLAY;
+}
+
+/* -------------------------------------------------------------------------- */
+
+ChannelStatus pressWhilePlay_(channel::Data& ch, SamplePlayerMode mode, bool isLoop)
+{
+ if (mode == SamplePlayerMode::SINGLE_RETRIG)
+ {
+ if (clock::canQuantize())
+ sequencer::quantizer.trigger(Q_ACTION_REWIND + ch.id);
+ else
+ rewind_(ch);
+ return ChannelStatus::PLAY;
+ }
+
+ if (isLoop || mode == SamplePlayerMode::SINGLE_ENDLESS)
+ return ChannelStatus::ENDING;
+
+ if (mode == SamplePlayerMode::SINGLE_BASIC)
+ {
+ rewind_(ch);
+ return ChannelStatus::OFF;
+ }
+
+ return ChannelStatus::OFF;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void toggleReadActions_(channel::Data& ch)
+{
+ if (clock::isRunning() && ch.state->recStatus.load() == ChannelStatus::PLAY && !conf::conf.treatRecsAsLoops)
+ kill_(ch);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void rewind_(channel::Data& ch, Frame localFrame)
+{
+ if (ch.isPlaying())
+ {
+ ch.state->rewinding = true;
+ ch.state->offset = localFrame;
+ }
+ else
+ ch.state->tracker.store(ch.samplePlayer->begin);
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+Data::Data(ID channelId)
+{
+ sequencer::quantizer.schedule(Q_ACTION_PLAY + channelId, [channelId](Frame delta) {
+ channel::Data& ch = model::get().getChannel(channelId);
+ ch.state->offset = delta;
+ ch.state->playStatus.store(ChannelStatus::PLAY);
+ });
+
+ sequencer::quantizer.schedule(Q_ACTION_REWIND + channelId, [channelId](Frame delta) {
+ channel::Data& ch = model::get().getChannel(channelId);
+ rewind_(ch, delta);
+ });
+}
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+void react(channel::Data& ch, const eventDispatcher::Event& e)
+{
+ if (!ch.hasWave())
+ return;
+
+ switch (e.type)
+ {
+
+ case eventDispatcher::EventType::KEY_PRESS:
+ press_(ch, std::get<int>(e.data));
+ break;
+
+ case eventDispatcher::EventType::KEY_RELEASE:
+ release_(ch);
+ break;
+
+ case eventDispatcher::EventType::KEY_KILL:
+ kill_(ch);
+ break;
+
+ case eventDispatcher::EventType::SEQUENCER_STOP:
+ onStopBySeq_(ch);
+ break;
+
+ case eventDispatcher::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
+ toggleReadActions_(ch);
+ break;
+
+ default:
+ break;
+ }
+}
+} // namespace giada::m::sampleReactor
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_CHANNEL_SAMPLE_REACTOR_H
+#define G_CHANNEL_SAMPLE_REACTOR_H
+
+#include "core/eventDispatcher.h"
+#include "core/quantizer.h"
+
+namespace giada::m::channel
+{
+struct Data;
+}
+
+/* sampleReactor
+Reacts to manual events sent to Sample Channels: key press, key release,
+sequencer stop, ... . */
+
+namespace giada::m::sampleReactor
+{
+struct Data
+{
+ Data(ID channelId);
+ Data(const Data&) = default;
+ Data(Data&&) = default;
+ Data& operator=(const Data&) = default;
+ Data& operator=(Data&&) = default;
+};
+
+void react(channel::Data& ch, const eventDispatcher::Event& e);
+} // namespace giada::m::sampleReactor
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "core/conf.h"
-#include "core/patch.h"
-#include "state.h"
-
-
-namespace giada {
-namespace m
-{
-MidiLearnerState::MidiLearnerState()
-: enabled (true)
-, filter (0)
-{
-}
-
-
-MidiLearnerState::MidiLearnerState(const patch::Channel& p)
-: enabled (p.midiIn)
-, filter (p.midiInFilter)
-, keyPress (p.midiInKeyPress)
-, keyRelease (p.midiInKeyRel)
-, kill (p.midiInKill)
-, arm (p.midiInArm)
-, volume (p.midiInVolume)
-, mute (p.midiInMute)
-, solo (p.midiInSolo)
-, readActions (p.midiInReadActions)
-, pitch (p.midiInPitch)
-{
-}
-
-
-MidiLearnerState::MidiLearnerState(const MidiLearnerState& o)
-: enabled (o.enabled.load())
-, filter (o.filter.load())
-, keyPress (o.keyPress)
-, keyRelease (o.keyRelease)
-, kill (o.kill)
-, arm (o.arm)
-, volume (o.volume)
-, mute (o.mute)
-, solo (o.solo)
-, readActions (o.readActions)
-, pitch (o.pitch)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool MidiLearnerState::isAllowed(int c) const
-{
- int filter_ = filter.load();
- bool enabled_ = enabled.load();
-
- return enabled_ && (filter_ == -1 || filter_ == c);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-MidiLighterState::MidiLighterState()
-: enabled(false)
-, playing(0x0)
-{
-}
-
-
-MidiLighterState::MidiLighterState(const patch::Channel& p)
-: enabled(p.midiOutL)
-, playing(p.midiOutLplaying)
-, mute (p.midiOutLmute)
-, solo (p.midiOutLsolo)
-{
-}
-
-
-MidiLighterState::MidiLighterState(const MidiLighterState& o)
-: enabled(o.enabled.load())
-, playing(o.playing)
-, mute (o.mute)
-, solo (o.solo)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-MidiSenderState::MidiSenderState()
-: enabled(false)
-, filter (0)
-{
-}
-
-
-MidiSenderState::MidiSenderState(const patch::Channel& p)
-: enabled(p.midiOut)
-, filter (p.midiOutChan)
-{
-}
-
-
-MidiSenderState::MidiSenderState(const MidiSenderState& o)
-: enabled(o.enabled.load())
-, filter (o.filter.load())
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-MidiReceiverState::MidiReceiverState()
-{
- midiBuffer.ensureSize(G_DEFAULT_VST_MIDIBUFFER_SIZE);
-}
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-SamplePlayerState::SamplePlayerState()
-: tracker (0)
-, pitch (G_DEFAULT_PITCH)
-, mode (SamplePlayerMode::SINGLE_BASIC)
-, velocityAsVol(false)
-, rewinding (false)
-, quantizing (false)
-, offset (0)
-{
-}
-
-
-SamplePlayerState::SamplePlayerState(const SamplePlayerState& o)
-: tracker (o.tracker.load())
-, pitch (o.pitch.load())
-, mode (o.mode.load())
-, shift (o.shift.load())
-, begin (o.begin.load())
-, end (o.end.load())
-, velocityAsVol(o.velocityAsVol.load())
-, rewinding (o.rewinding)
-, quantizing (o.quantizing)
-, offset (o.offset)
-, quantizer (o.quantizer)
-{
-}
-
-
-SamplePlayerState::SamplePlayerState(const patch::Channel& p)
-: tracker (0)
-, pitch (p.pitch)
-, mode (p.mode)
-, shift (p.shift)
-, begin (p.begin)
-, end (p.end)
-, velocityAsVol(p.midiInVeloAsVol)
-, rewinding (false)
-, quantizing (false)
-, offset (0)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SamplePlayerState::isAnyLoopMode() const
-{
- SamplePlayerMode m = mode.load();
-
- return m == SamplePlayerMode::LOOP_BASIC ||
- m == SamplePlayerMode::LOOP_ONCE ||
- m == SamplePlayerMode::LOOP_REPEAT ||
- m == SamplePlayerMode::LOOP_ONCE_BAR;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-AudioReceiverState::AudioReceiverState(const conf::Conf& c)
-: inputMonitor (c.inputMonitorDefaultOn)
-, overdubProtection(c.overdubProtectionDefaultOn)
-{
-}
-
-
-AudioReceiverState::AudioReceiverState(const patch::Channel& p)
-: inputMonitor (p.inputMonitor)
-, overdubProtection(p.overdubProtection)
-{
-}
-
-
-AudioReceiverState::AudioReceiverState(const AudioReceiverState& o)
-: inputMonitor (o.inputMonitor.load())
-, overdubProtection(o.overdubProtection.load())
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-ChannelState::ChannelState(ID id, Frame bufferSize)
-: id (id)
-, playStatus (ChannelStatus::OFF)
-, recStatus (ChannelStatus::OFF)
-, volume (G_DEFAULT_VOL)
-, pan (G_DEFAULT_PAN)
-, mute (false)
-, solo (false)
-, armed (false)
-, key (0)
-, readActions(true)
-, buffer (bufferSize, G_MAX_IO_CHANS)
-, hasActions (false)
-, height (G_GUI_UNIT)
-, volume_i (1.0f)
-{
-}
-
-
-ChannelState::ChannelState(const ChannelState& o)
-: id (o.id)
-, playStatus (o.playStatus.load())
-, recStatus (o.recStatus.load())
-, volume (o.volume.load())
-, pan (o.pan.load())
-, mute (o.mute.load())
-, solo (o.solo.load())
-, armed (o.armed.load())
-, key (o.key.load())
-, readActions(o.readActions.load())
-, buffer (o.buffer)
-, hasActions (o.hasActions)
-, name (o.name)
-, height (o.height)
-, volume_i (o.volume_i)
-{
-}
-
-
-ChannelState::ChannelState(const patch::Channel& p, Frame bufferSize)
-: id (p.id)
-, playStatus (ChannelStatus::OFF)
-, recStatus (ChannelStatus::OFF)
-, volume (p.volume)
-, pan (p.pan)
-, mute (p.mute)
-, solo (p.solo)
-, armed (p.armed)
-, key (p.key)
-, readActions(p.readActions)
-, buffer (bufferSize, G_MAX_IO_CHANS)
-, hasActions (p.hasActions)
-, name (p.name)
-, height (p.height)
-, volume_i (1.0f)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool ChannelState::isPlaying() const
-{
- ChannelStatus s = playStatus.load();
- return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING;
-}
-}} // giada::m::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_CHANNEL_STATE_H
-#define G_CHANNEL_STATE_H
-
-
-#include <string>
-#include <atomic>
-#include "core/const.h"
-#include "core/types.h"
-#include "core/quantizer.h"
-#include "core/audioBuffer.h"
-#include "core/midiLearnParam.h"
-#ifdef WITH_VST
-#include "deps/juce-config.h"
-#endif
-
-
-namespace giada {
-namespace m {
-namespace conf
-{
-struct Conf;
-}
-namespace patch
-{
-struct Channel;
-}
-struct MidiLearnerState
-{
- MidiLearnerState();
- MidiLearnerState(const patch::Channel& p);
- MidiLearnerState(const MidiLearnerState& o);
-
- /* isAllowed
- Tells whether the current MIDI channel 'channel' is enabled to receive MIDI
- data. */
-
- bool isAllowed(int channel) const;
-
- /* enabled
- Tells whether MIDI learning is enabled for the current channel. */
-
- std::atomic<bool> enabled;
-
- /* filter
- Which MIDI channel should be filtered out when receiving MIDI messages.
- If -1 means 'all'. */
-
- std::atomic<int> filter;
-
- /* MIDI learning fields. */
-
- MidiLearnParam keyPress;
- MidiLearnParam keyRelease;
- MidiLearnParam kill;
- MidiLearnParam arm;
- MidiLearnParam volume;
- MidiLearnParam mute;
- MidiLearnParam solo;
- MidiLearnParam readActions; // Sample Channels only
- MidiLearnParam pitch; // Sample Channels only
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-struct MidiLighterState
-{
- MidiLighterState();
- MidiLighterState(const patch::Channel& p);
- MidiLighterState(const MidiLighterState& o);
-
- /* enabled
- Tells whether MIDI ligthing is enabled or not. */
-
- std::atomic<bool> enabled;
-
- /* MIDI learning fields for MIDI ligthing. */
-
- MidiLearnParam playing;
- MidiLearnParam mute;
- MidiLearnParam solo;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-struct MidiSenderState
-{
- MidiSenderState();
- MidiSenderState(const patch::Channel& p);
- MidiSenderState(const MidiSenderState& o);
-
- /* enabled
- Tells whether MIDI output is enabled or not. */
-
- std::atomic<bool> enabled;
-
- /* filter
- Which MIDI channel data should be sent to. */
-
- std::atomic<int> filter;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-struct MidiReceiverState
-{
- MidiReceiverState();
-
- /* midiBuffer
- Contains MIDI events to be sent to plug-ins. */
-
- juce::MidiBuffer midiBuffer;
-};
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-struct SamplePlayerState
-{
- SamplePlayerState();
- SamplePlayerState(const patch::Channel& p);
- SamplePlayerState(const SamplePlayerState& o);
-
- bool isAnyLoopMode() const;
-
- std::atomic<Frame> tracker;
- std::atomic<float> pitch;
- std::atomic<SamplePlayerMode> mode;
- std::atomic<Frame> shift;
- std::atomic<Frame> begin;
- std::atomic<Frame> end;
-
- /* velocityAsVol
- Velocity drives volume. */
-
- std::atomic<bool> velocityAsVol;
-
- bool rewinding;
- bool quantizing;
- Frame offset;
- Quantizer quantizer;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-struct AudioReceiverState
-{
- AudioReceiverState(const conf::Conf& c);
- AudioReceiverState(const patch::Channel& p);
- AudioReceiverState(const AudioReceiverState& o);
-
- std::atomic<bool> inputMonitor;
- std::atomic<bool> overdubProtection;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-struct ChannelState
-{
- ChannelState(ID id, Frame bufferSize);
- ChannelState(const patch::Channel& p, Frame bufferSize);
- ChannelState(const ChannelState& o);
-
- bool isPlaying() const;
-
- ID id;
-
- std::atomic<ChannelStatus> playStatus;
- std::atomic<ChannelStatus> recStatus;
- std::atomic<float> volume;
- std::atomic<float> pan;
- std::atomic<bool> mute;
- std::atomic<bool> solo;
- std::atomic<bool> armed;
- std::atomic<int> key;
- std::atomic<bool> readActions;
-
- /* buffer (internal)
- Working buffer for internal processing. */
-
- AudioBuffer buffer;
-
- bool hasActions;
- std::string name;
- Pixel height;
-
- /* volume_i (internal)
- Internal volume used for volume automation and velocity-drives-volume mode
- on Sample Channels. */
-
- float volume_i;
-};
-}} // giada::m::
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <memory>
-#include <cassert>
-#include <algorithm>
+#include "waveReader.h"
+#include "core/audioBuffer.h"
#include "core/const.h"
#include "core/model/model.h"
-#include "core/audioBuffer.h"
#include "core/wave.h"
#include "utils/log.h"
-#include "waveReader.h"
-
-
-namespace giada {
-namespace m
-{
-WaveReader::WaveReader()
-: wave (nullptr),
- m_srcState(nullptr)
-{
- allocateSrc();
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-WaveReader::WaveReader(const WaveReader& o)
-: wave (o.wave),
- m_srcState(nullptr)
-{
- allocateSrc();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-WaveReader::WaveReader(WaveReader&& o)
-: wave (o.wave),
- m_srcState(nullptr)
-{
- moveSrc(&o.m_srcState);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-WaveReader& WaveReader::operator=(const WaveReader& o)
-{
- if (this == &o) return *this;
- wave = o.wave;
- allocateSrc();
- return *this;
-}
-
+#include <algorithm>
+#include <cassert>
+#include <memory>
-WaveReader& WaveReader::operator=(WaveReader&& o)
+namespace giada::m
{
- if (this == &o) return *this;
- wave = o.wave;
- moveSrc(&o.m_srcState);
- return *this;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-WaveReader::~WaveReader()
+WaveReader::WaveReader(Resampler* r)
+: wave(nullptr)
+, m_resampler(r)
{
- if (m_srcState != nullptr)
- src_delete(m_srcState);
}
-
/* -------------------------------------------------------------------------- */
-
-Frame WaveReader::fill(AudioBuffer& out, Frame start, Frame offset, float pitch) const
+WaveReader::Result WaveReader::fill(AudioBuffer& out, Frame start, Frame max,
+ Frame offset, float pitch) const
{
assert(wave != nullptr);
assert(start >= 0);
+ assert(max <= wave->getBuffer().countFrames());
assert(offset < out.countFrames());
- model::WavesLock l(model::waves); // TODO dependency
-
- if (pitch == 1.0) return fillCopy(out, start, offset);
- else return fillResampled(out, start, offset, pitch);
+ if (pitch == 1.0f)
+ return fillCopy(out, start, max, offset);
+ else
+ return fillResampled(out, start, max, offset, pitch);
}
-
/* -------------------------------------------------------------------------- */
-
-Frame WaveReader::fillResampled(AudioBuffer& dest, Frame start, Frame offset, float pitch) const
+WaveReader::Result WaveReader::fillResampled(AudioBuffer& dest, Frame start,
+ Frame max, Frame offset, float pitch) const
{
- SRC_DATA srcData;
-
- srcData.data_in = wave->getFrame(start); // Source data
- srcData.input_frames = wave->getSize() - start; // How many readable frames
- srcData.data_out = dest[offset]; // Destination (processed data)
- srcData.output_frames = dest.countFrames() - offset; // How many frames to process
- srcData.end_of_input = false;
- srcData.src_ratio = 1 / pitch;
-
- src_process(m_srcState, &srcData);
-
- return srcData.input_frames_used;
+ Resampler::Result res = m_resampler->process(
+ /*input=*/wave->getBuffer()[0],
+ /*inputPos=*/start,
+ /*inputLen=*/max,
+ /*output=*/dest[offset],
+ /*outputLen=*/dest.countFrames() - offset,
+ /*pitch=*/pitch);
+
+ return {
+ static_cast<int>(res.used),
+ static_cast<int>(res.generated)};
}
-
/* -------------------------------------------------------------------------- */
-
-Frame WaveReader::fillCopy(AudioBuffer& dest, Frame start, Frame offset) const
+WaveReader::Result WaveReader::fillCopy(AudioBuffer& dest, Frame start, Frame max, Frame offset) const
{
Frame used = dest.countFrames() - offset;
- if (used > wave->getSize() - start)
- used = wave->getSize() - start;
+ if (used > max - start)
+ used = max - start;
- dest.copyData(wave->getFrame(start), used, G_MAX_IO_CHANS, offset);
+ dest.set(wave->getBuffer(), used, start, offset);
- return used;
+ return {used, used};
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void WaveReader::allocateSrc()
-{
- m_srcState = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr);
- if (m_srcState == nullptr) {
- u::log::print("[WaveReader] unable to allocate memory for SRC_STATE!\n");
- throw std::bad_alloc();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void WaveReader::moveSrc(SRC_STATE** other)
+void WaveReader::last() const
{
- if (m_srcState != nullptr)
- src_delete(m_srcState);
- m_srcState = *other;
- *other = nullptr;
+ if (m_resampler != nullptr)
+ m_resampler->last();
}
-}} // giada::m::
+} // namespace giada::m
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CHANNEL_WAVE_READER_H
#define G_CHANNEL_WAVE_READER_H
-
-#include <samplerate.h>
#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave;
+class AudioBuffer;
+class Resampler;
class WaveReader final
{
public:
+ /* Result
+ A Result object is returned by the fill() function below, containing the
+ number of frames used and generated from a buffer filling operation. The
+ two values are different only when pitch is != 1.0, where a chunk of audio
+ in input (used) might result in a longer or shorter portion of audio in
+ output (generated). */
- WaveReader();
- WaveReader(const WaveReader&);
- WaveReader(WaveReader&&);
- WaveReader& operator=(const WaveReader&);
- WaveReader& operator=(WaveReader&&);
- ~WaveReader();
+ struct Result
+ {
+ Frame used, generated;
+ };
- Frame fill(AudioBuffer& out, Frame start, Frame offset, float pitch) const;
+ WaveReader() = delete;
+ WaveReader(Resampler* r);
- /* wave
- Wave object. Might be null if the channel has no sample. */
+ /* fill
+ Fills audio buffer 'out' with data coming from Wave, copying it from 'start'
+ frame up to 'max'. The buffer is filled starting at 'offset'. */
- const Wave* wave;
+ Result fill(AudioBuffer& out, Frame start, Frame max, Frame offset,
+ float pitch) const;
-private:
+ /* last
+ Call this when you are about to process the last chunk of pitched data.
+ Ignored if pitch == 1.0. */
- Frame fillResampled(AudioBuffer& out, Frame start, Frame offset, float pitch) const;
- Frame fillCopy (AudioBuffer& out, Frame start, Frame offset) const;
+ void last() const;
- void allocateSrc();
- void moveSrc(SRC_STATE** o);
+ /* wave
+ Wave object. Might be null if the channel has no sample. */
- /* srcState
- Struct from libsamplerate. */
+ Wave* wave;
- SRC_STATE* m_srcState;
-};
-}} // giada::m::
+private:
+ Result fillResampled(AudioBuffer& out, Frame start, Frame max, Frame offset,
+ float pitch) const;
+ Result fillCopy(AudioBuffer& out, Frame start, Frame max, Frame offset) const;
+ Resampler* m_resampler;
+};
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <atomic>
-#include <cassert>
-#include "glue/main.h"
-#include "glue/events.h"
-#include "core/model/model.h"
+#include "clock.h"
#include "core/conf.h"
-#include "core/sequencer.h"
#include "core/const.h"
#include "core/kernelAudio.h"
-#include "core/mixerHandler.h"
#include "core/kernelMidi.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/recorderHandler.h"
+#include "core/sequencer.h"
+#include "glue/events.h"
+#include "utils/log.h"
#include "utils/math.h"
-#include "clock.h"
-
+#include <atomic>
+#include <cassert>
namespace giada::m::clock
{
namespace
{
-std::atomic<int> currentFrameWait_(0);
-std::atomic<int> currentFrame_(0);
-std::atomic<int> currentBeat_(0);
-
/* quantizerStep_
Tells how many frames to wait to perform a quantized action. */
/* midiTC*
MIDI timecode variables. */
-int midiTCrate_ = 0; // Send MTC data every midiTCrate_ frames
+int midiTCrate_ = 0; // Send MTC data every midiTCrate_ frames
int midiTCframes_ = 0;
int midiTCseconds_ = 0;
int midiTCminutes_ = 0;
kernelAudio::JackState jackStatePrev_;
#endif
-
/* -------------------------------------------------------------------------- */
/* recomputeFrames_
void recomputeFrames_(model::Clock& c)
{
c.framesInLoop = static_cast<int>((conf::conf.samplerate * (60.0f / c.bpm)) * c.beats);
- c.framesInBar = static_cast<int>(c.framesInLoop / (float) c.bars);
- c.framesInBeat = static_cast<int>(c.framesInLoop / (float) c.beats);
+ c.framesInBar = static_cast<int>(c.framesInLoop / (float)c.bars);
+ c.framesInBeat = static_cast<int>(c.framesInLoop / (float)c.beats);
c.framesInSeq = c.framesInBeat * G_MAX_BEATS;
if (c.quantize != 0)
quantizerStep_ = c.framesInBeat / c.quantize;
}
-} // {anonymous}
+/* -------------------------------------------------------------------------- */
+
+void setBpm_(float current)
+{
+ float ratio = model::get().clock.bpm / current;
+
+ model::get().clock.bpm = current;
+ recomputeFrames_(model::get().clock);
+
+ m::recorderHandler::updateBpm(ratio, quantizerStep_);
+
+ model::swap(model::SwapType::HARD);
+
+ u::log::print("[clock::setBpm_] Bpm changed to %f\n", current);
+}
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init(int sampleRate, float midiTCfps)
{
- midiTCrate_ = static_cast<int>((sampleRate / midiTCfps) * G_MAX_IO_CHANS); // stereo values
+ midiTCrate_ = static_cast<int>((sampleRate / midiTCfps) * G_MAX_IO_CHANS); // stereo values
- model::onSwap(model::clock, [&](model::Clock& c)
- {
- c.bars = G_DEFAULT_BARS;
- c.beats = G_DEFAULT_BEATS;
- c.bpm = G_DEFAULT_BPM;
- c.quantize = G_DEFAULT_QUANTIZE;
- recomputeFrames_(c);
- });
-}
+ model::get().clock.bars = G_DEFAULT_BARS;
+ model::get().clock.beats = G_DEFAULT_BEATS;
+ model::get().clock.bpm = G_DEFAULT_BPM;
+ model::get().clock.quantize = G_DEFAULT_QUANTIZE;
+ recomputeFrames_(model::get().clock);
+ model::swap(model::SwapType::NONE);
+}
/* -------------------------------------------------------------------------- */
-
void recomputeFrames()
{
- model::onSwap(model::clock, [&](model::Clock& c) { recomputeFrames_(c); });
+ recomputeFrames_(model::get().clock);
+ model::swap(model::SwapType::NONE);
}
-
/* -------------------------------------------------------------------------- */
-
bool isRunning()
{
- model::ClockLock lock(model::clock);
-
- return model::clock.get()->status == ClockStatus::RUNNING;
+ return model::get().clock.status == ClockStatus::RUNNING;
}
-
bool isActive()
{
- model::ClockLock lock(model::clock);
-
- ClockStatus status = model::clock.get()->status;
- return status == ClockStatus::RUNNING || status == ClockStatus::WAITING;
+ const model::Clock& c = model::get().clock;
+ return c.status == ClockStatus::RUNNING || c.status == ClockStatus::WAITING;
}
-
bool quantoHasPassed()
{
- return clock::getQuantizerValue() != 0 && currentFrame_.load() % quantizerStep_ == 0;
+ const model::Clock& c = model::get().clock;
+ return clock::getQuantizerValue() != 0 && c.state->currentFrame.load() % quantizerStep_ == 0;
}
-
bool isOnBar()
{
- model::ClockLock lock(model::clock);
+ const model::Clock& c = model::get().clock;
- const model::Clock* c = model::clock.get();
-
- int currentFrame = currentFrame_.load();
+ int currentFrame = c.state->currentFrame.load();
- if (c->status == ClockStatus::WAITING || currentFrame == 0)
+ if (c.status == ClockStatus::WAITING || currentFrame == 0)
return false;
- return currentFrame % c->framesInBar == 0;
+ return currentFrame % c.framesInBar == 0;
}
-
bool isOnBeat()
{
- model::ClockLock lock(model::clock);
-
- const model::Clock* c = model::clock.get();
-
- if (c->status == ClockStatus::WAITING)
- return currentFrameWait_.load() % c->framesInBeat == 0;
- return currentFrame_.load() % c->framesInBeat == 0;
-}
+ const model::Clock& c = model::get().clock;
+ if (c.status == ClockStatus::WAITING)
+ return c.state->currentFrameWait.load() % c.framesInBeat == 0;
+ return c.state->currentFrame.load() % c.framesInBeat == 0;
+}
bool isOnFirstBeat()
{
- return currentFrame_.load() == 0;
+ return model::get().clock.state->currentFrame.load() == 0;
}
-
/* -------------------------------------------------------------------------- */
-
void setBpm(float b)
-{
+{
b = std::clamp(b, G_MIN_BPM, G_MAX_BPM);
- model::onSwap(model::clock, [&](model::Clock& c)
+ /* If JACK is being used, let it handle the bpm change. */
+
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
{
- c.bpm = b;
- recomputeFrames_(c);
- });
-}
+ kernelAudio::jackSetBpm(b);
+ return;
+ }
+#endif
+ setBpm_(b);
+}
void setBeats(int newBeats, int newBars)
{
newBeats = std::clamp(newBeats, 1, G_MAX_BEATS);
newBars = std::clamp(newBars, 1, newBeats); // Bars cannot be greater than beats
- model::onSwap(model::clock, [&](model::Clock& c)
- {
- c.beats = newBeats;
- c.bars = newBars;
- recomputeFrames_(c);
- });
-}
+ model::get().clock.beats = newBeats;
+ model::get().clock.bars = newBars;
+ recomputeFrames_(model::get().clock);
+ model::swap(model::SwapType::HARD);
+}
void setQuantize(int q)
{
- model::onSwap(model::clock, [&](model::Clock& c)
- {
- c.quantize = q;
- recomputeFrames_(c);
- });
-}
+ model::get().clock.quantize = q;
+ recomputeFrames_(model::get().clock);
+ model::swap(model::SwapType::HARD);
+}
void setStatus(ClockStatus s)
{
- model::onSwap(model::clock, [&](model::Clock& c)
+ model::get().clock.status = s;
+ model::swap(model::SwapType::SOFT);
+
+ if (s == ClockStatus::RUNNING)
{
- c.status = s;
- });
-
- if (s == ClockStatus::RUNNING) {
- if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M) {
+ if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
+ {
kernelMidi::send(MIDI_START, -1, -1);
kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
}
}
- else
- if (s == ClockStatus::STOPPED) {
+ else if (s == ClockStatus::STOPPED)
+ {
if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
kernelMidi::send(MIDI_STOP, -1, -1);
}
}
-
/* -------------------------------------------------------------------------- */
-
-void incrCurrentFrame()
+void advance(Frame amount)
{
- model::ClockLock lock(model::clock);
-
- const model::Clock* c = model::clock.get();
-
- if (c->status == ClockStatus::WAITING) {
- int f = currentFrameWait_.load() + 1;
- f %= c->framesInLoop;
- currentFrameWait_.store(f);
+ const model::Clock& c = model::get().clock;
+
+ if (c.status == ClockStatus::WAITING)
+ {
+ int f = (c.state->currentFrameWait.load() + amount) % c.framesInLoop;
+ c.state->currentFrameWait.store(f);
return;
}
- int f = currentFrame_.load() + 1;
- int b = currentBeat_.load();
+ int f = (c.state->currentFrame.load() + amount) % c.framesInLoop;
+ int b = f / c.framesInBeat;
- f %= c->framesInLoop;
- b = f / c->framesInBeat;
-
- currentFrame_.store(f);
- currentBeat_.store(b);
+ c.state->currentFrame.store(f);
+ c.state->currentBeat.store(b);
}
+/* -------------------------------------------------------------------------- */
void rewind()
{
- currentFrame_.store(0);
- currentBeat_.store(0);
- currentFrameWait_.store(0);
-
+ const model::Clock& c = model::get().clock;
+
+ c.state->currentFrame.store(0);
+ c.state->currentBeat.store(0);
+ c.state->currentFrameWait.store(0);
+
sendMIDIrewind();
}
-
/* -------------------------------------------------------------------------- */
-
void sendMIDIsync()
{
- model::ClockLock lock(model::clock);
-
- const model::Clock* c = model::clock.get();
-
+ const model::Clock& c = model::get().clock;
+
/* Sending MIDI sync while waiting is meaningless. */
- if (c->status == ClockStatus::WAITING)
+ if (c.status == ClockStatus::WAITING)
return;
- int currentFrame = currentFrame_.load();
+ int currentFrame = c.state->currentFrame.load();
/* TODO - only Master (_M) is implemented so far. */
- if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M) {
- if (currentFrame % (c->framesInBeat / 24) == 0)
+ if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
+ {
+ if (currentFrame % (c.framesInBeat / 24) == 0)
kernelMidi::send(MIDI_CLOCK, -1, -1);
return;
}
- if (conf::conf.midiSync == MIDI_SYNC_MTC_M) {
+ if (conf::conf.midiSync == MIDI_SYNC_MTC_M)
+ {
/* check if a new timecode frame has passed. If so, send MIDI TC
* quarter frames. 8 quarter frames, divided in two branches:
* 1-4 and 5-8. We check timecode frame's parity: if even, send
* range 1-4, if odd send 5-8. */
- if (currentFrame % midiTCrate_ != 0) // no timecode frame passed
+ if (currentFrame % midiTCrate_ != 0) // no timecode frame passed
return;
/* frame low nibble
* seconds low nibble
* seconds high nibble */
- if (midiTCframes_ % 2 == 0) {
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes_ & 0x0F) | 0x00, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes_ >> 4) | 0x10, -1);
+ if (midiTCframes_ % 2 == 0)
+ {
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes_ & 0x0F) | 0x00, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes_ >> 4) | 0x10, -1);
kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds_ & 0x0F) | 0x20, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds_ >> 4) | 0x30, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds_ >> 4) | 0x30, -1);
}
/* minutes low nibble
* hours low nibble
* hours high nibble SMPTE frame rate */
- else {
+ else
+ {
kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes_ & 0x0F) | 0x40, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes_ >> 4) | 0x50, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours_ & 0x0F) | 0x60, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours_ >> 4) | 0x70, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes_ >> 4) | 0x50, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours_ & 0x0F) | 0x60, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours_ >> 4) | 0x70, -1);
}
midiTCframes_++;
/* check if total timecode frames are greater than timecode fps:
* if so, a second has passed */
- if (midiTCframes_ > conf::conf.midiTCfps) {
+ if (midiTCframes_ > conf::conf.midiTCfps)
+ {
midiTCframes_ = 0;
midiTCseconds_++;
- if (midiTCseconds_ >= 60) {
+ if (midiTCseconds_ >= 60)
+ {
midiTCminutes_++;
midiTCseconds_ = 0;
- if (midiTCminutes_ >= 60) {
+ if (midiTCminutes_ >= 60)
+ {
midiTChours_++;
midiTCminutes_ = 0;
}
}
}
-
/* -------------------------------------------------------------------------- */
-
void sendMIDIrewind()
{
midiTCframes_ = 0;
* be sent. The Full Frame is a SysEx message that encodes the entire
* SMPTE time in one message */
- if (conf::conf.midiSync == MIDI_SYNC_MTC_M) {
- kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
- kernelMidi::send(0x01, 0x01, 0x00); // hours 0
- kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
- kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
+ if (conf::conf.midiSync == MIDI_SYNC_MTC_M)
+ {
+ kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
+ kernelMidi::send(0x01, 0x01, 0x00); // hours 0
+ kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
+ kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
}
- else
- if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
+ else if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_AUDIO_JACK
void recvJackSync()
kernelAudio::JackState jackStateCurr = kernelAudio::jackTransportQuery();
- if (jackStateCurr != jackStatePrev_) {
-
- if (jackStateCurr.frame != jackStatePrev_.frame && jackStateCurr.frame == 0) {
-G_DEBUG("JackState received - rewind to frame 0");
- sequencer::rewind();
+ if (jackStateCurr != jackStatePrev_)
+ {
+ if (jackStateCurr.frame != jackStatePrev_.frame && jackStateCurr.frame == 0)
+ {
+ G_DEBUG("JackState received - rewind to frame 0");
+ sequencer::rawRewind();
}
- if (jackStateCurr.bpm != jackStatePrev_.bpm && jackStateCurr.bpm > 1.0f) { // 0 bpm if Jack does not send that info
-G_DEBUG("JackState received - bpm=" << jackStateCurr.bpm);
- c::main::setBpm(jackStateCurr.bpm);
+ // jackStateCurr.bpm == 0 if JACK doesn't send that info
+ if (jackStateCurr.bpm != jackStatePrev_.bpm && jackStateCurr.bpm > 1.0f)
+ {
+ G_DEBUG("JackState received - bpm=" << jackStateCurr.bpm);
+ setBpm_(jackStateCurr.bpm);
}
- if (jackStateCurr.running != jackStatePrev_.running) {
-G_DEBUG("JackState received - running=" << jackStateCurr.running);
- jackStateCurr.running ? sequencer::start() : sequencer::stop();
+ if (jackStateCurr.running != jackStatePrev_.running)
+ {
+ G_DEBUG("JackState received - running=" << jackStateCurr.running);
+ jackStateCurr.running ? sequencer::rawStart() : sequencer::rawStop();
}
}
#endif
-
/* -------------------------------------------------------------------------- */
-
bool canQuantize()
{
- model::ClockLock lock(model::clock);
-
- const model::Clock* c = model::clock.get();
- return c->quantize > 0 && c->status == ClockStatus::RUNNING;
-}
+ const model::Clock& c = model::get().clock;
+ return c.quantize > 0 && c.status == ClockStatus::RUNNING;
+}
/* -------------------------------------------------------------------------- */
-
Frame quantize(Frame f)
{
- if (!canQuantize()) return f;
+ if (!canQuantize())
+ return f;
return u::math::quantize(f, quantizerStep_) % getFramesInLoop(); // No overflow
}
+/* -------------------------------------------------------------------------- */
+
+int getCurrentFrame() { return model::get().clock.state->currentFrame.load(); }
+int getCurrentBeat() { return model::get().clock.state->currentBeat.load(); }
+int getQuantizerStep() { return quantizerStep_; }
+ClockStatus getStatus() { return model::get().clock.status; }
+int getFramesInLoop() { return model::get().clock.framesInLoop; }
+int getFramesInBar() { return model::get().clock.framesInBar; }
+int getFramesInBeat() { return model::get().clock.framesInBeat; }
+int getFramesInSeq() { return model::get().clock.framesInSeq; }
+int getQuantizerValue() { return model::get().clock.quantize; }
+float getBpm() { return model::get().clock.bpm; }
+int getBeats() { return model::get().clock.beats; }
+int getBars() { return model::get().clock.bars; }
+
+/* -------------------------------------------------------------------------- */
+
+float getCurrentSecond()
+{
+ return getCurrentFrame() / static_cast<float>(conf::conf.samplerate);
+}
/* -------------------------------------------------------------------------- */
+Frame getMaxFramesInLoop()
+{
+ return (conf::conf.samplerate * (60.0f / G_MIN_BPM)) * getBeats();
+}
+
+/* -------------------------------------------------------------------------- */
-int getCurrentFrame() { return currentFrame_.load(); }
-int getCurrentBeat() { return currentBeat_.load(); }
-int getQuantizerStep() { return quantizerStep_; }
-ClockStatus getStatus() { model::ClockLock lock(model::clock); return model::clock.get()->status; }
-int getFramesInLoop() { model::ClockLock lock(model::clock); return model::clock.get()->framesInLoop; }
-int getFramesInBar() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBar; }
-int getFramesInBeat() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBeat; }
-int getFramesInSeq() { model::ClockLock lock(model::clock); return model::clock.get()->framesInSeq; }
-int getQuantizerValue() { model::ClockLock lock(model::clock); return model::clock.get()->quantize; }
-float getBpm() { model::ClockLock lock(model::clock); return model::clock.get()->bpm; }
-int getBeats() { model::ClockLock lock(model::clock); return model::clock.get()->beats; }
-int getBars() { model::ClockLock lock(model::clock); return model::clock.get()->bars; }
-} // giada::m::clock::
+float calcBpmFromRec(Frame recordedFrames)
+{
+ return (60.0f * getBeats()) / (recordedFrames / static_cast<float>(conf::conf.samplerate));
+}
+} // namespace giada::m::clock
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CLOCK_H
#define G_CLOCK_H
-
#include "types.h"
-
namespace giada::m::clock
{
void init(int sampleRate, float midiTCfps);
/* sendMIDIsync
Generates MIDI sync output data. */
-
+/*TODO - move this to giada::m::sync*/
void sendMIDIsync();
/* sendMIDIrewind
Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */
-
+/*TODO - move this to giada::m::sync*/
void sendMIDIrewind();
#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) || defined(G_OS_MAC)
+/*TODO - move this to giada::m::sync*/
void recvJackSync();
#endif
-float getBpm();
-int getBeats();
-int getBars();
-int getCurrentBeat();
-int getCurrentFrame();
-int getFramesInBar();
-int getFramesInBeat();
-int getFramesInLoop();
-int getFramesInSeq();
-int getQuantizerValue();
-int getQuantizerStep();
+float getBpm();
+int getBeats();
+int getBars();
+int getCurrentBeat();
+int getCurrentFrame();
+float getCurrentSecond();
+int getFramesInBar();
+int getFramesInBeat();
+int getFramesInLoop();
+int getFramesInSeq();
+int getQuantizerValue();
+int getQuantizerStep();
ClockStatus getStatus();
-/* incrCurrentFrame
-Increases current frame by a single step (+1). */
+/* getMaxFramesInLoop
+Returns how many frames the current loop length might contain at the slowest
+speed possible (G_MIN_BPM). Call this whenever you change the number or beats. */
+
+Frame getMaxFramesInLoop();
-void incrCurrentFrame();
+/* advance
+Increases current frame by a specific amount. */
+
+void advance(Frame amount);
/* quantoHasPassed
Tells whether a quantizer unit has passed yet. */
void rewind();
void setStatus(ClockStatus s);
-} // giada::m::clock::
+/* calcBpmFromRec
+Given the amount of recorded frames, returns the speed of the current
+performance. Used while input recording in FREE mode. */
+
+float calcBpmFromRec(Frame recordedFrames);
+} // namespace giada::m::clock
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <fstream>
-#include <cassert>
-#include <string>
-#include <FL/Fl.H>
+#include "conf.h"
+#include "core/const.h"
+#include "core/types.h"
#include "deps/json/single_include/nlohmann/json.hpp"
#include "utils/fs.h"
#include "utils/log.h"
-#include "core/const.h"
-#include "core/types.h"
-#include "conf.h"
-
+#include <FL/Fl.H>
+#include <cassert>
+#include <fstream>
+#include <string>
namespace nl = nlohmann;
-
-namespace giada {
-namespace m {
-namespace conf
+namespace giada::m::conf
{
namespace
{
std::string confFilePath_ = "";
std::string confDirPath_ = "";
-
/* -------------------------------------------------------------------------- */
-
void sanitize_()
{
- conf.soundDeviceOut = std::max(0, conf.soundDeviceOut);
- conf.channelsOut = std::max(0, conf.channelsOut);
+ conf.soundDeviceOut = std::max(0, conf.soundDeviceOut);
+ conf.channelsOutCount = G_MAX_IO_CHANS;
+ conf.channelsOutStart = std::max(0, conf.channelsOutStart);
+ conf.channelsInCount = std::max(1, conf.channelsInCount);
+ conf.channelsInStart = std::max(0, conf.channelsInStart);
}
-
/* -------------------------------------------------------------------------- */
-
/* createConfigFolder
Creates local folder where to put the configuration file. Path differs from OS
to OS. */
u::log::print("[conf::createConfigFolder] .giada folder not present. Updating...\n");
- if (u::fs::mkdir(confDirPath_)) {
+ if (u::fs::mkdir(confDirPath_))
+ {
u::log::print("[conf::createConfigFolder] status: ok\n");
return 1;
}
- else {
+ else
+ {
u::log::print("[conf::createConfigFolder] status: error!\n");
return 0;
}
#endif
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Conf conf;
-
/* -------------------------------------------------------------------------- */
-
void init()
{
conf = Conf();
#endif
}
-
/* -------------------------------------------------------------------------- */
-
bool read()
{
init();
nl::json j = nl::json::parse(ifs);
- conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode);
- conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem);
- conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut);
- conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn);
- conf.channelsOut = j.value(CONF_KEY_CHANNELS_OUT, conf.channelsOut);
- conf.channelsInCount = j.value(CONF_KEY_CHANNELS_IN_COUNT, conf.channelsInCount);
- conf.channelsInStart = j.value(CONF_KEY_CHANNELS_IN_START, conf.channelsInStart);
- conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate);
- conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize);
- conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput);
- conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality);
- conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem);
- conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut);
- conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn);
- conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath);
- conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap);
- conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync);
- conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps);
- conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt);
- conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops);
- conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn);
- conf.overdubProtectionDefaultOn = j.value(CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON, conf.overdubProtectionDefaultOn);
- conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath);
- conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath);
- conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath);
- conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX);
- conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY);
- conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW);
- conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH);
- conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX);
- conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY);
- conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW);
- conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH);
- conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition);
- conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath);
- conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue);
- conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX);
- conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY);
- conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW);
- conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH);
- conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom);
- conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal);
- conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn);
- conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX);
- conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY);
- conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW);
- conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH);
- conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal);
- conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn);
- conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY);
- conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH);
- conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH);
- conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH);
- conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH);
- conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX);
- conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY);
- conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX);
- conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY);
- conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW);
- conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH);
- conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode);
- conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel);
- conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled);
- conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter);
- conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind);
- conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop);
- conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec);
- conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec);
- conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome);
- conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn);
- conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut);
- conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble);
- conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf);
+ conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode);
+ conf.showTooltips = j.value(CONF_KEY_SHOW_TOOLTIPS, conf.showTooltips);
+ conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem);
+ conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut);
+ conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn);
+ conf.channelsOutCount = j.value(CONF_KEY_CHANNELS_OUT_COUNT, conf.channelsOutCount);
+ conf.channelsOutStart = j.value(CONF_KEY_CHANNELS_OUT_START, conf.channelsOutStart);
+ conf.channelsInCount = j.value(CONF_KEY_CHANNELS_IN_COUNT, conf.channelsInCount);
+ conf.channelsInStart = j.value(CONF_KEY_CHANNELS_IN_START, conf.channelsInStart);
+ conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate);
+ conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize);
+ conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput);
+ conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality);
+ conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem);
+ conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut);
+ conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn);
+ conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath);
+ conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap);
+ conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync);
+ conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps);
+ conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt);
+ conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops);
+ conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn);
+ conf.overdubProtectionDefaultOn = j.value(CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON, conf.overdubProtectionDefaultOn);
+ conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath);
+ conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath);
+ conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath);
+ conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX);
+ conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY);
+ conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW);
+ conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH);
+ conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX);
+ conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY);
+ conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW);
+ conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH);
+ conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition);
+ conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath);
+ conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue);
+ conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX);
+ conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY);
+ conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW);
+ conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH);
+ conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom);
+ conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal);
+ conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn);
+ conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX);
+ conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY);
+ conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW);
+ conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH);
+ conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal);
+ conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn);
+ conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY);
+ conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH);
+ conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH);
+ conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH);
+ conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH);
+ conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX);
+ conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY);
+ conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX);
+ conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY);
+ conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW);
+ conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH);
+ conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode);
+ conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel);
+ conf.inputRecMode = j.value(CONF_KEY_INPUT_REC_MODE, conf.inputRecMode);
+ conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled);
+ conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter);
+ conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind);
+ conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop);
+ conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec);
+ conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec);
+ conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome);
+ conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn);
+ conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut);
+ conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble);
+ conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf);
#ifdef WITH_VST
- conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX);
- conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY);
- conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW);
- conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH);
- conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod);
+ conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX);
+ conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY);
+ conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW);
+ conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH);
+ conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod);
#endif
sanitize_();
return true;
}
-
/* -------------------------------------------------------------------------- */
-
bool write()
{
if (!createConfigFolder_())
j[CONF_KEY_HEADER] = "GIADACFG";
j[CONF_KEY_LOG_MODE] = conf.logMode;
+ j[CONF_KEY_SHOW_TOOLTIPS] = conf.showTooltips;
j[CONF_KEY_SOUND_SYSTEM] = conf.soundSystem;
j[CONF_KEY_SOUND_DEVICE_OUT] = conf.soundDeviceOut;
j[CONF_KEY_SOUND_DEVICE_IN] = conf.soundDeviceIn;
- j[CONF_KEY_CHANNELS_OUT] = conf.channelsOut;
+ j[CONF_KEY_CHANNELS_OUT_COUNT] = conf.channelsOutCount;
+ j[CONF_KEY_CHANNELS_OUT_START] = conf.channelsOutStart;
j[CONF_KEY_CHANNELS_IN_COUNT] = conf.channelsInCount;
j[CONF_KEY_CHANNELS_IN_START] = conf.channelsInStart;
j[CONF_KEY_SAMPLERATE] = conf.samplerate;
j[CONF_KEY_MIDI_INPUT_H] = conf.midiInputH;
j[CONF_KEY_REC_TRIGGER_MODE] = static_cast<int>(conf.recTriggerMode);
j[CONF_KEY_REC_TRIGGER_LEVEL] = conf.recTriggerLevel;
+ j[CONF_KEY_INPUT_REC_MODE] = static_cast<int>(conf.inputRecMode);
#ifdef WITH_VST
- j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX;
- j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY;
- j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW;
- j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH;
- j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod;
+ j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX;
+ j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY;
+ j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW;
+ j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH;
+ j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod;
#endif
- std::ofstream ofs(confFilePath_);
- if (!ofs.good()) {
+ std::ofstream ofs(confFilePath_);
+ if (!ofs.good())
+ {
u::log::print("[conf::write] unable to write configuration file!\n");
return false;
}
- ofs << j;
+ ofs << j;
return true;
}
-}}} // giada::m::conf::
\ No newline at end of file
+} // namespace giada::m::conf
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CONF_H
#define G_CONF_H
-
-#include <string>
-#include "utils/gui.h"
#include "core/const.h"
#include "core/types.h"
+#include "utils/gui.h"
+#include <string>
-
-namespace giada {
-namespace m {
-namespace conf
+namespace giada::m::conf
{
struct Conf
{
- int logMode = LOG_MODE_MUTE;
- int soundSystem = G_DEFAULT_SOUNDSYS;
- int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
- int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
- int channelsOut = 0;
- int channelsInCount = 0;
- int channelsInStart = 0;
- int samplerate = G_DEFAULT_SAMPLERATE;
- int buffersize = G_DEFAULT_BUFSIZE;
- bool limitOutput = false;
- int rsmpQuality = 0;
+ int logMode = LOG_MODE_MUTE;
+ bool showTooltips = true;
+ int soundSystem = G_DEFAULT_SOUNDSYS;
+ int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+ int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
+ int channelsOutCount = G_MAX_IO_CHANS;
+ int channelsOutStart = 0;
+ int channelsInCount = 1;
+ int channelsInStart = 0;
+ int samplerate = G_DEFAULT_SAMPLERATE;
+ int buffersize = G_DEFAULT_BUFSIZE;
+ bool limitOutput = false;
+ int rsmpQuality = 0;
int midiSystem = 0;
int midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
int mainWindowH = G_MIN_GUI_HEIGHT;
int browserX = u::gui::centerWindowX(G_DEFAULT_SUBWINDOW_W);
- int browserY = u::gui::centerWindowY(G_DEFAULT_SUBWINDOW_H);
- int browserW = G_DEFAULT_SUBWINDOW_W;
+ int browserY = u::gui::centerWindowY(G_DEFAULT_SUBWINDOW_H);
+ int browserW = G_DEFAULT_SUBWINDOW_W;
int browserH = G_DEFAULT_SUBWINDOW_H;
int browserPosition;
int browserLastValue;
int actionEditorY = u::gui::centerWindowY(G_DEFAULT_SUBWINDOW_H);
int actionEditorX = u::gui::centerWindowX(G_DEFAULT_SUBWINDOW_W);
- int actionEditorW = G_DEFAULT_SUBWINDOW_W;
- int actionEditorH = G_DEFAULT_SUBWINDOW_H;
+ int actionEditorW = G_DEFAULT_SUBWINDOW_W;
+ int actionEditorH = G_DEFAULT_SUBWINDOW_H;
int actionEditorZoom = 100;
int actionEditorGridVal = 0;
int actionEditorGridOn = false;
int sampleEditorX;
int sampleEditorY;
- int sampleEditorW = G_DEFAULT_SUBWINDOW_W;
- int sampleEditorH = G_DEFAULT_SUBWINDOW_H;
+ int sampleEditorW = G_DEFAULT_SUBWINDOW_W;
+ int sampleEditorH = G_DEFAULT_SUBWINDOW_H;
int sampleEditorGridVal = 0;
int sampleEditorGridOn = false;
- int midiInputX;
- int midiInputY;
- int midiInputW = G_DEFAULT_SUBWINDOW_W;
+ int midiInputX;
+ int midiInputY;
+ int midiInputW = G_DEFAULT_SUBWINDOW_W;
int midiInputH = G_DEFAULT_SUBWINDOW_H;
int pianoRollY = -1;
int pianoRollH = 422;
- int sampleActionEditorH = 40;
- int velocityEditorH = 40;
- int envelopeEditorH = 40;
+ int sampleActionEditorH = 40;
+ int velocityEditorH = 40;
+ int envelopeEditorH = 40;
int pluginListX;
int pluginListY;
RecTriggerMode recTriggerMode = RecTriggerMode::NORMAL;
float recTriggerLevel = G_DEFAULT_REC_TRIGGER_LEVEL;
+ InputRecMode inputRecMode = InputRecMode::FREE;
bool midiInEnabled = false;
int midiInFilter = -1;
#ifdef WITH_VST
- int pluginChooserX;
+ int pluginChooserX;
int pluginChooserY;
- int pluginChooserW = G_DEFAULT_SUBWINDOW_W;
+ int pluginChooserW = G_DEFAULT_SUBWINDOW_W;
int pluginChooserH = G_DEFAULT_SUBWINDOW_H;
int pluginSortMethod = 0;
#endif
};
-
/* -------------------------------------------------------------------------- */
-
extern Conf conf;
-
/* -------------------------------------------------------------------------- */
-
void init();
bool read();
bool write();
-}}} // giada::m::conf::
+} // namespace giada::m::conf
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_CONST_H
#define G_CONST_H
-
#include <cstdint>
-
+#include <iostream>
/* -- debug ----------------------------------------------------------------- */
#ifndef NDEBUG
- #define G_DEBUG_MODE
- #define G_DEBUG(x) std::cerr << __FILE__ << "::" << __func__ << "() - " << x << "\n";
+#define G_DEBUG_MODE
+// TODO - move G_DEBUG macro definition to u::log
+#define G_DEBUG(x) std::cerr << __FILE__ << "::" << __func__ << "() - " << x << "\n";
#else
- #define G_DEBUG(x) do {} while (0)
+#define G_DEBUG(x) \
+ do \
+ { \
+ } while (0)
#endif
-
/* -- environment ----------------------------------------------------------- */
#if defined(_WIN32)
- #define G_OS_WINDOWS
+#define G_OS_WINDOWS
#elif defined(__APPLE__)
- #define G_OS_MAC
+#define G_OS_MAC
#elif defined(__linux__)
- #define G_OS_LINUX
+#define G_OS_LINUX
#elif defined(__FreeBSD__)
- #define G_OS_FREEBSD
+#define G_OS_FREEBSD
#endif
#ifndef BUILD_DATE
- #define BUILD_DATE __DATE__
+#define BUILD_DATE __DATE__
#endif
-
-
/* -- version --------------------------------------------------------------- */
constexpr auto G_APP_NAME = "Giada";
-constexpr auto G_VERSION_STR = "0.17.1";
+constexpr auto G_VERSION_STR = "0.18.1";
constexpr int G_VERSION_MAJOR = 0;
-constexpr int G_VERSION_MINOR = 17;
+constexpr int G_VERSION_MINOR = 18;
constexpr int G_VERSION_PATCH = 1;
constexpr auto CONF_FILENAME = "giada.conf";
#ifdef G_OS_WINDOWS
- #define G_SLASH '\\'
- #define G_SLASH_STR "\\"
+#define G_SLASH '\\'
+#define G_SLASH_STR "\\"
#else
- #define G_SLASH '/'
- #define G_SLASH_STR "/"
+#define G_SLASH '/'
+#define G_SLASH_STR "/"
#endif
+/* -- Engine ---------------------------------------------------------------- */
+/* G_EVENT_DISPATCHER_RATE_MS
+The amount of sleep between each Event Dispatcher cycle. It should be lower
+than the audio thread sleep time. Note: this value will obviously increase the
+live input latency, keep it small! */
+constexpr int G_EVENT_DISPATCHER_RATE_MS = 5;
/* -- GUI ------------------------------------------------------------------- */
constexpr float G_GUI_REFRESH_RATE = 1 / 30.0f; // 30 fps
constexpr int G_GUI_FONT_SIZE_BASE = 12;
constexpr int G_GUI_INNER_MARGIN = 4;
constexpr int G_GUI_OUTER_MARGIN = 8;
-constexpr int G_GUI_UNIT = 20; // base unit for elements
+constexpr int G_GUI_UNIT = 20; // base unit for elements
constexpr int G_GUI_ZOOM_FACTOR = 2;
-#define G_COLOR_RED fl_rgb_color(28, 32, 80)
-#define G_COLOR_BLUE fl_rgb_color(113, 31, 31)
-#define G_COLOR_RED_ALERT fl_rgb_color(239, 75, 53)
-#define G_COLOR_LIGHT_2 fl_rgb_color(200, 200, 200)
-#define G_COLOR_LIGHT_1 fl_rgb_color(170, 170, 170)
-#define G_COLOR_GREY_4 fl_rgb_color(78, 78, 78)
-#define G_COLOR_GREY_3 fl_rgb_color(54, 54, 54)
-#define G_COLOR_GREY_2 fl_rgb_color(37, 37, 37)
-#define G_COLOR_GREY_1_5 fl_rgb_color(28, 28, 28)
-#define G_COLOR_GREY_1 fl_rgb_color(25, 25, 25)
-#define G_COLOR_BLACK fl_rgb_color(0, 0, 0)
-
-
+#define G_COLOR_RED fl_rgb_color(28, 32, 80)
+#define G_COLOR_BLUE fl_rgb_color(113, 31, 31)
+#define G_COLOR_RED_ALERT fl_rgb_color(239, 75, 53)
+#define G_COLOR_LIGHT_2 fl_rgb_color(200, 200, 200)
+#define G_COLOR_LIGHT_1 fl_rgb_color(170, 170, 170)
+#define G_COLOR_GREY_4 fl_rgb_color(78, 78, 78)
+#define G_COLOR_GREY_3 fl_rgb_color(54, 54, 54)
+#define G_COLOR_GREY_2 fl_rgb_color(37, 37, 37)
+#define G_COLOR_GREY_1_5 fl_rgb_color(28, 28, 28)
+#define G_COLOR_GREY_1 fl_rgb_color(25, 25, 25)
+#define G_COLOR_BLACK fl_rgb_color(0, 0, 0)
/* -- MIN/MAX values -------------------------------------------------------- */
-constexpr float G_MIN_BPM = 20.0f;
-constexpr auto G_MIN_BPM_STR = "20.0";
-constexpr float G_MAX_BPM = 999.0f;
-constexpr auto G_MAX_BPM_STR = "999.0";
-constexpr int G_MAX_BEATS = 32;
-constexpr int G_MAX_BARS = 32;
-constexpr int G_MAX_QUANTIZE = 8;
-constexpr float G_MIN_DB_SCALE = 60.0f;
-constexpr int G_MIN_COLUMN_WIDTH = 140;
-constexpr float G_MAX_BOOST_DB = 20.0f;
-constexpr float G_MIN_PITCH = 0.1f;
-constexpr float G_MAX_PITCH = 4.0f;
-constexpr float G_MAX_PAN = 1.0f;
-constexpr float G_MAX_VOLUME = 1.0f;
-constexpr int G_MAX_GRID_VAL = 64;
-constexpr int G_MIN_BUF_SIZE = 8;
-constexpr int G_MAX_BUF_SIZE = 4096;
-constexpr int G_MIN_GUI_WIDTH = 816;
-constexpr int G_MIN_GUI_HEIGHT = 510;
-constexpr int G_MAX_IO_CHANS = 2;
-constexpr int G_MAX_VELOCITY = 0x7F;
-constexpr int G_MAX_MIDI_CHANS = 16;
-constexpr int G_MAX_POLYPHONY = 32;
-constexpr int G_MAX_QUEUE_EVENTS = 32;
-constexpr int G_MAX_QUANTIZER_SIZE = 8;
-
-
+constexpr float G_MIN_BPM = 20.0f;
+constexpr auto G_MIN_BPM_STR = "20.0";
+constexpr float G_MAX_BPM = 999.0f;
+constexpr auto G_MAX_BPM_STR = "999.0";
+constexpr int G_MAX_BEATS = 32;
+constexpr int G_MAX_BARS = 32;
+constexpr int G_MAX_QUANTIZE = 8;
+constexpr float G_MIN_DB_SCALE = 60.0f;
+constexpr int G_MIN_COLUMN_WIDTH = 140;
+constexpr float G_MAX_BOOST_DB = 20.0f;
+constexpr float G_MIN_PITCH = 0.1f;
+constexpr float G_MAX_PITCH = 4.0f;
+constexpr float G_MAX_PAN = 1.0f;
+constexpr float G_MAX_VOLUME = 1.0f;
+constexpr int G_MAX_GRID_VAL = 64;
+constexpr int G_MIN_BUF_SIZE = 8;
+constexpr int G_MAX_BUF_SIZE = 4096;
+constexpr int G_MIN_GUI_WIDTH = 816;
+constexpr int G_MIN_GUI_HEIGHT = 510;
+constexpr int G_MAX_IO_CHANS = 2;
+constexpr int G_MAX_VELOCITY = 0x7F;
+constexpr int G_MAX_MIDI_CHANS = 16;
+constexpr int G_MAX_POLYPHONY = 32;
+constexpr int G_MAX_DISPATCHER_EVENTS = 32;
+constexpr int G_MAX_SEQUENCER_EVENTS = 128; // Per block
+constexpr int G_MAX_QUANTIZER_SIZE = 32;
/* -- kernel audio ---------------------------------------------------------- */
-constexpr int G_SYS_API_NONE = 0x00; // 0000 0000
-constexpr int G_SYS_API_JACK = 0x01; // 0000 0001
-constexpr int G_SYS_API_ALSA = 0x02; // 0000 0010
-constexpr int G_SYS_API_DS = 0x04; // 0000 0100
-constexpr int G_SYS_API_ASIO = 0x08; // 0000 1000
-constexpr int G_SYS_API_CORE = 0x10; // 0001 0000
-constexpr int G_SYS_API_PULSE = 0x20; // 0010 0000
-constexpr int G_SYS_API_WASAPI = 0x40; // 0100 0000
-constexpr int G_SYS_API_ANY = 0x7F; // 0111 1111
-
-
+constexpr int G_SYS_API_NONE = 0;
+constexpr int G_SYS_API_JACK = 1;
+constexpr int G_SYS_API_ALSA = 2;
+constexpr int G_SYS_API_DS = 3;
+constexpr int G_SYS_API_ASIO = 4;
+constexpr int G_SYS_API_CORE = 5;
+constexpr int G_SYS_API_PULSE = 6;
+constexpr int G_SYS_API_WASAPI = 7;
/* -- kernel midi ----------------------------------------------------------- */
-constexpr int G_MIDI_API_JACK = 0x01; // 0000 0001
-constexpr int G_MIDI_API_ALSA = 0x02; // 0000 0010
-
-
+constexpr int G_MIDI_API_JACK = 0x01; // 0000 0001
+constexpr int G_MIDI_API_ALSA = 0x02; // 0000 0010
/* -- default system -------------------------------------------------------- */
#if defined(G_OS_LINUX)
- #define G_DEFAULT_SOUNDSYS G_SYS_API_NONE
+#define G_DEFAULT_SOUNDSYS G_SYS_API_NONE
#elif defined(G_OS_FREEBSD)
- #define G_DEFAULT_SOUNDSYS G_SYS_API_PULSE
+#define G_DEFAULT_SOUNDSYS G_SYS_API_PULSE
#elif defined(G_OS_WINDOWS)
- #define G_DEFAULT_SOUNDSYS G_SYS_API_DS
+#define G_DEFAULT_SOUNDSYS G_SYS_API_DS
#elif defined(G_OS_MAC)
- #define G_DEFAULT_SOUNDSYS G_SYS_API_CORE
+#define G_DEFAULT_SOUNDSYS G_SYS_API_CORE
#endif
-constexpr int G_DEFAULT_SOUNDDEV_OUT = 0; // FIXME - please override with rtAudio::getDefaultDevice (or similar)
-constexpr int G_DEFAULT_SOUNDDEV_IN = -1; // no recording by default: input disabled
+constexpr int G_DEFAULT_SOUNDDEV_OUT = -1; // disabled by default
+constexpr int G_DEFAULT_SOUNDDEV_IN = -1; // disabled by default
constexpr int G_DEFAULT_MIDI_SYSTEM = 0;
constexpr int G_DEFAULT_MIDI_PORT_IN = -1;
constexpr int G_DEFAULT_MIDI_PORT_OUT = -1;
constexpr int G_DEFAULT_SAMPLERATE = 44100;
constexpr int G_DEFAULT_BUFSIZE = 1024;
-constexpr int G_DEFAULT_BIT_DEPTH = 32; // float
+constexpr int G_DEFAULT_BIT_DEPTH = 32;
constexpr float G_DEFAULT_VOL = 1.0f;
constexpr float G_DEFAULT_PAN = 0.5f;
constexpr float G_DEFAULT_PITCH = 1.0f;
constexpr float G_DEFAULT_BPM = 120.0f;
constexpr int G_DEFAULT_BEATS = 4;
constexpr int G_DEFAULT_BARS = 1;
-constexpr int G_DEFAULT_QUANTIZE = 0; // quantizer off
-constexpr float G_DEFAULT_FADEOUT_STEP = 0.01f; // micro-fadeout speed
+constexpr int G_DEFAULT_QUANTIZE = 0; // quantizer off
+constexpr float G_DEFAULT_FADEOUT_STEP = 0.01f; // micro-fadeout speed
constexpr int G_DEFAULT_COLUMN_WIDTH = 380;
constexpr auto G_DEFAULT_PATCH_NAME = "(default patch)";
-constexpr int G_DEFAULT_ACTION_SIZE = 8192; // frames
+constexpr int G_DEFAULT_ACTION_SIZE = 8192; // frames
constexpr int G_DEFAULT_ZOOM_RATIO = 128;
constexpr float G_DEFAULT_REC_TRIGGER_LEVEL = -10.0f;
constexpr int G_DEFAULT_SUBWINDOW_W = 640;
constexpr int G_DEFAULT_SUBWINDOW_H = 480;
-constexpr int G_DEFAULT_VST_MIDIBUFFER_SIZE = 1024; // TODO - not 100% sure about this size
-
-
+constexpr int G_DEFAULT_VST_MIDIBUFFER_SIZE = 1024; // TODO - not 100% sure about this size
/* -- responses and return codes -------------------------------------------- */
constexpr int G_RES_ERR_PROCESSING = -6;
constexpr int G_RES_ERR_PATH_TOO_LONG = -3;
constexpr int G_RES_ERR_IO = -2;
constexpr int G_RES_ERR_MEMORY = -1;
-constexpr int G_RES_ERR = 0;
-constexpr int G_RES_OK = 1;
-
-
+constexpr int G_RES_ERR = 0;
+constexpr int G_RES_OK = 1;
/* -- log modes ------------------------------------------------------------- */
constexpr int LOG_MODE_STDOUT = 0x01;
constexpr int LOG_MODE_FILE = 0x02;
constexpr int LOG_MODE_MUTE = 0x04;
-
-
/* -- unique IDs of mainWin's subwindows ------------------------------------ */
/* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */
constexpr int WID_BEATS = -1;
constexpr int WID_MIDI_INPUT = -13;
constexpr int WID_MIDI_OUTPUT = -14;
-
-
/* -- patch signals --------------------------------------------------------- */
constexpr int G_PATCH_UNSUPPORTED = -2;
constexpr int G_PATCH_UNREADABLE = -1;
-constexpr int G_PATCH_INVALID = 0;
-constexpr int G_PATCH_OK = 1;
-
-
+constexpr int G_PATCH_INVALID = 0;
+constexpr int G_PATCH_OK = 1;
/* -- midimap signals ------------------------------------------------------- */
constexpr int MIDIMAP_NOT_SPECIFIED = 0x00;
constexpr int MIDIMAP_INVALID = 0x02;
constexpr int MIDIMAP_READ_OK = 0x04;
-
-
/* -- MIDI in parameters (for MIDI learning) -------------------------------- */
constexpr int G_MIDI_IN_ENABLED = 1;
constexpr int G_MIDI_IN_FILTER = 2;
constexpr int G_MIDI_IN_PITCH = 19;
constexpr int G_MIDI_IN_READ_ACTIONS = 20;
-
-
/* -- MIDI out parameters (for MIDI output and lightning) ------------------- */
-constexpr int G_MIDI_OUT_ENABLED = 1;
-constexpr int G_MIDI_OUT_L_ENABLED = 2;
-constexpr int G_MIDI_OUT_L_PLAYING = 3;
-constexpr int G_MIDI_OUT_L_MUTE = 4;
-constexpr int G_MIDI_OUT_L_SOLO = 5;
-
-
+constexpr int G_MIDI_OUT_ENABLED = 1;
+constexpr int G_MIDI_OUT_L_ENABLED = 2;
+constexpr int G_MIDI_OUT_L_PLAYING = 3;
+constexpr int G_MIDI_OUT_L_MUTE = 4;
+constexpr int G_MIDI_OUT_L_SOLO = 5;
/* -- MIDI signals -------------------------------------------------------------
Channel voices messages - controller (0xB0) is a special subset of this family:
constexpr int MIDI_START = 0xFA;
constexpr int MIDI_CONTINUE = 0xFB;
constexpr int MIDI_STOP = 0xFC;
-constexpr int MIDI_EOX = 0xF7; // end of sysex
+constexpr int MIDI_EOX = 0xF7; // end of sysex
/* midi sync constants */
constexpr int MIDI_SYNC_NONE = 0x00;
-constexpr int MIDI_SYNC_CLOCK_M = 0x01; // master
-constexpr int MIDI_SYNC_CLOCK_S = 0x02; // slave
-constexpr int MIDI_SYNC_MTC_M = 0x04; // master
-constexpr int MIDI_SYNC_MTC_S = 0x08; // slave
+constexpr int MIDI_SYNC_CLOCK_M = 0x01; // master
+constexpr int MIDI_SYNC_CLOCK_S = 0x02; // slave
+constexpr int MIDI_SYNC_MTC_M = 0x04; // master
+constexpr int MIDI_SYNC_MTC_S = 0x08; // slave
/* JSON patch keys */
constexpr auto PATCH_KEY_COLUMN_WIDTH = "width";
constexpr auto PATCH_KEY_COLUMN_CHANNELS = "channels";
constexpr auto G_PATCH_KEY_ACTION_ID = "id";
-constexpr auto G_PATCH_KEY_ACTION_CHANNEL = "channel";
-constexpr auto G_PATCH_KEY_ACTION_FRAME = "frame";
-constexpr auto G_PATCH_KEY_ACTION_EVENT = "event";
-constexpr auto G_PATCH_KEY_ACTION_PREV = "prev";
+constexpr auto G_PATCH_KEY_ACTION_CHANNEL = "channel";
+constexpr auto G_PATCH_KEY_ACTION_FRAME = "frame";
+constexpr auto G_PATCH_KEY_ACTION_EVENT = "event";
+constexpr auto G_PATCH_KEY_ACTION_PREV = "prev";
constexpr auto G_PATCH_KEY_ACTION_NEXT = "next";
/* JSON config keys */
constexpr auto CONF_KEY_HEADER = "header";
constexpr auto CONF_KEY_LOG_MODE = "log_mode";
+constexpr auto CONF_KEY_SHOW_TOOLTIPS = "show_tooltips";
constexpr auto CONF_KEY_SOUND_SYSTEM = "sound_system";
constexpr auto CONF_KEY_SOUND_DEVICE_IN = "sound_device_in";
constexpr auto CONF_KEY_SOUND_DEVICE_OUT = "sound_device_out";
-constexpr auto CONF_KEY_CHANNELS_OUT = "channels_out";
+constexpr auto CONF_KEY_CHANNELS_OUT_COUNT = "channels_out_count";
+constexpr auto CONF_KEY_CHANNELS_OUT_START = "channels_out_start";
constexpr auto CONF_KEY_CHANNELS_IN_COUNT = "channels_in_count";
constexpr auto CONF_KEY_CHANNELS_IN_START = "channels_in_start";
constexpr auto CONF_KEY_SAMPLERATE = "samplerate";
constexpr auto CONF_KEY_PLUGIN_SORT_METHOD = "plugin_sort_method";
constexpr auto CONF_KEY_REC_TRIGGER_MODE = "rec_trigger_mode";
constexpr auto CONF_KEY_REC_TRIGGER_LEVEL = "rec_trigger_level";
+constexpr auto CONF_KEY_INPUT_REC_MODE = "input_rec_mode";
/* JSON midimaps keys */
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "eventDispatcher.h"
+#include "core/clock.h"
+#include "core/const.h"
+#include "core/model/model.h"
+#include "core/sequencer.h"
+#include "core/worker.h"
+#include "utils/log.h"
+#include <functional>
+
+namespace giada::m::eventDispatcher
+{
+namespace
+{
+Worker worker_;
+
+/* eventBuffer_
+Buffer of events sent to channels for event parsing. This is filled with Events
+coming from the two event queues.*/
+
+EventBuffer eventBuffer_;
+
+/* -------------------------------------------------------------------------- */
+
+void processFuntions_()
+{
+ for (const Event& e : eventBuffer_)
+ {
+ if (e.type == EventType::FUNCTION)
+ std::get<std::function<void()>>(e.data)();
+ G_DEBUG("Event type=" << (int)e.type << ", delta=" << e.delta << ", frame=" << clock::getCurrentFrame());
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void processChannels_()
+{
+ for (channel::Data& ch : model::get().channels)
+ channel::react(ch, eventBuffer_, mixer::isChannelAudible(ch));
+ model::swap(model::SwapType::SOFT);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void processSequencer_()
+{
+ sequencer::react(eventBuffer_);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void process_()
+{
+ eventBuffer_.clear();
+
+ Event e;
+ while (UIevents.pop(e))
+ eventBuffer_.push_back(e);
+ while (MidiEvents.pop(e))
+ eventBuffer_.push_back(e);
+
+ if (eventBuffer_.size() == 0)
+ return;
+
+ processFuntions_();
+ processChannels_();
+ processSequencer_();
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+Queue<Event, G_MAX_DISPATCHER_EVENTS> UIevents;
+Queue<Event, G_MAX_DISPATCHER_EVENTS> MidiEvents;
+
+/* -------------------------------------------------------------------------- */
+
+void init()
+{
+ worker_.start(process_, /*sleep=*/G_EVENT_DISPATCHER_RATE_MS);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void pumpUIevent(Event e) { UIevents.push(e); }
+void pumpMidiEvent(Event e) { MidiEvents.push(e); }
+} // namespace giada::m::eventDispatcher
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_EVENT_DISPATCHER_H
+#define G_EVENT_DISPATCHER_H
+
+#include "core/action.h"
+#include "core/const.h"
+#include "core/queue.h"
+#include "core/ringBuffer.h"
+#include "core/types.h"
+#include <atomic>
+#include <functional>
+#include <thread>
+#include <variant>
+
+/* giada::m::eventDispatcher
+Takes events from the two queues (MIDI and UI) filled by c::events and turns
+them into actual changes in the data model. The EventDispatcher runs in a
+separate worker thread. */
+
+namespace giada::m::eventDispatcher
+{
+enum class EventType
+{
+ KEY_PRESS,
+ KEY_RELEASE,
+ KEY_KILL,
+ SEQUENCER_START,
+ SEQUENCER_STOP,
+ SEQUENCER_REWIND,
+ MIDI,
+ CHANNEL_TOGGLE_READ_ACTIONS,
+ CHANNEL_KILL_READ_ACTIONS,
+ CHANNEL_TOGGLE_ARM,
+ CHANNEL_MUTE,
+ CHANNEL_SOLO,
+ CHANNEL_VOLUME,
+ CHANNEL_PITCH,
+ CHANNEL_PAN,
+ FUNCTION
+};
+
+using EventData = std::variant<int, float, Action, std::function<void()>>;
+
+struct Event
+{
+ EventType type;
+ Frame delta = 0;
+ ID channelId = 0;
+ EventData data;
+};
+
+/* EventBuffer
+Alias for a RingBuffer containing events to be sent to engine. The double size
+is due to the presence of two distinct Queues for collecting events coming from
+other threads. See below. */
+
+using EventBuffer = RingBuffer<Event, G_MAX_DISPATCHER_EVENTS * 2>;
+
+/* Event queues
+Collect events coming from the UI or MIDI devices. Our poor's man Queue is a
+single-producer/single-consumer one, so we need two queues for two writers.
+TODO - let's add a multi-producer queue sooner or later! */
+
+extern Queue<Event, G_MAX_DISPATCHER_EVENTS> UIevents;
+extern Queue<Event, G_MAX_DISPATCHER_EVENTS> MidiEvents;
+
+void init();
+
+void pumpUIevent(Event e);
+void pumpMidiEvent(Event e);
+} // namespace giada::m::eventDispatcher
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#include "graphics.h"
-
const char* giada_logo_xpm[] = {
-"245 86 12 1",
-" c #191919",
-". c #303030",
-"+ c #404040",
-"@ c #454545",
-"# c #5A5A5A",
-"$ c #686868",
-"% c #818181",
-"& c #9C9C9C",
-"* c #B8B8B8",
-"= c #CDCDCD",
-"- c #DDDDDD",
-"; c #FEFEFE",
-" ...+++@@@@++... ",
-" .+@@@@@@@@@@@@@@@@@@@@. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
-" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" @@@@@@@@@@@@@@@@@@++$*--;;;;;;;;-=&@+@@@@@@@@@@@@@@@@@@+ ",
-" .@@@@@@@@@@@@@@@@+#&=;;;;;;;;;;;;;;;;;-*%++@@@@@@@@@@@@@@@+ ",
-" .@@@@@@@@@@@@@@@@$=;;;;;;;;;;;;;;;;;;;;;;;;&#+@@@@@@@@@@@@@@@ ",
-" .@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@ ",
-" @@@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@+ ",
-" @@@@@@@@@@@@@+*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@+ ",
-" @@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@. ",
-" .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@@@ ",
-" .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@@@ ",
-" @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@@. ",
-" +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@ ",
-" @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@+ ",
-" @@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@. ",
-" .@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;=*&%%%%&*-;;;;;;;;;;;;;;;;;;;;;%+@@@@@@@@@@ ",
-" +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;-%++@@@@@@@++@*-;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@. ",
-" .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;=+@@@@@@@@@@ ",
-" +@@@@@@@@@@-;;;;;;;;;;;;;;;;-$+@@@@@@@@@@@@@@@@@@@&;;;;;;;;;;;;;;;;;%@@@@@@@@@@. %*------=*%. $%%%%%%% $%%%%%%%%. #%%%%%%%%%%%%%%$@ %%%%%%%%% ",
-" @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@+$;;;;;;;;;;;;;;;;-+@@@@@@@@@+ #=;;;;;;;;;;;;;=$ .;;;;;;;;& &;;;;;;;;;; ;;;;;;;;;;;;;;;;;;-&# -;;;;;;;;;= ",
-" .@@@@@@@@@@;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;%@@@@@@@@@@ .=;;;;;;;;;;;;;;;;;;# +;;;;;;;;* ;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;@ .;;;;;;;;;;;. ",
-" +@@@@@@@@@%;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;@@@@@@@@@@. @;;;;;;;;;;;;;;;;;;;;;* +;;;;;;;;* +;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;* &;;;;;;;;;;;* ",
-" @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;$@@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;= +;;;;;;;;* *;;;;;;;;;;;;. .;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;;;;;;;;; ",
-" .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;=+@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;* #;;;;;;;;;;;;;+ ",
-" +@@@@@@@@@&;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@ *;;;;;;;;;;;;;;;;;;;;;;;;;;# +;;;;;;;;* $;;;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;;# *;;;;;;;;;;;;;= ",
-" @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+#*;;;-&@@@@@@@@@@@@@@;;;;;;;;;;;;;;%@@@@@@@@@. +;;;;;;;;;;-# #-;;;;;;;;;;= +;;;;;;;;* =;;;;;;;;;;;;;;# .;;;;;;;;-&&&&*-;;;;;;;;;;;; ;;;;;;;;;;;;;;;. ",
-" @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*+@@@@@@@@@@+&;;;;;;;;;;;;;*+@@@@@@@@+ &;;;;;;;;;% %;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;* .;;;;;;;;= #;;;;;;;;;;# %;;;;;;;;;;;;;;;$ ",
-".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+&;;;;;;;;;;;;#@@@@@@@@@@#;;;;;;;;;;;;;;+@@@@@@@@@ =;;;;;;;;= =;;;;;;;;;@ +;;;;;;;;* &;;;;;;;%;;;;;;;; .;;;;;;;;= ;;;;;;;;;& -;;;;;;;%;;;;;;;= ",
-".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@@-;;;;;;;;;;;;;#@@@@@@@@@ ;;;;;;;;;@ -;;;;;;;;. +;;;;;;;;* -;;;;;;;+*;;;;;;;% .;;;;;;;;= %;;;;;;;;- ;;;;;;;; ;;;;;;;;. ",
-".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;%@@@@@@@@@ ;;;;;;;;; +;;;;;;;;* .;;;;;;;; #;;;;;;;= .;;;;;;;;= ;;;;;;;;; &;;;;;;;& &;;;;;;;& ",
-"+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@ .;;;;;;;;- +;;;;;;;;* *;;;;;;;% ;;;;;;;; .;;;;;;;;= -;;;;;;;; ;;;;;;;;. @;;;;;;;- ",
-"+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;-+@@@@@@@@+-;;;;;;;;;;;;*@@@@@@@@@. .;;;;;;;;- +;;;;;;;;* ;;;;;;;;. *;;;;;;;& .;;;;;;;;= =;;;;;;;;. .;;;;;;;; ;;;;;;;;. ",
-"+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@-;;;;;;;;;;;;=@@@@@@@@@. .;;;;;;;;- .%%%%%%%%%%%%$ +;;;;;;;;* +;;;;;;;- .;;;;;;;; .;;;;;;;;= =;;;;;;;;. &;;;;;;;* %;;;;;;;* ",
-"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;% +;;;;;;;;* *;;;;;;;& ;;;;;;;;. .;;;;;;;;= *;;;;;;;;. ;;;;;;;;. .;;;;;;;; ",
-"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;; *;;;;;;;& .;;;;;;;;= *;;;;;;;;+ #;;;;;;;- ;;;;;;;;+ ",
-"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@#$$&*-;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* $;;;;;;;= .;;;;;;;; .;;;;;;;;= *;;;;;;;;+ *;;;;;;;& %;;;;;;;* ",
-"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@+@=;;;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* =;;;;;;;% -;;;;;;;# .;;;;;;;;= *;;;;;;;;. ;;;;;;;;+ ;;;;;;;;. ",
-"+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@@@@@@#-;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;. &;;;;;;;* .;;;;;;;;= =;;;;;;;;. %;;;;;;;- =;;;;;;;# ",
-"+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;;+@@@@@+++++++@@@@@@@&;;*@@@@@@@@@. .;;;;;;;;- @%%%%*;;;;;;;;& +;;;;;;;;* &;;;;;;;=.......#;;;;;;;; .;;;;;;;;= =;;;;;;;;. -;;;;;;;%.......&;;;;;;;= ",
-"+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@+#%*-;;-=&$@@@@@@@%;&@@@@@@@@@ ;;;;;;;;; %;;;;;;;;% +;;;;;;;;* -;;;;;;;;;;;;;;;;;;;;;;;;% .;;;;;;;;= -;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;. ",
-".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@*;;;;;;;;;;;%@@@@@@&%@@@@@@@@@ ;;;;;;;;; &;;;;;;;;% +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;= .;;;;;;;;= ;;;;;;;;; %;;;;;;;;;;;;;;;;;;;;;;;;;& ",
-".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@+%;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@ -;;;;;;;;# =;;;;;;;;@ +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= $;;;;;;;;- ;;;;;;;;;;;;;;;;;;;;;;;;;;- ",
-".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+*;;;;;;;;;;;;#@+*;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@@ =;;;;;;;;= @;;;;;;;;; +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;= -;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
-" @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*@@+*;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@+ &;;;;;;;;;% +;;;;;;;;;= +;;;;;;;;* +;;;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= +-;;;;;;;;;$ &;;;;;;;;;;;;;;;;;;;;;;;;;;;* ",
-" @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+$=;;;-&@@@@&;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@. .;;;;;;;;;;-@ .*;;;;;;;;;;% +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;;;;. .;;;;;;;;-&&&&&*;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ",
-" +@@@@@@@@@*;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@+@@@@@;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@ &;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;$ #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
-" .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;@ +;;;;;;;;* $;;;;;;;;% ;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;- *;;;;;;;; .;;;;;;;;* ",
-" @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* *;;;;;;;; &;;;;;;;;# .;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;= -;;;;;;;;.",
-" +@@@@@@@@@%;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@. #;;;;;;;;;;;;;;;;;;;;;% +;;;;;;;;* ;;;;;;;;* ;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;- %;;;;;;;;% %;;;;;;;;#",
-" .@@@@@@@@@@;;;;;;;;;;;;;;;;&+@@@@@@@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@@@ .-;;;;;;;;;;;;;;;;;;+ +;;;;;;;;* &;;;;;;;;$ =;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;% -;;;;;;;; ;;;;;;;;*",
-" @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@+;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@@+ $=;;;;;;;;;;;;;=$ .;;;;;;;;* =;;;;;;;; $;;;;;;;;# .;;;;;;;;;;;;;;;;;;;=%. ;;;;;;;;& *;;;;;;;;",
-" +@@@@@@@@@@-;;;;;;;;;;;;;;;;-#+@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@. .%=---;---=%. %&&&&&&&. +&&&&&&&. $&&&&&&% #&&&&&&&&&&&&&&%$+ $&&&&&&% %&&&&&&#",
-" .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;=+@@@@@@ ",
-" +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;=%++@@@@@@@+@;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@. ",
-" .@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;=&%%$%%&*-;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@ ",
-" +@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@. ",
-" @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@+ ",
-" +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@ ",
-" @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
-" .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@ ",
-" .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@ ",
-" +@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
-" @@@@@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%+@@@+ ",
-" @@@@@@@@@@@@@+#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-#@@@@+ ",
-" .@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@ ",
-" .@@@@@@@@@@@@@@@@$-;;;;;;;;;;;;;;;;;;;;;;;;*$%*-;;;-*$@@@@@@@ ",
-" .@@@@@@@@@@@@@@@@+#&-;;;;;;;;;;;;;;;;;;=%@+@@+++.+++@@@@@@+ ",
-" @@@@@@@@@@@@@@@@@@++%*--;;;;;;;;--&#++@@@@@@@@@@@@@@@@@+ ",
-" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
-" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
-" .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
-" .+@@@@@@@@@@@@@@@@@@@@. ",
-" ...+++@@@@++... "};
-
+ "245 86 12 1",
+ " c #191919",
+ ". c #303030",
+ "+ c #404040",
+ "@ c #454545",
+ "# c #5A5A5A",
+ "$ c #686868",
+ "% c #818181",
+ "& c #9C9C9C",
+ "* c #B8B8B8",
+ "= c #CDCDCD",
+ "- c #DDDDDD",
+ "; c #FEFEFE",
+ " ...+++@@@@++... ",
+ " .+@@@@@@@@@@@@@@@@@@@@. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
+ " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " @@@@@@@@@@@@@@@@@@++$*--;;;;;;;;-=&@+@@@@@@@@@@@@@@@@@@+ ",
+ " .@@@@@@@@@@@@@@@@+#&=;;;;;;;;;;;;;;;;;-*%++@@@@@@@@@@@@@@@+ ",
+ " .@@@@@@@@@@@@@@@@$=;;;;;;;;;;;;;;;;;;;;;;;;&#+@@@@@@@@@@@@@@@ ",
+ " .@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@ ",
+ " @@@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@+ ",
+ " @@@@@@@@@@@@@+*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@+ ",
+ " @@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@. ",
+ " .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@@@ ",
+ " .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@@@ ",
+ " @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@@. ",
+ " +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@ ",
+ " @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@+ ",
+ " @@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@. ",
+ " .@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;=*&%%%%&*-;;;;;;;;;;;;;;;;;;;;;%+@@@@@@@@@@ ",
+ " +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;-%++@@@@@@@++@*-;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@. ",
+ " .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;=+@@@@@@@@@@ ",
+ " +@@@@@@@@@@-;;;;;;;;;;;;;;;;-$+@@@@@@@@@@@@@@@@@@@&;;;;;;;;;;;;;;;;;%@@@@@@@@@@. %*------=*%. $%%%%%%% $%%%%%%%%. #%%%%%%%%%%%%%%$@ %%%%%%%%% ",
+ " @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@+$;;;;;;;;;;;;;;;;-+@@@@@@@@@+ #=;;;;;;;;;;;;;=$ .;;;;;;;;& &;;;;;;;;;; ;;;;;;;;;;;;;;;;;;-&# -;;;;;;;;;= ",
+ " .@@@@@@@@@@;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;%@@@@@@@@@@ .=;;;;;;;;;;;;;;;;;;# +;;;;;;;;* ;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;@ .;;;;;;;;;;;. ",
+ " +@@@@@@@@@%;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;@@@@@@@@@@. @;;;;;;;;;;;;;;;;;;;;;* +;;;;;;;;* +;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;* &;;;;;;;;;;;* ",
+ " @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;$@@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;= +;;;;;;;;* *;;;;;;;;;;;;. .;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;;;;;;;;; ",
+ " .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;=+@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;* #;;;;;;;;;;;;;+ ",
+ " +@@@@@@@@@&;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@ *;;;;;;;;;;;;;;;;;;;;;;;;;;# +;;;;;;;;* $;;;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;;# *;;;;;;;;;;;;;= ",
+ " @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+#*;;;-&@@@@@@@@@@@@@@;;;;;;;;;;;;;;%@@@@@@@@@. +;;;;;;;;;;-# #-;;;;;;;;;;= +;;;;;;;;* =;;;;;;;;;;;;;;# .;;;;;;;;-&&&&*-;;;;;;;;;;;; ;;;;;;;;;;;;;;;. ",
+ " @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*+@@@@@@@@@@+&;;;;;;;;;;;;;*+@@@@@@@@+ &;;;;;;;;;% %;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;* .;;;;;;;;= #;;;;;;;;;;# %;;;;;;;;;;;;;;;$ ",
+ ".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+&;;;;;;;;;;;;#@@@@@@@@@@#;;;;;;;;;;;;;;+@@@@@@@@@ =;;;;;;;;= =;;;;;;;;;@ +;;;;;;;;* &;;;;;;;%;;;;;;;; .;;;;;;;;= ;;;;;;;;;& -;;;;;;;%;;;;;;;= ",
+ ".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@@-;;;;;;;;;;;;;#@@@@@@@@@ ;;;;;;;;;@ -;;;;;;;;. +;;;;;;;;* -;;;;;;;+*;;;;;;;% .;;;;;;;;= %;;;;;;;;- ;;;;;;;; ;;;;;;;;. ",
+ ".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;%@@@@@@@@@ ;;;;;;;;; +;;;;;;;;* .;;;;;;;; #;;;;;;;= .;;;;;;;;= ;;;;;;;;; &;;;;;;;& &;;;;;;;& ",
+ "+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@ .;;;;;;;;- +;;;;;;;;* *;;;;;;;% ;;;;;;;; .;;;;;;;;= -;;;;;;;; ;;;;;;;;. @;;;;;;;- ",
+ "+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;-+@@@@@@@@+-;;;;;;;;;;;;*@@@@@@@@@. .;;;;;;;;- +;;;;;;;;* ;;;;;;;;. *;;;;;;;& .;;;;;;;;= =;;;;;;;;. .;;;;;;;; ;;;;;;;;. ",
+ "+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@-;;;;;;;;;;;;=@@@@@@@@@. .;;;;;;;;- .%%%%%%%%%%%%$ +;;;;;;;;* +;;;;;;;- .;;;;;;;; .;;;;;;;;= =;;;;;;;;. &;;;;;;;* %;;;;;;;* ",
+ "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;% +;;;;;;;;* *;;;;;;;& ;;;;;;;;. .;;;;;;;;= *;;;;;;;;. ;;;;;;;;. .;;;;;;;; ",
+ "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;; *;;;;;;;& .;;;;;;;;= *;;;;;;;;+ #;;;;;;;- ;;;;;;;;+ ",
+ "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@#$$&*-;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* $;;;;;;;= .;;;;;;;; .;;;;;;;;= *;;;;;;;;+ *;;;;;;;& %;;;;;;;* ",
+ "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@+@=;;;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* =;;;;;;;% -;;;;;;;# .;;;;;;;;= *;;;;;;;;. ;;;;;;;;+ ;;;;;;;;. ",
+ "+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@@@@@@#-;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;. &;;;;;;;* .;;;;;;;;= =;;;;;;;;. %;;;;;;;- =;;;;;;;# ",
+ "+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;;+@@@@@+++++++@@@@@@@&;;*@@@@@@@@@. .;;;;;;;;- @%%%%*;;;;;;;;& +;;;;;;;;* &;;;;;;;=.......#;;;;;;;; .;;;;;;;;= =;;;;;;;;. -;;;;;;;%.......&;;;;;;;= ",
+ "+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@+#%*-;;-=&$@@@@@@@%;&@@@@@@@@@ ;;;;;;;;; %;;;;;;;;% +;;;;;;;;* -;;;;;;;;;;;;;;;;;;;;;;;;% .;;;;;;;;= -;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+ ".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@*;;;;;;;;;;;%@@@@@@&%@@@@@@@@@ ;;;;;;;;; &;;;;;;;;% +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;= .;;;;;;;;= ;;;;;;;;; %;;;;;;;;;;;;;;;;;;;;;;;;;& ",
+ ".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@+%;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@ -;;;;;;;;# =;;;;;;;;@ +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= $;;;;;;;;- ;;;;;;;;;;;;;;;;;;;;;;;;;;- ",
+ ".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+*;;;;;;;;;;;;#@+*;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@@ =;;;;;;;;= @;;;;;;;;; +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;= -;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+ " @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*@@+*;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@+ &;;;;;;;;;% +;;;;;;;;;= +;;;;;;;;* +;;;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= +-;;;;;;;;;$ &;;;;;;;;;;;;;;;;;;;;;;;;;;;* ",
+ " @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+$=;;;-&@@@@&;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@. .;;;;;;;;;;-@ .*;;;;;;;;;;% +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;;;;. .;;;;;;;;-&&&&&*;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ",
+ " +@@@@@@@@@*;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@+@@@@@;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@ &;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;$ #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+ " .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;@ +;;;;;;;;* $;;;;;;;;% ;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;- *;;;;;;;; .;;;;;;;;* ",
+ " @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* *;;;;;;;; &;;;;;;;;# .;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;= -;;;;;;;;.",
+ " +@@@@@@@@@%;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@. #;;;;;;;;;;;;;;;;;;;;;% +;;;;;;;;* ;;;;;;;;* ;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;- %;;;;;;;;% %;;;;;;;;#",
+ " .@@@@@@@@@@;;;;;;;;;;;;;;;;&+@@@@@@@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@@@ .-;;;;;;;;;;;;;;;;;;+ +;;;;;;;;* &;;;;;;;;$ =;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;% -;;;;;;;; ;;;;;;;;*",
+ " @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@+;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@@+ $=;;;;;;;;;;;;;=$ .;;;;;;;;* =;;;;;;;; $;;;;;;;;# .;;;;;;;;;;;;;;;;;;;=%. ;;;;;;;;& *;;;;;;;;",
+ " +@@@@@@@@@@-;;;;;;;;;;;;;;;;-#+@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@. .%=---;---=%. %&&&&&&&. +&&&&&&&. $&&&&&&% #&&&&&&&&&&&&&&%$+ $&&&&&&% %&&&&&&#",
+ " .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;=+@@@@@@ ",
+ " +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;=%++@@@@@@@+@;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@. ",
+ " .@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;=&%%$%%&*-;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@ ",
+ " +@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@. ",
+ " @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@+ ",
+ " +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@ ",
+ " @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
+ " .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@ ",
+ " .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@ ",
+ " +@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
+ " @@@@@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%+@@@+ ",
+ " @@@@@@@@@@@@@+#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-#@@@@+ ",
+ " .@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@ ",
+ " .@@@@@@@@@@@@@@@@$-;;;;;;;;;;;;;;;;;;;;;;;;*$%*-;;;-*$@@@@@@@ ",
+ " .@@@@@@@@@@@@@@@@+#&-;;;;;;;;;;;;;;;;;;=%@+@@+++.+++@@@@@@+ ",
+ " @@@@@@@@@@@@@@@@@@++%*--;;;;;;;;--&#++@@@@@@@@@@@@@@@@@+ ",
+ " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
+ " +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+ " .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
+ " .+@@@@@@@@@@@@@@@@@@@@. ",
+ " ...+++@@@@++... "};
const char* loopRepeat_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #323331",
-"@ c #4D4F4C",
-"# c #646663",
-"$ c #787A77",
-"% c #919390",
-"& c #BFC1BD",
-"..................",
-"..................",
-"..................",
-"...&%#......#%&...",
-"...&&&%+..+%&&&...",
-"...$%&&%..%&&%$...",
-".....$&&##&&$.....",
-"......%&%%&%......",
-"......$&&&&$......",
-"......$&&&&$......",
-"......%&%%&%......",
-".....$&&##&&$.....",
-"...$%&&%..%&&%$...",
-"...&&&%+..+%&&&...",
-"...&%#......#%&...",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #323331",
+ "@ c #4D4F4C",
+ "# c #646663",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BFC1BD",
+ "..................",
+ "..................",
+ "..................",
+ "...&%#......#%&...",
+ "...&&&%+..+%&&&...",
+ "...$%&&%..%&&%$...",
+ ".....$&&##&&$.....",
+ "......%&%%&%......",
+ "......$&&&&$......",
+ "......$&&&&$......",
+ "......%&%%&%......",
+ ".....$&&##&&$.....",
+ "...$%&&%..%&&%$...",
+ "...&&&%+..+%&&&...",
+ "...&%#......#%&...",
+ "..................",
+ "..................",
+ ".................."};
const char* loopBasic_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #313230",
-"@ c #4D4F4C",
-"# c #666765",
-"$ c #787A77",
-"% c #919390",
-"& c #BEC0BD",
-"..................",
-"..................",
-"..................",
-"......#%&&%#......",
-"....+%&&&&&&%+....",
-"....%&&&%%&&&%....",
-"...#&&%+..+%&&#...",
-"...%&&+....+&&%...",
-"...&&%......%&&...",
-"...&&%......%&&...",
-"...%&&+....+&&%...",
-"...#&&%+..+%&&#...",
-"....%&&&%%&&&%....",
-"....+%&&&&&&%+....",
-"......#%&&%#......",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #313230",
+ "@ c #4D4F4C",
+ "# c #666765",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BEC0BD",
+ "..................",
+ "..................",
+ "..................",
+ "......#%&&%#......",
+ "....+%&&&&&&%+....",
+ "....%&&&%%&&&%....",
+ "...#&&%+..+%&&#...",
+ "...%&&+....+&&%...",
+ "...&&%......%&&...",
+ "...&&%......%&&...",
+ "...%&&+....+&&%...",
+ "...#&&%+..+%&&#...",
+ "....%&&&%%&&&%....",
+ "....+%&&&&&&%+....",
+ "......#%&&%#......",
+ "..................",
+ "..................",
+ ".................."};
const char* loopOnce_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #323331",
-"@ c #4D4F4C",
-"# c #646663",
-"$ c #787A77",
-"% c #919390",
-"& c #BFC1BD",
-"..................",
-"..................",
-"..................",
-"......$%&&%#......",
-"....+&&&&&&&&+....",
-"...+&&&&$$&&&&+...",
-"...$&&$....$&&$...",
-"...%&&......%&%...",
-"..................",
-"..................",
-"...%&&+.....&&&...",
-"...#&&$....$&&#...",
-"....%&&&%$&&&%....",
-"....+%&&&&&&%+....",
-"......#%&&%#......",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #323331",
+ "@ c #4D4F4C",
+ "# c #646663",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BFC1BD",
+ "..................",
+ "..................",
+ "..................",
+ "......$%&&%#......",
+ "....+&&&&&&&&+....",
+ "...+&&&&$$&&&&+...",
+ "...$&&$....$&&$...",
+ "...%&&......%&%...",
+ "..................",
+ "..................",
+ "...%&&+.....&&&...",
+ "...#&&$....$&&#...",
+ "....%&&&%$&&&%....",
+ "....+%&&&&&&%+....",
+ "......#%&&%#......",
+ "..................",
+ "..................",
+ ".................."};
const char* loopOnceBar_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #393A38",
-"+ c #545553",
-"@ c #747673",
-"# c #A3A5A2",
-"$ c #ADAFAC",
-"% c #B5B7B4",
-"& c #C7C9C6",
-" ",
-" ",
-" ",
-" @$&%#@ ",
-" .$&&&&&&$. ",
-" %&&#@@#&&$ ",
-" @&&@ @&&@ ",
-" %&# +%$+ #&$ ",
-" %&&% ",
-" %&&% ",
-" $&# +%%+ #&$ ",
-" @&&@ @&&@ ",
-" $&&#@@#&&$ ",
-" .$&&&&&&$. ",
-" @#&&#@ ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #393A38",
+ "+ c #545553",
+ "@ c #747673",
+ "# c #A3A5A2",
+ "$ c #ADAFAC",
+ "% c #B5B7B4",
+ "& c #C7C9C6",
+ " ",
+ " ",
+ " ",
+ " @$&%#@ ",
+ " .$&&&&&&$. ",
+ " %&&#@@#&&$ ",
+ " @&&@ @&&@ ",
+ " %&# +%$+ #&$ ",
+ " %&&% ",
+ " %&&% ",
+ " $&# +%%+ #&$ ",
+ " @&&@ @&&@ ",
+ " $&&#@@#&&$ ",
+ " .$&&&&&&$. ",
+ " @#&&#@ ",
+ " ",
+ " ",
+ " "};
const char* oneshotBasic_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #313230",
-"@ c #4D4F4C",
-"# c #666765",
-"$ c #787A77",
-"% c #919390",
-"& c #BEC0BD",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"...$$$$$$$$$$$$...",
-"...&&&&&&&&&&&&...",
-"...&&&&&&&&&&&&...",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #313230",
+ "@ c #4D4F4C",
+ "# c #666765",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BEC0BD",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "...$$$$$$$$$$$$...",
+ "...&&&&&&&&&&&&...",
+ "...&&&&&&&&&&&&...",
+ "..................",
+ "..................",
+ ".................."};
const char* oneshotRetrig_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #313230",
-"@ c #4D4F4C",
-"# c #666765",
-"$ c #787A77",
-"% c #919390",
-"& c #BEC0BD",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"...$$$$$$$#@......",
-"...&&&&&&&&&&@....",
-"...&&&&&&&&&&&+...",
-"..........+$&&%...",
-"............%&&...",
-"............%&&...",
-"...........+&&%...",
-"...$$$$$$$%&&&#...",
-"...&&&&&&&&&&%....",
-"...&&&&&&&&%#.....",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #313230",
+ "@ c #4D4F4C",
+ "# c #666765",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BEC0BD",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "...$$$$$$$#@......",
+ "...&&&&&&&&&&@....",
+ "...&&&&&&&&&&&+...",
+ "..........+$&&%...",
+ "............%&&...",
+ "............%&&...",
+ "...........+&&%...",
+ "...$$$$$$$%&&&#...",
+ "...&&&&&&&&&&%....",
+ "...&&&&&&&&%#.....",
+ "..................",
+ "..................",
+ ".................."};
const char* oneshotPress_xpm[] = {
-"18 18 8 1",
-" c #181917",
-". c #242523",
-"+ c #313230",
-"@ c #4D4F4C",
-"# c #666765",
-"$ c #787A77",
-"% c #919390",
-"& c #BEC0BD",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"...+%&%+..........",
-"...%&&&%..........",
-"...&&&&&..........",
-"...$&&&$..........",
-"...+$&$+..........",
-"..................",
-"...$$$$$$$$$$$$...",
-"...&&&&&&&&&&&&...",
-"...&&&&&&&&&&&&...",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #313230",
+ "@ c #4D4F4C",
+ "# c #666765",
+ "$ c #787A77",
+ "% c #919390",
+ "& c #BEC0BD",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "...+%&%+..........",
+ "...%&&&%..........",
+ "...&&&&&..........",
+ "...$&&&$..........",
+ "...+$&$+..........",
+ "..................",
+ "...$$$$$$$$$$$$...",
+ "...&&&&&&&&&&&&...",
+ "...&&&&&&&&&&&&...",
+ "..................",
+ "..................",
+ ".................."};
const char* oneshotEndless_xpm[] = {
-"18 18 6 1",
-" c #242523",
-". c #464745",
-"+ c #6D6F6C",
-"@ c #888A87",
-"# c #ADAFAC",
-"$ c #C6C8C5",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .++. ",
-" @$$$$#. ",
-" @$$$$$$$. ",
-" .$$#. +$$@ ",
-" +$$. @$# ",
-" +$$ @$$ ",
-" .$$+ #$# ",
-" @@@$$$@@#$$+ ",
-" $$$$$$$$$$@ ",
-" $$$$$$$$#+ ",
-" ",
-" ",
-" "};
-
+ "18 18 6 1",
+ " c #242523",
+ ". c #464745",
+ "+ c #6D6F6C",
+ "@ c #888A87",
+ "# c #ADAFAC",
+ "$ c #C6C8C5",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .++. ",
+ " @$$$$#. ",
+ " @$$$$$$$. ",
+ " .$$#. +$$@ ",
+ " +$$. @$# ",
+ " +$$ @$$ ",
+ " .$$+ #$# ",
+ " @@@$$$@@#$$+ ",
+ " $$$$$$$$$$@ ",
+ " $$$$$$$$#+ ",
+ " ",
+ " ",
+ " "};
const char* updirOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #332F2E",
-"+ c #54494A",
-"@ c #6B5A5C",
-"# c #866C6B",
-"$ c #967B7A",
-"% c #987D7C",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" @@ ",
-" #&&# ",
-" .#&&&&#. ",
-" .$&&&&&&$. ",
-" +@%&&&&%@+ ",
-" #&&&&# ",
-" #&&&&# ",
-" #&&&&# ",
-" #&&&&# ",
-" #&&&&# ",
-" .... ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #332F2E",
+ "+ c #54494A",
+ "@ c #6B5A5C",
+ "# c #866C6B",
+ "$ c #967B7A",
+ "% c #987D7C",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @@ ",
+ " #&&# ",
+ " .#&&&&#. ",
+ " .$&&&&&&$. ",
+ " +@%&&&&%@+ ",
+ " #&&&&# ",
+ " #&&&&# ",
+ " #&&&&# ",
+ " #&&&&# ",
+ " #&&&&# ",
+ " .... ",
+ " ",
+ " ",
+ " "};
const char* updirOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #555150",
-"+ c #706465",
-"@ c #7D6B6E",
-"# c #877373",
-"$ c #957978",
-"% c #9F8382",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" ## ",
-" #&&# ",
-" .$&&&&$. ",
-" .%&&&&&&%. ",
-" +@%&&&&%@+ ",
-" $&&&&$ ",
-" $&&&&$ ",
-" $&&&&$ ",
-" $&&&&$ ",
-" $&&&&$ ",
-" .... ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #555150",
+ "+ c #706465",
+ "@ c #7D6B6E",
+ "# c #877373",
+ "$ c #957978",
+ "% c #9F8382",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ## ",
+ " #&&# ",
+ " .$&&&&$. ",
+ " .%&&&&&&%. ",
+ " +@%&&&&%@+ ",
+ " $&&&&$ ",
+ " $&&&&$ ",
+ " $&&&&$ ",
+ " $&&&&$ ",
+ " $&&&&$ ",
+ " .... ",
+ " ",
+ " ",
+ " "};
const char* pause_xpm[] = {
-"23 23 8 1",
-" c #4D4F4C",
-". c #514E53",
-"+ c #5C4F61",
-"@ c #6F507E",
-"# c #855098",
-"$ c #9551AE",
-"% c #A652C5",
-"& c #AE52D1",
-" ",
-" ",
-" ",
-" ",
-" ",
-" #+ ",
-" &%#. ",
-" &&&%@ ",
-" &&&&&$+ ",
-" &&&&&&&#. ",
-" &&&&&&&&%@ ",
-" &&&&&&&&&&# ",
-" &&&&&&&&%@. ",
-" &&&&&&&#. ",
-" &&&&&$+ ",
-" &&&%@ ",
-" &&#. ",
-" $+ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "23 23 8 1",
+ " c #4D4F4C",
+ ". c #514E53",
+ "+ c #5C4F61",
+ "@ c #6F507E",
+ "# c #855098",
+ "$ c #9551AE",
+ "% c #A652C5",
+ "& c #AE52D1",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " #+ ",
+ " &%#. ",
+ " &&&%@ ",
+ " &&&&&$+ ",
+ " &&&&&&&#. ",
+ " &&&&&&&&%@ ",
+ " &&&&&&&&&&# ",
+ " &&&&&&&&%@. ",
+ " &&&&&&&#. ",
+ " &&&&&$+ ",
+ " &&&%@ ",
+ " &&#. ",
+ " $+ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* play_xpm[] = {
-"23 23 8 1",
-" c #242523",
-". c #393534",
-"+ c #574B4C",
-"@ c #6E5B5A",
-"# c #7C6663",
-"$ c #8C7170",
-"% c #A48384",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" $. ",
-" &&@ ",
-" &&&%+ ",
-" &&&&&$. ",
-" &&&&&&&@ ",
-" &&&&&&&&%+ ",
-" &&&&&&&&&&# ",
-" &&&&&&&&%+ ",
-" &&&&&&&#. ",
-" &&&&&$. ",
-" &&&%+ ",
-" &&@ ",
-" $. ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "23 23 8 1",
+ " c #242523",
+ ". c #393534",
+ "+ c #574B4C",
+ "@ c #6E5B5A",
+ "# c #7C6663",
+ "$ c #8C7170",
+ "% c #A48384",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " $. ",
+ " &&@ ",
+ " &&&%+ ",
+ " &&&&&$. ",
+ " &&&&&&&@ ",
+ " &&&&&&&&%+ ",
+ " &&&&&&&&&&# ",
+ " &&&&&&&&%+ ",
+ " &&&&&&&#. ",
+ " &&&&&$. ",
+ " &&&%+ ",
+ " &&@ ",
+ " $. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* rewindOff_xpm[] = {
-"23 23 8 1",
-" c #242523",
-". c #393534",
-"+ c #574B4C",
-"@ c #6E5B5A",
-"# c #7C6663",
-"$ c #8C7170",
-"% c #A48384",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .$ ",
-" @&& ",
-" +%&&& ",
-" .$&&&&& ",
-" @&&&&&&& ",
-" +%&&&&&&&& ",
-" #&&&&&&&&&& ",
-" +%&&&&&&&& ",
-" .#&&&&&&& ",
-" .$&&&&& ",
-" +%&&& ",
-" @&& ",
-" .$ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "23 23 8 1",
+ " c #242523",
+ ". c #393534",
+ "+ c #574B4C",
+ "@ c #6E5B5A",
+ "# c #7C6663",
+ "$ c #8C7170",
+ "% c #A48384",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .$ ",
+ " @&& ",
+ " +%&&& ",
+ " .$&&&&& ",
+ " @&&&&&&& ",
+ " +%&&&&&&&& ",
+ " #&&&&&&&&&& ",
+ " +%&&&&&&&& ",
+ " .#&&&&&&& ",
+ " .$&&&&& ",
+ " +%&&& ",
+ " @&& ",
+ " .$ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* rewindOn_xpm[] = {
-"23 23 8 1",
-" c #4D4F4C",
-". c #514E53",
-"+ c #5C4F61",
-"@ c #6F507E",
-"# c #855098",
-"$ c #9551AE",
-"% c #A652C5",
-"& c #AE52D1",
-" ",
-" ",
-" ",
-" ",
-" ",
-" +# ",
-" .#%& ",
-" @%&&& ",
-" +$&&&&& ",
-" .#&&&&&&& ",
-" @%&&&&&&&& ",
-" #&&&&&&&&&& ",
-" .@%&&&&&&&& ",
-" .#&&&&&&& ",
-" +$&&&&& ",
-" @%&&& ",
-" .#&& ",
-" +$ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "23 23 8 1",
+ " c #4D4F4C",
+ ". c #514E53",
+ "+ c #5C4F61",
+ "@ c #6F507E",
+ "# c #855098",
+ "$ c #9551AE",
+ "% c #A652C5",
+ "& c #AE52D1",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " +# ",
+ " .#%& ",
+ " @%&&& ",
+ " +$&&&&& ",
+ " .#&&&&&&& ",
+ " @%&&&&&&&& ",
+ " #&&&&&&&&&& ",
+ " .@%&&&&&&&& ",
+ " .#&&&&&&& ",
+ " +$&&&&& ",
+ " @%&&& ",
+ " .#&& ",
+ " +$ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* giada_icon[] = {
-"65 65 8 1",
-" c #444643",
-". c #565755",
-"+ c #6C6E6B",
-"@ c #898B88",
-"# c #A3A5A2",
-"$ c #BEC0BD",
-"% c #D7DAD6",
-"& c #FCFEFB",
-" ",
-" &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
-" &&&&&&&&&&&%. @&&&&&&&&&&&& ",
-" &&&&&&&&&&$ ...+@@#@@+.. +%&&&&&&&&&& ",
-" &&&&&&&&&# .+#$&&&&&&&&&&%$@+. .%&&&&&&&&& ",
-" &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@. .%&&&&&&&& ",
-" &&&&&&&# .#%&&&&&&&&&&&&&&&&&&&&&%@ .%&&&&&&& ",
-" &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&%+ .&&&&&&& ",
-" &&&&&% $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@ +&&&&&& ",
-" &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
-" &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# $&&&& ",
-" &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
-" &&&+ %&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# #&&& ",
-" &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&@ +%&& ",
-" &%+ @&&&&&&&&&&&&&&%@.. ..+#&&&&&&&&&&&&&&%. #&& ",
-" &$ .%&&&&&&&&&&&&%+ #&&&&&&&&&&&&&# +&& ",
-" &@ $&&&&&&&&&&&&# +%&&&&&&&&&&&%+ $& ",
-" %. +%&&&&&&&&&&&@ .$&&&&&&&&&&&# @& ",
-" $ #&&&&&&&&&&&@ .$&&&&&&&&&&&+ & ",
-" @ .%&&&&&&&&&&# .%&&&&&&&&&&@ $ ",
-" . +&&&&&&&&&&% +&&&&&&&&&&%. @ ",
-" #&&&&&&&&&&+ @%&&%$+ #&&&&&&&&&&. . ",
-" .%&&&&&&&&&$ +%&&&&&&&# .&&&&&&&&&&@ ",
-" .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&$ ",
-" +&&&&&&&&&& .%&&&&&&&&&&&# @&&&&&&&&&% ",
-" @&&&&&&&&&$ @&&&&&&&&&&&&&. +&&&&&&&&&& ",
-" @&&&&&&&&&# %&&&&&&&&&&&&&# .%&&&&&&&&&. ",
-" #&&&&&&&&&@ .&&&&&&&&&&&&&&$ .%&&&&&&&&&. ",
-" #&&&&&&&&&@ .&&&&&&&&&&&&&&% .#$%&&&&&&&+ ",
-" #&&&&&&&&&@ .&&&&&&&&&&&&&&$ @%&&&&. ",
-" @&&&&&&&&&# %&&&&&&&&&&&&&# @%&&. ",
-" @&&&&&&&&&$ #&&&&&&&&&&&&&. +@@@@+. .$& ",
-" +&&&&&&&&&& .%&&&&&&&&&&&# +$%&&&&&%#. .$ ",
-" .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&&%@ . ",
-" .%&&&&&&&&&$ +%&&&&&&&#. %&&&&&&&&&&&&&# ",
-" #&&&&&&&&&&+ @%&&&$+ $&&&&&&&&&&&&&&&@ . ",
-" . +&&&&&&&&&&% @&&&&&&&&&&&&&&&&%. @ ",
-" @ .%&&&&&&&&&&# %&&&&&&&&&&&&&&&&&@ $ ",
-" $ #&&&&&&&&&&&@ @&&&&&&&&&&&&&&&&&&%. & ",
-" %. +%&&&&&&&&&&&@ $&&&&&&&&&&&&&&&&&&&. @& ",
-" &@ $&&&&&&&&&&&&# %&&&&&&&&&&&&&&&&&&&+ $& ",
-" &$ .%&&&&&&&&&&&&%+ %&&&&&&&&&&&&&&&&&&&@ +&& ",
-" &%+ @&&&&&&&&&&&&&&%@.. .%&&&&&&&&&&&&&&&&&&&@ #&& ",
-" &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&&&&&+ +%&& ",
-" &&&+ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&. #&&& ",
-" &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
-" &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+ $&&&& ",
-" &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
-" &&&&&% .$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. +&&&&&& ",
-" &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. .&&&&&&& ",
-" &&&&&&&# .$%&&&&&&&&&&&&&&&&&&&&&&&&&&&&%# .%&&&&&&& ",
-" &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@#$%%$$@+ .%&&&&&&&& ",
-" &&&&&&&&&# .+#%&&&&&&&&&&&$@+. .... .%&&&&&&&&& ",
-" &&&&&&&&&&$ ..+@@###@+... +%&&&&&&&&&& ",
-" &&&&&&&&&&&%. @&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
-" &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
-" "};
-
+ "65 65 8 1",
+ " c #444643",
+ ". c #565755",
+ "+ c #6C6E6B",
+ "@ c #898B88",
+ "# c #A3A5A2",
+ "$ c #BEC0BD",
+ "% c #D7DAD6",
+ "& c #FCFEFB",
+ " ",
+ " &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&%. @&&&&&&&&&&&& ",
+ " &&&&&&&&&&$ ...+@@#@@+.. +%&&&&&&&&&& ",
+ " &&&&&&&&&# .+#$&&&&&&&&&&%$@+. .%&&&&&&&&& ",
+ " &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@. .%&&&&&&&& ",
+ " &&&&&&&# .#%&&&&&&&&&&&&&&&&&&&&&%@ .%&&&&&&& ",
+ " &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&%+ .&&&&&&& ",
+ " &&&&&% $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@ +&&&&&& ",
+ " &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
+ " &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# $&&&& ",
+ " &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
+ " &&&+ %&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# #&&& ",
+ " &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&@ +%&& ",
+ " &%+ @&&&&&&&&&&&&&&%@.. ..+#&&&&&&&&&&&&&&%. #&& ",
+ " &$ .%&&&&&&&&&&&&%+ #&&&&&&&&&&&&&# +&& ",
+ " &@ $&&&&&&&&&&&&# +%&&&&&&&&&&&%+ $& ",
+ " %. +%&&&&&&&&&&&@ .$&&&&&&&&&&&# @& ",
+ " $ #&&&&&&&&&&&@ .$&&&&&&&&&&&+ & ",
+ " @ .%&&&&&&&&&&# .%&&&&&&&&&&@ $ ",
+ " . +&&&&&&&&&&% +&&&&&&&&&&%. @ ",
+ " #&&&&&&&&&&+ @%&&%$+ #&&&&&&&&&&. . ",
+ " .%&&&&&&&&&$ +%&&&&&&&# .&&&&&&&&&&@ ",
+ " .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&$ ",
+ " +&&&&&&&&&& .%&&&&&&&&&&&# @&&&&&&&&&% ",
+ " @&&&&&&&&&$ @&&&&&&&&&&&&&. +&&&&&&&&&& ",
+ " @&&&&&&&&&# %&&&&&&&&&&&&&# .%&&&&&&&&&. ",
+ " #&&&&&&&&&@ .&&&&&&&&&&&&&&$ .%&&&&&&&&&. ",
+ " #&&&&&&&&&@ .&&&&&&&&&&&&&&% .#$%&&&&&&&+ ",
+ " #&&&&&&&&&@ .&&&&&&&&&&&&&&$ @%&&&&. ",
+ " @&&&&&&&&&# %&&&&&&&&&&&&&# @%&&. ",
+ " @&&&&&&&&&$ #&&&&&&&&&&&&&. +@@@@+. .$& ",
+ " +&&&&&&&&&& .%&&&&&&&&&&&# +$%&&&&&%#. .$ ",
+ " .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&&%@ . ",
+ " .%&&&&&&&&&$ +%&&&&&&&#. %&&&&&&&&&&&&&# ",
+ " #&&&&&&&&&&+ @%&&&$+ $&&&&&&&&&&&&&&&@ . ",
+ " . +&&&&&&&&&&% @&&&&&&&&&&&&&&&&%. @ ",
+ " @ .%&&&&&&&&&&# %&&&&&&&&&&&&&&&&&@ $ ",
+ " $ #&&&&&&&&&&&@ @&&&&&&&&&&&&&&&&&&%. & ",
+ " %. +%&&&&&&&&&&&@ $&&&&&&&&&&&&&&&&&&&. @& ",
+ " &@ $&&&&&&&&&&&&# %&&&&&&&&&&&&&&&&&&&+ $& ",
+ " &$ .%&&&&&&&&&&&&%+ %&&&&&&&&&&&&&&&&&&&@ +&& ",
+ " &%+ @&&&&&&&&&&&&&&%@.. .%&&&&&&&&&&&&&&&&&&&@ #&& ",
+ " &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&&&&&+ +%&& ",
+ " &&&+ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&. #&&& ",
+ " &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
+ " &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+ $&&&& ",
+ " &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
+ " &&&&&% .$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. +&&&&&& ",
+ " &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. .&&&&&&& ",
+ " &&&&&&&# .$%&&&&&&&&&&&&&&&&&&&&&&&&&&&&%# .%&&&&&&& ",
+ " &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@#$%%$$@+ .%&&&&&&&& ",
+ " &&&&&&&&&# .+#%&&&&&&&&&&&$@+. .... .%&&&&&&&&& ",
+ " &&&&&&&&&&$ ..+@@###@+... +%&&&&&&&&&& ",
+ " &&&&&&&&&&&%. @&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
+ " &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
+ " "};
const char* recOff_xpm[] = {
-"23 23 8 1",
-" c #242523",
-". c #342F2E",
-"+ c #3F3B3A",
-"@ c #594F4F",
-"# c #7A6663",
-"$ c #8C7170",
-"% c #A68384",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @$%%$@ ",
-" .$&&&&&&$. ",
-" $&&&&&&&&$ ",
-" @&&&#++#&&&@ ",
-" $&&# #&&$ ",
-" %&&+ +&&% ",
-" %&&+ +&&% ",
-" $&&# #&&$ ",
-" @&&&#++#&&&@ ",
-" $&&&&&&&&$ ",
-" .$&&&&&&$. ",
-" @$%%$@ ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" "};
+ "23 23 8 1",
+ " c #242523",
+ ". c #342F2E",
+ "+ c #3F3B3A",
+ "@ c #594F4F",
+ "# c #7A6663",
+ "$ c #8C7170",
+ "% c #A68384",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @$%%$@ ",
+ " .$&&&&&&$. ",
+ " $&&&&&&&&$ ",
+ " @&&&#++#&&&@ ",
+ " $&&# #&&$ ",
+ " %&&+ +&&% ",
+ " %&&+ +&&% ",
+ " $&&# #&&$ ",
+ " @&&&#++#&&&@ ",
+ " $&&&&&&&&$ ",
+ " .$&&&&&&$. ",
+ " @$%%$@ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* recOn_xpm[] = {
-"23 23 8 1",
-" c #4D4F4C",
-". c #5F4E50",
-"+ c #6E4F50",
-"@ c #8C5050",
-"# c #AE5454",
-"$ c #BB5253",
-"% c #C55352",
-"& c #E85557",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @$&&$@ ",
-" .%&&&&&&%. ",
-" %&&&&&&&&% ",
-" @&&&#++#&&&@ ",
-" $&&# #&&$ ",
-" &&&+ +&&& ",
-" &&&+ +&&& ",
-" $&&# #&&$ ",
-" @&&&#++#&&&@ ",
-" %&&&&&&&&% ",
-" .%&&&&&&%. ",
-" @$&&$@ ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" "};
+ "23 23 8 1",
+ " c #4D4F4C",
+ ". c #5F4E50",
+ "+ c #6E4F50",
+ "@ c #8C5050",
+ "# c #AE5454",
+ "$ c #BB5253",
+ "% c #C55352",
+ "& c #E85557",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @$&&$@ ",
+ " .%&&&&&&%. ",
+ " %&&&&&&&&% ",
+ " @&&&#++#&&&@ ",
+ " $&&# #&&$ ",
+ " &&&+ +&&& ",
+ " &&&+ +&&& ",
+ " $&&# #&&$ ",
+ " @&&&#++#&&&@ ",
+ " %&&&&&&&&% ",
+ " .%&&&&&&%. ",
+ " @$&&$@ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* inputRecOn_xpm[] = {
-"23 23 8 1",
-" c #524D4C",
-". c #4D4F4C",
-"+ c #5D4F50",
-"@ c #8C5050",
-"# c #BB5253",
-"$ c #C45251",
-"% c #DD5256",
-"& c #EA5657",
-".......................",
-".......................",
-".......................",
-".......................",
-".......................",
-"........ @#%%#@ .......",
-".......+$&&&&&&$+......",
-"...... $&&&&&&&&$ .....",
-"......@&&&&&&&&&&@.....",
-"......#&&&&&&&&&&#.....",
-"......%&&&&&&&&&&%.....",
-"......%&&&&&&&&&&%.....",
-"......#&&&&&&&&&&#.....",
-"......@&&&&&&&&&&@.....",
-".......$&&&&&&&&$......",
-".......+$&&&&&&$+......",
-"........ @#%%#@ .......",
-".......................",
-".......................",
-".......................",
-".......................",
-".......................",
-"......................."};
+ "23 23 8 1",
+ " c #524D4C",
+ ". c #4D4F4C",
+ "+ c #5D4F50",
+ "@ c #8C5050",
+ "# c #BB5253",
+ "$ c #C45251",
+ "% c #DD5256",
+ "& c #EA5657",
+ ".......................",
+ ".......................",
+ ".......................",
+ ".......................",
+ ".......................",
+ "........ @#%%#@ .......",
+ ".......+$&&&&&&$+......",
+ "...... $&&&&&&&&$ .....",
+ "......@&&&&&&&&&&@.....",
+ "......#&&&&&&&&&&#.....",
+ "......%&&&&&&&&&&%.....",
+ "......%&&&&&&&&&&%.....",
+ "......#&&&&&&&&&&#.....",
+ "......@&&&&&&&&&&@.....",
+ ".......$&&&&&&&&$......",
+ ".......+$&&&&&&$+......",
+ "........ @#%%#@ .......",
+ ".......................",
+ ".......................",
+ ".......................",
+ ".......................",
+ ".......................",
+ "......................."};
const char* inputRecOff_xpm[] = {
-"23 23 8 1",
-" c #242523",
-". c #252724",
-"+ c #332F2E",
-"@ c #594E4F",
-"# c #896E6D",
-"$ c #8D7271",
-"% c #A68384",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .@#%%#@. ",
-" +$&&&&&&$+ ",
-" .$&&&&&&&&$. ",
-" @&&&&&&&&&&@ ",
-" #&&&&&&&&&&# ",
-" %&&&&&&&&&&% ",
-" %&&&&&&&&&&% ",
-" #&&&&&&&&&&# ",
-" @&&&&&&&&&&@ ",
-" $&&&&&&&&$ ",
-" +$&&&&&&$+ ",
-" .@#%%#@. ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" "};
+ "23 23 8 1",
+ " c #242523",
+ ". c #252724",
+ "+ c #332F2E",
+ "@ c #594E4F",
+ "# c #896E6D",
+ "$ c #8D7271",
+ "% c #A68384",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .@#%%#@. ",
+ " +$&&&&&&$+ ",
+ " .$&&&&&&&&$. ",
+ " @&&&&&&&&&&@ ",
+ " #&&&&&&&&&&# ",
+ " %&&&&&&&&&&% ",
+ " %&&&&&&&&&&% ",
+ " #&&&&&&&&&&# ",
+ " @&&&&&&&&&&@ ",
+ " $&&&&&&&&$ ",
+ " +$&&&&&&$+ ",
+ " .@#%%#@. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
+
+const char* freeInputRecOff_xpm[] = {
+ "13 23 9 1",
+ " c None",
+ ". c #232523",
+ "+ c #353130",
+ "@ c #483E3F",
+ "# c #4D4F4C",
+ "$ c #574D4D",
+ "% c #705D5C",
+ "& c #8C7271",
+ "* c #AF8D8E",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".....+%%.....",
+ ".....@*&.....",
+ ".....@*&.....",
+ ".....@*&.....",
+ "....$@$@.....",
+ "...+**&@.....",
+ "....+%**&@...",
+ "......+%*$...",
+ ".....@&%+....",
+ ".....@*&.....",
+ ".....@*&.....",
+ ".....@*&.....",
+ ".....+@@.....",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "............."};
+
+const char* freeInputRecOn_xpm[] = {
+ "13 23 9 1",
+ " c None",
+ ". c #4D4F4C",
+ "+ c #575352",
+ "@ c #5D5857",
+ "# c #6A5F5F",
+ "$ c #796A6B",
+ "% c #877472",
+ "& c #977C7C",
+ "* c #B08E8F",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".....@%$.....",
+ ".....#*&.....",
+ ".....#*&.....",
+ ".....#*&.....",
+ "....##$#.....",
+ "...+**&@.....",
+ "....@%**&@...",
+ "......@%*$...",
+ ".....@&%@....",
+ ".....#*&.....",
+ ".....#*&.....",
+ ".....#*&.....",
+ ".....+##.....",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "............."};
const char* muteOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #2E2F2D",
-"+ c #3B3C3A",
-"@ c #525451",
-"# c #6F716E",
-"$ c #878986",
-"% c #ADAFAC",
-"& c #C6C8C5",
-" ",
-" ",
-" ",
-" ",
-" ++. .++ ",
-" +&&$ $&&+ ",
-" +&&% %&&+ ",
-" +&%&++&%&+ ",
-" +&$&##&$&+ ",
-" +&#%$$%#&+ ",
-" +&#$%%$#&+ ",
-" +&#@&&@#&+ ",
-" +&#+&&+#&+ ",
-" .#@ ## @#. ",
-" ",
-" ",
-" ",
-" "};
+ "18 18 8 1",
+ " c #242523",
+ ". c #2E2F2D",
+ "+ c #3B3C3A",
+ "@ c #525451",
+ "# c #6F716E",
+ "$ c #878986",
+ "% c #ADAFAC",
+ "& c #C6C8C5",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ++. .++ ",
+ " +&&$ $&&+ ",
+ " +&&% %&&+ ",
+ " +&%&++&%&+ ",
+ " +&$&##&$&+ ",
+ " +&#%$$%#&+ ",
+ " +&#$%%$#&+ ",
+ " +&#@&&@#&+ ",
+ " +&#+&&+#&+ ",
+ " .#@ ## @#. ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* muteOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #585A57",
-"+ c #616260",
-"@ c #7A7C79",
-"# c #888A87",
-"$ c #989A97",
-"% c #B2B4B1",
-"& c #C6C8C5",
-" ",
-" ",
-" ",
-" ",
-" .. .. ",
-" +&&$ $&&+ ",
-" +&&% %&&+ ",
-" +&%&++&%&+ ",
-" +&$&@@&$&+ ",
-" +&#%$$%#&+ ",
-" +&#$&&$#&+ ",
-" +&#@&&@#&+ ",
-" +&#.&&.#&+ ",
-" .#+ ## +#. ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #585A57",
+ "+ c #616260",
+ "@ c #7A7C79",
+ "# c #888A87",
+ "$ c #989A97",
+ "% c #B2B4B1",
+ "& c #C6C8C5",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .. .. ",
+ " +&&$ $&&+ ",
+ " +&&% %&&+ ",
+ " +&%&++&%&+ ",
+ " +&$&@@&$&+ ",
+ " +&#%$$%#&+ ",
+ " +&#$&&$#&+ ",
+ " +&#@&&@#&+ ",
+ " +&#.&&.#&+ ",
+ " .#+ ## +#. ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* readActionOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #393B38",
-"+ c #555754",
-"@ c #6B6D6A",
-"# c #7F807E",
-"$ c #9C9E9B",
-"% c #B1B3B0",
-"& c #C3C5C2",
-" ",
-" ",
-" ",
-" ",
-" .... ",
-" %&&&&%+ ",
-" %&@@@&& ",
-" %% $&. ",
-" %&@@#&$ ",
-" %&&&&@ ",
-" %% +&$ ",
-" %% #&# ",
-" %% %&+ ",
-" @@ .#+ ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #393B38",
+ "+ c #555754",
+ "@ c #6B6D6A",
+ "# c #7F807E",
+ "$ c #9C9E9B",
+ "% c #B1B3B0",
+ "& c #C3C5C2",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .... ",
+ " %&&&&%+ ",
+ " %&@@@&& ",
+ " %% $&. ",
+ " %&@@#&$ ",
+ " %&&&&@ ",
+ " %% +&$ ",
+ " %% #&# ",
+ " %% %&+ ",
+ " @@ .#+ ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* readActionOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #696B68",
-"+ c #7A7C79",
-"@ c #888A87",
-"# c #939592",
-"$ c #A7A9A6",
-"% c #B7B9B6",
-"& c #C4C6C3",
-" ",
-" ",
-" ",
-" ",
-" ",
-" %&&&&%. ",
-" %&++@&& ",
-" %% $& ",
-" %&@@#&$ ",
-" %&&&&@ ",
-" %% +&$ ",
-" %% #&# ",
-" %% %&. ",
-" +@ .@+ ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #696B68",
+ "+ c #7A7C79",
+ "@ c #888A87",
+ "# c #939592",
+ "$ c #A7A9A6",
+ "% c #B7B9B6",
+ "& c #C4C6C3",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " %&&&&%. ",
+ " %&++@&& ",
+ " %% $& ",
+ " %&@@#&$ ",
+ " %&&&&@ ",
+ " %% +&$ ",
+ " %% #&# ",
+ " %% %&. ",
+ " +@ .@+ ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* readActionDisabled_xpm[] = {
-"18 18 7 1",
-" c None",
-". c #252525",
-"+ c #313131",
-"@ c #393939",
-"# c #424242",
-"$ c #4A4A4A",
-"% c #585858",
-"..................",
-"..................",
-"..................",
-"..................",
-".....@@@@+........",
-".....%%%%%%+......",
-".....%%#+$%$......",
-".....%%@.#%$......",
-".....%%##%%@......",
-".....%%%%%$.......",
-".....%%@+%%#......",
-".....%%@.@%%......",
-".....%%@..%%#.....",
-".....@@...+@@.....",
-"..................",
-"..................",
-"..................",
-".................."};
-
+ "18 18 7 1",
+ " c None",
+ ". c #252525",
+ "+ c #313131",
+ "@ c #393939",
+ "# c #424242",
+ "$ c #4A4A4A",
+ "% c #585858",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".....@@@@+........",
+ ".....%%%%%%+......",
+ ".....%%#+$%$......",
+ ".....%%@.#%$......",
+ ".....%%##%%@......",
+ ".....%%%%%$.......",
+ ".....%%@+%%#......",
+ ".....%%@.@%%......",
+ ".....%%@..%%#.....",
+ ".....@@...+@@.....",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
const char* metronomeOff_xpm[] = {
-"13 23 3 1",
-" c None",
-". c #252525",
-"+ c #B18E8E",
-".............",
-".............",
-".............",
-".............",
-".............",
-".............",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-".............",
-".............",
-".............",
-".............",
-".............",
-".............",
-"............."};
-
+ "13 23 3 1",
+ " c None",
+ ". c #252525",
+ "+ c #B18E8E",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "............."};
const char* metronomeOn_xpm[] = {
-"13 23 3 1",
-" c None",
-". c #4E4E4E",
-"+ c #B18E8E",
-".............",
-".............",
-".............",
-".............",
-".............",
-".............",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-"....+...+....",
-".............",
-".............",
-".............",
-".............",
-".............",
-".............",
-"............."};
-
+ "13 23 3 1",
+ " c None",
+ ". c #4E4E4E",
+ "+ c #B18E8E",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ "....+...+....",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ ".............",
+ "............."};
const char* recTriggerModeOff_xpm[] = {
-"13 23 8 1",
-" c #232523",
-". c #2A2625",
-"+ c #43393A",
-"@ c #514647",
-"# c #6F5C59",
-"$ c #8B7170",
-"% c #AA8889",
-"& c #B08E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @$@ ",
-" %&% ",
-" $&$ ",
-" .+. ",
-" ",
-" #%# ",
-" %&% ",
-" #%# ",
-" ",
-" .+. ",
-" $&$ ",
-" %&% ",
-" @$@ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "13 23 8 1",
+ " c #232523",
+ ". c #2A2625",
+ "+ c #43393A",
+ "@ c #514647",
+ "# c #6F5C59",
+ "$ c #8B7170",
+ "% c #AA8889",
+ "& c #B08E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @$@ ",
+ " %&% ",
+ " $&$ ",
+ " .+. ",
+ " ",
+ " #%# ",
+ " %&% ",
+ " #%# ",
+ " ",
+ " .+. ",
+ " $&$ ",
+ " %&% ",
+ " @$@ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* recTriggerModeOn_xpm[] = {
-"13 23 8 1",
-" c #4D4F4C",
-". c #534E4D",
-"+ c #605B5A",
-"@ c #6D6363",
-"# c #817072",
-"$ c #967C7B",
-"% c #AC8A8B",
-"& c #B08E8F",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @$@ ",
-" %&% ",
-" $&$ ",
-" .+. ",
-" ",
-" #%# ",
-" %&% ",
-" #%# ",
-" ",
-" .+. ",
-" $&$ ",
-" %&% ",
-" @$@ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "13 23 8 1",
+ " c #4D4F4C",
+ ". c #534E4D",
+ "+ c #605B5A",
+ "@ c #6D6363",
+ "# c #817072",
+ "$ c #967C7B",
+ "% c #AC8A8B",
+ "& c #B08E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @$@ ",
+ " %&% ",
+ " $&$ ",
+ " .+. ",
+ " ",
+ " #%# ",
+ " %&% ",
+ " #%# ",
+ " ",
+ " .+. ",
+ " $&$ ",
+ " %&% ",
+ " @$@ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* zoomInOff_xpm[] = {
-"18 18 8 1",
-" c None",
-". c #252525",
-"+ c #262626",
-"@ c #535353",
-"# c #ACACAC",
-"$ c #AEAEAE",
-"% c #B1B1B1",
-"& c #C4C4C4",
-"++++++++++++++++++",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+.......@@.......+",
-"+.......#$.......+",
-"+.......#$.......+",
-"+....@%%&&%%@....+",
-"+....@%%&&%%@....+",
-"+.......#$.......+",
-"+.......#$.......+",
-"+.......@@.......+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"++++++++++++++++++"};
-
+ "18 18 8 1",
+ " c None",
+ ". c #252525",
+ "+ c #262626",
+ "@ c #535353",
+ "# c #ACACAC",
+ "$ c #AEAEAE",
+ "% c #B1B1B1",
+ "& c #C4C4C4",
+ "++++++++++++++++++",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+.......@@.......+",
+ "+.......#$.......+",
+ "+.......#$.......+",
+ "+....@%%&&%%@....+",
+ "+....@%%&&%%@....+",
+ "+.......#$.......+",
+ "+.......#$.......+",
+ "+.......@@.......+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "++++++++++++++++++"};
const char* zoomInOn_xpm[] = {
-"18 18 8 1",
-" c None",
-". c #4E4E4E",
-"+ c #707070",
-"@ c #717171",
-"# c #B3B3B3",
-"$ c #B5B5B5",
-"% c #B7B7B7",
-"& c #C5C5C5",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"........++........",
-"........#$........",
-"........#$........",
-".....@%%&&%%@.....",
-".....@%%&&%%@.....",
-"........#$........",
-"........#$........",
-"........++........",
-"..................",
-"..................",
-"..................",
-"..................",
-".................."};
-
+ "18 18 8 1",
+ " c None",
+ ". c #4E4E4E",
+ "+ c #707070",
+ "@ c #717171",
+ "# c #B3B3B3",
+ "$ c #B5B5B5",
+ "% c #B7B7B7",
+ "& c #C5C5C5",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "........++........",
+ "........#$........",
+ "........#$........",
+ ".....@%%&&%%@.....",
+ ".....@%%&&%%@.....",
+ "........#$........",
+ "........#$........",
+ "........++........",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
const char* zoomOutOff_xpm[] = {
-"18 18 5 1",
-" c None",
-". c #252525",
-"+ c #262626",
-"@ c #9C9C9C",
-"# c #BBBBBB",
-"++++++++++++++++++",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+......@##@......+",
-"+......@##@......+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"+................+",
-"++++++++++++++++++"};
-
+ "18 18 5 1",
+ " c None",
+ ". c #252525",
+ "+ c #262626",
+ "@ c #9C9C9C",
+ "# c #BBBBBB",
+ "++++++++++++++++++",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+......@##@......+",
+ "+......@##@......+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "+................+",
+ "++++++++++++++++++"};
const char* zoomOutOn_xpm[] = {
-"18 18 4 1",
-" c None",
-". c #4E4E4E",
-"+ c #A7A7A7",
-"@ c #BEBEBE",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-".......+@@+.......",
-".......+@@+.......",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-".................."};
-
-
+ "18 18 4 1",
+ " c None",
+ ". c #4E4E4E",
+ "+ c #A7A7A7",
+ "@ c #BEBEBE",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".......+@@+.......",
+ ".......+@@+.......",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
const char* scrollRightOff_xpm[] = {
-"12 12 8 1",
-" c #181917",
-". c #242523",
-"+ c #2E2F2D",
-"@ c #4D4F4C",
-"# c #5D5F5C",
-"$ c #828481",
-"% c #9B9D9A",
-"& c #BCBEBB",
-"............",
-"............",
-"...+........",
-"...&$@......",
-"...$&&%@....",
-"....+#%&%...",
-"....+#%&%...",
-"...$&&%#....",
-"...&$@......",
-"...+........",
-"............",
-"............"};
-
+ "12 12 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #2E2F2D",
+ "@ c #4D4F4C",
+ "# c #5D5F5C",
+ "$ c #828481",
+ "% c #9B9D9A",
+ "& c #BCBEBB",
+ "............",
+ "............",
+ "...+........",
+ "...&$@......",
+ "...$&&%@....",
+ "....+#%&%...",
+ "....+#%&%...",
+ "...$&&%#....",
+ "...&$@......",
+ "...+........",
+ "............",
+ "............"};
const char* scrollLeftOff_xpm[] = {
-"12 12 8 1",
-" c #181917",
-". c #242523",
-"+ c #2E2F2D",
-"@ c #4D4F4C",
-"# c #5D5F5C",
-"$ c #828481",
-"% c #9B9D9A",
-"& c #BCBEBB",
-"............",
-"............",
-"........+...",
-"......@$&...",
-"....@%&&$...",
-"...%&%#+....",
-"...%&%#+....",
-"....#%&&$...",
-"......@$&...",
-"........+...",
-"............",
-"............"};
-
+ "12 12 8 1",
+ " c #181917",
+ ". c #242523",
+ "+ c #2E2F2D",
+ "@ c #4D4F4C",
+ "# c #5D5F5C",
+ "$ c #828481",
+ "% c #9B9D9A",
+ "& c #BCBEBB",
+ "............",
+ "............",
+ "........+...",
+ "......@$&...",
+ "....@%&&$...",
+ "...%&%#+....",
+ "...%&%#+....",
+ "....#%&&$...",
+ "......@$&...",
+ "........+...",
+ "............",
+ "............"};
const char* scrollLeftOn_xpm[] = {
-"12 12 8 1",
-" c #4D4F4C",
-". c #6B6D6A",
-"+ c #7B7D7A",
-"@ c #969895",
-"# c #A6A8A5",
-"$ c #B4B6B3",
-"% c #C0C2BF",
-"& c #FEFFFC",
-" ",
-" ",
-" ",
-" .@$ ",
-" +#%%@ ",
-" $%#+ ",
-" %%#+ ",
-" +$%%@ ",
-" .#$ ",
-" ",
-" ",
-" "};
-
+ "12 12 8 1",
+ " c #4D4F4C",
+ ". c #6B6D6A",
+ "+ c #7B7D7A",
+ "@ c #969895",
+ "# c #A6A8A5",
+ "$ c #B4B6B3",
+ "% c #C0C2BF",
+ "& c #FEFFFC",
+ " ",
+ " ",
+ " ",
+ " .@$ ",
+ " +#%%@ ",
+ " $%#+ ",
+ " %%#+ ",
+ " +$%%@ ",
+ " .#$ ",
+ " ",
+ " ",
+ " "};
const char* scrollRightOn_xpm[] = {
-"12 12 8 1",
-" c #4D4F4C",
-". c #6B6D6A",
-"+ c #7B7D7A",
-"@ c #969895",
-"# c #A6A8A5",
-"$ c #B4B6B3",
-"% c #C0C2BF",
-"& c #FEFFFC",
-" ",
-" ",
-" ",
-" %@. ",
-" @%%#. ",
-" +#%# ",
-" +#%# ",
-" @%%#+ ",
-" %@. ",
-" ",
-" ",
-" "};
-
+ "12 12 8 1",
+ " c #4D4F4C",
+ ". c #6B6D6A",
+ "+ c #7B7D7A",
+ "@ c #969895",
+ "# c #A6A8A5",
+ "$ c #B4B6B3",
+ "% c #C0C2BF",
+ "& c #FEFFFC",
+ " ",
+ " ",
+ " ",
+ " %@. ",
+ " @%%#. ",
+ " +#%# ",
+ " +#%# ",
+ " @%%#+ ",
+ " %@. ",
+ " ",
+ " ",
+ " "};
const char* soloOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #616360",
-"+ c #737572",
-"@ c #838582",
-"# c #929491",
-"$ c #A5A7A4",
-"% c #B1B3B0",
-"& c #C6C8C5",
-" ",
-" ",
-" ",
-" ",
-" .@+. ",
-" #&&&&# ",
-" .&$ %&. ",
-" &%+ .. ",
-" #&&&$. ",
-" .@$&&. ",
-" .#. @&@ ",
-" .&$. #&+ ",
-" #&&&&$ ",
-" .+@+ ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #616360",
+ "+ c #737572",
+ "@ c #838582",
+ "# c #929491",
+ "$ c #A5A7A4",
+ "% c #B1B3B0",
+ "& c #C6C8C5",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .@+. ",
+ " #&&&&# ",
+ " .&$ %&. ",
+ " &%+ .. ",
+ " #&&&$. ",
+ " .@$&&. ",
+ " .#. @&@ ",
+ " .&$. #&+ ",
+ " #&&&&$ ",
+ " .+@+ ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* soloOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #3D3F3D",
-"+ c #525451",
-"@ c #666865",
-"# c #80827F",
-"$ c #979996",
-"% c #A7A9A6",
-"& c #C6C8C5",
-" ",
-" ",
-" ",
-" ",
-" .@@. ",
-" #&&&&# ",
-" .&$ %&. ",
-" &%+ .. ",
-" #&&&$+ ",
-" .@%&&. ",
-" +#. @&@ ",
-" .&$..#&+ ",
-" #&&&&$ ",
-" .@@+ ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #3D3F3D",
+ "+ c #525451",
+ "@ c #666865",
+ "# c #80827F",
+ "$ c #979996",
+ "% c #A7A9A6",
+ "& c #C6C8C5",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .@@. ",
+ " #&&&&# ",
+ " .&$ %&. ",
+ " &%+ .. ",
+ " #&&&$+ ",
+ " .@%&&. ",
+ " +#. @&@ ",
+ " .&$..#&+ ",
+ " #&&&&$ ",
+ " .@@+ ",
+ " ",
+ " ",
+ " ",
+ " "};
#ifdef WITH_VST
-
const char* fxOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #40423F",
-"+ c #4D4E4C",
-"@ c #686A67",
-"# c #7B7D7A",
-"$ c #919390",
-"% c #AEB0AD",
-"& c #C1C3C0",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ..... . . ",
-" $&&&$ $% @&. ",
-" $$ .&#&@ ",
-" $%##. @&$ ",
-" $%##. #&% ",
-" $$ .&@&# ",
-" $$ %$ @&. ",
-" .. + +. ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #40423F",
+ "+ c #4D4E4C",
+ "@ c #686A67",
+ "# c #7B7D7A",
+ "$ c #919390",
+ "% c #AEB0AD",
+ "& c #C1C3C0",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ..... . . ",
+ " $&&&$ $% @&. ",
+ " $$ .&#&@ ",
+ " $%##. @&$ ",
+ " $%##. #&% ",
+ " $$ .&@&# ",
+ " $$ %$ @&. ",
+ " .. + +. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* fxOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #565855",
-"+ c #636562",
-"@ c #80827F",
-"# c #8E908D",
-"$ c #9FA19E",
-"% c #B1B3B0",
-"& c #C1C3C0",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .++++ +. +. ",
-" $&&&$ $% @&. ",
-" $$ .&#&@ ",
-" $%##+ @&$ ",
-" $%##+ #&% ",
-" $$ +&@&# ",
-" $$ %$ @&+ ",
-" ++ .+. ++ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #565855",
+ "+ c #636562",
+ "@ c #80827F",
+ "# c #8E908D",
+ "$ c #9FA19E",
+ "% c #B1B3B0",
+ "& c #C1C3C0",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .++++ +. +. ",
+ " $&&&$ $% @&. ",
+ " $$ .&#&@ ",
+ " $%##+ @&$ ",
+ " $%##+ #&% ",
+ " $$ +&@&# ",
+ " $$ %$ @&+ ",
+ " ++ .+. ++ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* fxShiftUpOff_xpm[] = {
-"18 18 7 1",
-" c #242523",
-". c #4D4F4C",
-"+ c #A3A5A2",
-"@ c #868885",
-"# c #C1C3C0",
-"$ c #313330",
-"% c #626361",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .+@ ",
-" @+#. ",
-" $#%+@ ",
-" %# %#$ ",
-" +@ $#% ",
-" $#. @+ ",
-" $. $. ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 7 1",
+ " c #242523",
+ ". c #4D4F4C",
+ "+ c #A3A5A2",
+ "@ c #868885",
+ "# c #C1C3C0",
+ "$ c #313330",
+ "% c #626361",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .+@ ",
+ " @+#. ",
+ " $#%+@ ",
+ " %# %#$ ",
+ " +@ $#% ",
+ " $#. @+ ",
+ " $. $. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* fxShiftUpOn_xpm[] = {
-"18 18 5 1",
-" c #4D4F4C",
-". c #70726F",
-"+ c #A5A7A4",
-"@ c #C1C3BF",
-"# c #8E908D",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .++ ",
-" +@@. ",
-" @.+# ",
-" .@ .@ ",
-" +# @. ",
-" .@. #+ ",
-" . . ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 5 1",
+ " c #4D4F4C",
+ ". c #70726F",
+ "+ c #A5A7A4",
+ "@ c #C1C3BF",
+ "# c #8E908D",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .++ ",
+ " +@@. ",
+ " @.+# ",
+ " .@ .@ ",
+ " +# @. ",
+ " .@. #+ ",
+ " . . ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* fxShiftDownOff_xpm[] = {
-"18 18 7 1",
-" c #242523",
-". c #4D4F4C",
-"+ c #A3A5A2",
-"@ c #313330",
-"# c #626361",
-"$ c #868885",
-"% c #C1C3C0",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .+@ #$ ",
-" @%# +$ ",
-" $+ .%@ ",
-" .%@$+ ",
-" +$%# ",
-" #%%@ ",
-" @.. ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 7 1",
+ " c #242523",
+ ". c #4D4F4C",
+ "+ c #A3A5A2",
+ "@ c #313330",
+ "# c #626361",
+ "$ c #868885",
+ "% c #C1C3C0",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .+@ #$ ",
+ " @%# +$ ",
+ " $+ .%@ ",
+ " .%@$+ ",
+ " +$%# ",
+ " #%%@ ",
+ " @.. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* fxShiftDownOn_xpm[] = {
-"18 18 5 1",
-" c #4D4F4C",
-". c #70726F",
-"+ c #A5A7A4",
-"@ c #C1C3BF",
-"# c #8E908D",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" .+ .+ ",
-" @. +# ",
-" #+ .@. ",
-" .@.#+ ",
-" +#@. ",
-" #@@ ",
-" .. ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 5 1",
+ " c #4D4F4C",
+ ". c #70726F",
+ "+ c #A5A7A4",
+ "@ c #C1C3BF",
+ "# c #8E908D",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .+ .+ ",
+ " @. +# ",
+ " #+ .@. ",
+ " .@.#+ ",
+ " +#@. ",
+ " #@@ ",
+ " .. ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* vstLogo_xpm[] = {
-"65 38 8 1",
-" c #161715",
-". c #2B2D2A",
-"+ c #474846",
-"@ c #6A6C69",
-"# c #8C8E8B",
-"$ c #A8AAA7",
-"% c #C7C9C6",
-"& c #EEF0ED",
-" @#############################################################+ ",
-"@#.............................................................$+",
-"#. .#",
-"#. .#",
-"#. ...... .. .#",
-"#. .@$$$####$%$#@.+&$ .#",
-"#. .#$$#+. +#$%%%%$ .#",
-"#. .$$#$ .#$$%$ .#",
-"#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#",
-"#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#",
-"#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#",
-"#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#",
-"#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#",
-"#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#",
-"#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#",
-"#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#",
-"#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#",
-"#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#",
-"#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#",
-"#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#",
-"#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#",
-"#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#",
-"#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#",
-"#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#",
-"#. .#",
-"#. .#",
-"#. .#",
-"#. .#",
-"#. .#",
-"#. .@$$$$$$$$ .$%%%%%%# .#",
-"#. ....... .@@@@@@@@@. .#",
-"#. ........ @@@+@@@@@@@@@@+ .#",
-"@# ......... .####@@@@@@@@@@@+ #@",
-" @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ",
-" ......... .@@@@@@@@@@@@@@@. ",
-" ........ @@@@@@@@@@. ",
-" ........... .@@@@@@@@@ ",
-" .......... .@@@@@@@@ "};
-
+ "65 38 8 1",
+ " c #161715",
+ ". c #2B2D2A",
+ "+ c #474846",
+ "@ c #6A6C69",
+ "# c #8C8E8B",
+ "$ c #A8AAA7",
+ "% c #C7C9C6",
+ "& c #EEF0ED",
+ " @#############################################################+ ",
+ "@#.............................................................$+",
+ "#. .#",
+ "#. .#",
+ "#. ...... .. .#",
+ "#. .@$$$####$%$#@.+&$ .#",
+ "#. .#$$#+. +#$%%%%$ .#",
+ "#. .$$#$ .#$$%$ .#",
+ "#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#",
+ "#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#",
+ "#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#",
+ "#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#",
+ "#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#",
+ "#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#",
+ "#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#",
+ "#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#",
+ "#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#",
+ "#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#",
+ "#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#",
+ "#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#",
+ "#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#",
+ "#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#",
+ "#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#",
+ "#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#",
+ "#. .#",
+ "#. .#",
+ "#. .#",
+ "#. .#",
+ "#. .#",
+ "#. .@$$$$$$$$ .$%%%%%%# .#",
+ "#. ....... .@@@@@@@@@. .#",
+ "#. ........ @@@+@@@@@@@@@@+ .#",
+ "@# ......... .####@@@@@@@@@@@+ #@",
+ " @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ",
+ " ......... .@@@@@@@@@@@@@@@. ",
+ " ........ @@@@@@@@@@. ",
+ " ........... .@@@@@@@@@ ",
+ " .......... .@@@@@@@@ "};
const char* fxRemoveOff_xpm[] = {
-"18 18 9 1",
-" c None",
-". c #242623",
-"+ c #2F312E",
-"@ c #393A38",
-"# c #484A47",
-"$ c #5D5F5C",
-"% c #8E908D",
-"& c #9B9D9A",
-"* c #BDBFBC",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-".....+#@..@#+.....",
-"......&*++*&......",
-"......@*%%*@......",
-".......$**$.......",
-".......#**#.......",
-"......+*&&*+......",
-"......%*@@*%......",
-"......@@..@@......",
-"..................",
-"..................",
-"..................",
-"..................",
-".................."};
-
+ "18 18 9 1",
+ " c None",
+ ". c #242623",
+ "+ c #2F312E",
+ "@ c #393A38",
+ "# c #484A47",
+ "$ c #5D5F5C",
+ "% c #8E908D",
+ "& c #9B9D9A",
+ "* c #BDBFBC",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".....+#@..@#+.....",
+ "......&*++*&......",
+ "......@*%%*@......",
+ ".......$**$.......",
+ ".......#**#.......",
+ "......+*&&*+......",
+ "......%*@@*%......",
+ "......@@..@@......",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
const char* fxRemoveOn_xpm[] = {
-"18 18 9 1",
-" c None",
-". c #4D4F4C",
-"+ c #575956",
-"@ c #5C5D5B",
-"# c #666865",
-"$ c #787977",
-"% c #9C9E9B",
-"& c #A6A8A5",
-"* c #BFC1BE",
-"..................",
-"..................",
-"..................",
-"..................",
-"..................",
-"......#@..@#......",
-"......&*++*&......",
-"......@*%%*@......",
-".......$**$.......",
-".......#**#.......",
-"......+*&&*+......",
-"......%*@+*%......",
-"......@+..+@......",
-"..................",
-"..................",
-"..................",
-"..................",
-".................."};
+ "18 18 9 1",
+ " c None",
+ ". c #4D4F4C",
+ "+ c #575956",
+ "@ c #5C5D5B",
+ "# c #666865",
+ "$ c #787977",
+ "% c #9C9E9B",
+ "& c #A6A8A5",
+ "* c #BFC1BE",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "......#@..@#......",
+ "......&*++*&......",
+ "......@*%%*@......",
+ ".......$**$.......",
+ ".......#**#.......",
+ "......+*&&*+......",
+ "......%*@+*%......",
+ "......@+..+@......",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
#endif // #ifdef WITH_VST
-
const char* divideOn_xpm[] = {
-"18 18 7 1",
-" c #5A5A5A",
-". c #696969",
-"+ c #757575",
-"@ c #8B8B8B",
-"# c #AAAAAA",
-"$ c #BBBBBB",
-"% c #BDBDBD",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @@ ",
-" %$ ",
-" ++ ",
-" .########. ",
-" .########. ",
-" ++ ",
-" %$ ",
-" @@ ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 7 1",
+ " c #5A5A5A",
+ ". c #696969",
+ "+ c #757575",
+ "@ c #8B8B8B",
+ "# c #AAAAAA",
+ "$ c #BBBBBB",
+ "% c #BDBDBD",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @@ ",
+ " %$ ",
+ " ++ ",
+ " .########. ",
+ " .########. ",
+ " ++ ",
+ " %$ ",
+ " @@ ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* divideOff_xpm[] = {
-"18 18 8 1",
-" c #252525",
-". c #3B3B3B",
-"+ c #4D4D4D",
-"@ c #6D6D6D",
-"# c #6E6E6E",
-"$ c #9C9C9C",
-"% c #B5B5B5",
-"& c #B7B7B7",
-" ",
-" ",
-" ",
-" ",
-" ",
-" @# ",
-" &% ",
-" ++ ",
-" .$$$$$$$$. ",
-" .$$$$$$$$. ",
-" ++ ",
-" &% ",
-" @# ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #252525",
+ ". c #3B3B3B",
+ "+ c #4D4D4D",
+ "@ c #6D6D6D",
+ "# c #6E6E6E",
+ "$ c #9C9C9C",
+ "% c #B5B5B5",
+ "& c #B7B7B7",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " @# ",
+ " &% ",
+ " ++ ",
+ " .$$$$$$$$. ",
+ " .$$$$$$$$. ",
+ " ++ ",
+ " &% ",
+ " @# ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* multiplyOn_xpm[] = {
-"18 18 8 1",
-" c #595B58",
-". c #737572",
-"+ c #747673",
-"@ c #8B8D8A",
-"# c #8D8F8C",
-"$ c #8E908D",
-"% c #8F918E",
-"& c #C7C9C6",
-" ",
-" ",
-" ",
-" ",
-" ",
-" + . ",
-" +&$ #&. ",
-" #&$#&# ",
-" @&&# ",
-" @&&% ",
-" @&#@&% ",
-" +&# #&+ ",
-" + . ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #595B58",
+ ". c #737572",
+ "+ c #747673",
+ "@ c #8B8D8A",
+ "# c #8D8F8C",
+ "$ c #8E908D",
+ "% c #8F918E",
+ "& c #C7C9C6",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " + . ",
+ " +&$ #&. ",
+ " #&$#&# ",
+ " @&&# ",
+ " @&&% ",
+ " @&#@&% ",
+ " +&# #&+ ",
+ " + . ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* multiplyOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #4A4C49",
-"+ c #4D4E4C",
-"@ c #6D6F6C",
-"# c #717370",
-"$ c #737572",
-"% c #757774",
-"& c #C7C9C6",
-" ",
-" ",
-" ",
-" ",
-" ",
-" + . ",
-" +&$ #&. ",
-" #&$#&# ",
-" @&&# ",
-" @&&% ",
-" @&$@&% ",
-" +&# #&+ ",
-" + . ",
-" ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #4A4C49",
+ "+ c #4D4E4C",
+ "@ c #6D6F6C",
+ "# c #717370",
+ "$ c #737572",
+ "% c #757774",
+ "& c #C7C9C6",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " + . ",
+ " +&$ #&. ",
+ " #&$#&# ",
+ " @&&# ",
+ " @&&% ",
+ " @&$@&% ",
+ " +&# #&+ ",
+ " + . ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* channelStop_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #312D2C",
-"+ c #413A3A",
-"@ c #615253",
-"# c #73605F",
-"$ c #7A6663",
-"% c #9C7E7D",
-"& c #B08D8E",
-" ",
-" ",
-" ",
-" ",
-" ##. ",
-" $&%@ ",
-" $&&&%+ ",
-" $&&&&&$. ",
-" $&&&&&&&@ ",
-" $&&&&&&&@. ",
-" $&&&&&$. ",
-" $&&&%+ ",
-" $&&@ ",
-" $#. ",
-" . ",
-" ",
-" ",
-" "};
-
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #312D2C",
+ "+ c #413A3A",
+ "@ c #615253",
+ "# c #73605F",
+ "$ c #7A6663",
+ "% c #9C7E7D",
+ "& c #B08D8E",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ##. ",
+ " $&%@ ",
+ " $&&&%+ ",
+ " $&&&&&$. ",
+ " $&&&&&&&@ ",
+ " $&&&&&&&@. ",
+ " $&&&&&$. ",
+ " $&&&%+ ",
+ " $&&@ ",
+ " $#. ",
+ " . ",
+ " ",
+ " ",
+ " "};
const char* channelPlay_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #554E56",
-"+ c #5A4D59",
-"@ c #605068",
-"# c #775086",
-"$ c #8A509C",
-"% c #9E50B5",
-"& c #AD52D0",
-" ",
-" ",
-" ",
-" . ",
-" $$. ",
-" $&%# ",
-" $&&&%@ ",
-" $&&&&&$. ",
-" $&&&&&&&#. ",
-" $&&&&&&&#. ",
-" $&&&&&$+ ",
-" $&&&%@ ",
-" $&&# ",
-" $$. ",
-" . ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #554E56",
+ "+ c #5A4D59",
+ "@ c #605068",
+ "# c #775086",
+ "$ c #8A509C",
+ "% c #9E50B5",
+ "& c #AD52D0",
+ " ",
+ " ",
+ " ",
+ " . ",
+ " $$. ",
+ " $&%# ",
+ " $&&&%@ ",
+ " $&&&&&$. ",
+ " $&&&&&&&#. ",
+ " $&&&&&&&#. ",
+ " $&&&&&$+ ",
+ " $&&&%@ ",
+ " $&&# ",
+ " $$. ",
+ " . ",
+ " ",
+ " ",
+ " "};
const char* armOff_xpm[] = {
-"18 18 8 1",
-" c #242523",
-". c #4F4445",
-"+ c #514647",
-"@ c #6D5C5E",
-"# c #8E7372",
-"$ c #AA8889",
-"% c #AC898A",
-"& c #B18E8F",
-" ",
-" ",
-" ",
-" ",
-" +#%%#. ",
-" @&&&&&&@ ",
-" +&&&&&&&&. ",
-" #&&&&&&&&# ",
-" %&&&&&&&&% ",
-" %&&&&&&&&% ",
-" #&&&&&&&&# ",
-" .&&&&&&&&. ",
-" @&&&&&&@ ",
-" .#%%#. ",
-" ",
-" ",
-" ",
-" "};
-
+ "18 18 8 1",
+ " c #242523",
+ ". c #4F4445",
+ "+ c #514647",
+ "@ c #6D5C5E",
+ "# c #8E7372",
+ "$ c #AA8889",
+ "% c #AC898A",
+ "& c #B18E8F",
+ " ",
+ " ",
+ " ",
+ " ",
+ " +#%%#. ",
+ " @&&&&&&@ ",
+ " +&&&&&&&&. ",
+ " #&&&&&&&&# ",
+ " %&&&&&&&&% ",
+ " %&&&&&&&&% ",
+ " #&&&&&&&&# ",
+ " .&&&&&&&&. ",
+ " @&&&&&&@ ",
+ " .#%%#. ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* armOn_xpm[] = {
-"18 18 8 1",
-" c #4D4F4C",
-". c #6B5077",
-"+ c #805191",
-"@ c #9950AD",
-"# c #9751B3",
-"$ c #9553AD",
-"% c #AA52C9",
-"& c #AE52D1",
-" ",
-" ",
-" ",
-" ",
-" .#%%#. ",
-" +&&&&&&+ ",
-" .&&&&&&&&. ",
-" #&&&&&&&&@ ",
-" %&&&&&&&&% ",
-" %&&&&&&&&% ",
-" #&&&&&&&&$ ",
-" .&&&&&&&&. ",
-" +&&&&&&+ ",
-" .@%%$. ",
-" ",
-" ",
-" ",
-" "};
+ "18 18 8 1",
+ " c #4D4F4C",
+ ". c #6B5077",
+ "+ c #805191",
+ "@ c #9950AD",
+ "# c #9751B3",
+ "$ c #9553AD",
+ "% c #AA52C9",
+ "& c #AE52D1",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .#%%#. ",
+ " +&&&&&&+ ",
+ " .&&&&&&&&. ",
+ " #&&&&&&&&@ ",
+ " %&&&&&&&&% ",
+ " %&&&&&&&&% ",
+ " #&&&&&&&&$ ",
+ " .&&&&&&&&. ",
+ " +&&&&&&+ ",
+ " .@%%$. ",
+ " ",
+ " ",
+ " ",
+ " "};
const char* armDisabled_xpm[] = {
-"18 18 7 1",
-" c None",
-". c #232523",
-"+ c #303230",
-"@ c #393B38",
-"# c #424441",
-"$ c #4B4D4A",
-"% c #4D4F4C",
-"..................",
-"..................",
-"..................",
-"..................",
-"......+#$$#+......",
-".....@%%%%%%@.....",
-"....+%%%%%%%%+....",
-"....#%%%%%%%%#....",
-"....$%%%%%%%%$....",
-"....$%%%%%%%%$....",
-"....#%%%%%%%%#....",
-"....+%%%%%%%%+....",
-".....@%%%%%%@.....",
-"......+#$$#+......",
-"..................",
-"..................",
-"..................",
-".................."};
\ No newline at end of file
+ "18 18 7 1",
+ " c None",
+ ". c #232523",
+ "+ c #303230",
+ "@ c #393B38",
+ "# c #424441",
+ "$ c #4B4D4A",
+ "% c #4D4F4C",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "......+#$$#+......",
+ ".....@%%%%%%@.....",
+ "....+%%%%%%%%+....",
+ "....#%%%%%%%%#....",
+ "....$%%%%%%%%$....",
+ "....$%%%%%%%%$....",
+ "....#%%%%%%%%#....",
+ "....+%%%%%%%%+....",
+ ".....@%%%%%%@.....",
+ "......+#$$#+......",
+ "..................",
+ "..................",
+ "..................",
+ ".................."};
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GRAPHICS_H
#define G_GRAPHICS_H
-
extern const char* giada_logo_xpm[];
extern const char* loopRepeat_xpm[];
extern const char* inputRecOn_xpm[];
extern const char* inputRecOff_xpm[];
+extern const char* freeInputRecOn_xpm[];
+extern const char* freeInputRecOff_xpm[];
+
extern const char* divideOn_xpm[];
extern const char* divideOff_xpm[];
extern const char* multiplyOn_xpm[];
extern const char* giada_icon[];
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#include "idManager.h"
-
-namespace giada::m
+namespace giada::m
{
-IdManager::IdManager() : m_id(0)
+IdManager::IdManager()
+: m_id(0)
{
}
-
/* -------------------------------------------------------------------------- */
-
void IdManager::set(ID id)
{
if (id != 0 && id > m_id)
m_id = id;
}
-
/* -------------------------------------------------------------------------- */
+ID IdManager::generate(ID id)
+{
+ if (id != 0)
+ {
+ m_id = id;
+ return id;
+ }
+ return ++m_id;
+}
+
+/* -------------------------------------------------------------------------- */
-ID IdManager::get(ID id)
+ID IdManager::get()
{
- return id != 0 ? id : ++m_id;
+ return m_id;
}
-} // giada::m::
+} // namespace giada::m
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_ID_MANAGER_H
#define G_ID_MANAGER_H
-
#include "core/types.h"
-
-namespace giada::m
+namespace giada::m
{
class IdManager
{
public:
-
IdManager();
/* set
void set(ID id);
- /* get
+ /* generate
Generates a new unique id. If 'id' parameter is passed in is valid, it just
returns it with no unique id generation. Useful when loading things from the
model that already have their own id. */
- ID get(ID id=0);
+ ID generate(ID id = 0);
+
+ /* get
+ Returns the current id, a.k.a. the last generated one. */
-private:
+ ID get();
+ private:
ID m_id;
};
-} // giada::m::
-
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <thread>
#include <atomic>
#include <ctime>
+#include <thread>
#ifdef __APPLE__
- #include <pwd.h>
+#include <pwd.h>
#endif
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(WITH_VST)
- #include <X11/Xlib.h> // For XInitThreads
+#include <X11/Xlib.h> // For XInitThreads
#endif
-#include <FL/Fl.H>
-#include "deps/json/single_include/nlohmann/json.hpp"
-#include "gui/updater.h"
-#include "utils/log.h"
-#include "utils/fs.h"
-#include "utils/time.h"
-#include "utils/gui.h"
-#include "utils/ver.h"
-#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/warnings.h"
-#include "glue/main.h"
-#include "core/model/storage.h"
#include "core/channels/channelManager.h"
-#include "core/mixer.h"
-#include "core/wave.h"
-#include "core/const.h"
#include "core/clock.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/eventDispatcher.h"
+#include "core/kernelAudio.h"
+#include "core/kernelMidi.h"
+#include "core/midiMapConf.h"
+#include "core/mixer.h"
#include "core/mixerHandler.h"
-#include "core/sequencer.h"
+#include "core/model/model.h"
+#include "core/model/storage.h"
#include "core/patch.h"
-#include "core/conf.h"
-#include "core/waveManager.h"
-#include "core/plugins/pluginManager.h"
#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/recManager.h"
#include "core/recorder.h"
#include "core/recorderHandler.h"
-#include "core/recManager.h"
-#include "core/midiMapConf.h"
-#include "core/kernelMidi.h"
-#include "core/kernelAudio.h"
+#include "core/sequencer.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
+#include "deps/json/single_include/nlohmann/json.hpp"
+#include "glue/main.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/updater.h"
#include "init.h"
-
+#include "utils/fs.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/time.h"
+#include "utils/ver.h"
+#include <FL/Fl.H>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace m {
-namespace init
+namespace giada::m::init
{
namespace
{
{
if (!conf::read())
u::log::print("[init] Can't read configuration file! Using default values\n");
-
+
patch::init();
midimap::init();
midimap::setDefault();
model::load(conf::conf);
-
+
if (!u::log::init(conf::conf.logMode))
u::log::print("[init] log init failed! Using default stdout\n");
u::log::print("[init] MIDI map read failed!\n");
}
-
/* -------------------------------------------------------------------------- */
+void initSystem_()
+{
+ model::init();
+ eventDispatcher::init();
+}
+
+/* -------------------------------------------------------------------------- */
void initAudio_()
{
kernelAudio::startStream();
}
-
/* -------------------------------------------------------------------------- */
-
void initMIDI_()
{
kernelMidi::setApi(conf::conf.midiSystem);
kernelMidi::openOutDevice(conf::conf.midiPortOut);
- kernelMidi::openInDevice(conf::conf.midiPortIn);
+ kernelMidi::openInDevice(conf::conf.midiPortIn);
}
-
/* -------------------------------------------------------------------------- */
-
void initGUI_(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(__FreeBSD__)) && defined(WITH_VST)
XInitThreads();
#endif
G_MainWin = new v::gdMainWindow(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv);
G_MainWin->resize(conf::conf.mainWindowX, conf::conf.mainWindowY, conf::conf.mainWindowW,
- conf::conf.mainWindowH);
+ conf::conf.mainWindowH);
u::gui::updateMainWinLabel(patch::patch.name == "" ? G_DEFAULT_PATCH_NAME : patch::patch.name);
-
+
if (!kernelAudio::isReady())
v::gdAlert("Your soundcard isn't configured correctly.\n"
- "Check the configuration and restart Giada.");
+ "Check the configuration and restart Giada.");
+ v::updater::init();
u::gui::updateStaticWidgets();
-
- Fl::add_timeout(G_GUI_REFRESH_RATE, v::updater::update, nullptr);
}
-
/* -------------------------------------------------------------------------- */
-
void shutdownAudio_()
{
- if (kernelAudio::isReady()) {
+ if (kernelAudio::isReady())
+ {
kernelAudio::closeDevice();
u::log::print("[init] KernelAudio closed\n");
mh::close();
#endif
}
-
/* -------------------------------------------------------------------------- */
-
void shutdownGUI_()
{
u::gui::closeAllSubwindows();
u::log::print("[init] All subwindows and UI thread closed\n");
}
-
/* -------------------------------------------------------------------------- */
-
void printBuildInfo_()
{
u::log::print("[init] Giada %s\n", G_VERSION_STR);
u::log::print("[init] Libsamplerate\n"); // TODO - print version
u::log::print("[init] Libsndfile - %s\n", u::ver::getLibsndfileVersion());
u::log::print("[init] JSON for modern C++ - %d.%d.%d\n",
- NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH);
+ NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH);
#ifdef WITH_VST
u::log::print("[init] JUCE - %d.%d.%d\n", JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER);
#endif
kernelAudio::logCompiledAPIs();
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void startup(int argc, char** argv)
{
printBuildInfo_();
+
initConf_();
+ initSystem_();
initAudio_();
initMIDI_();
initGUI_(argc, argv);
}
-
/* -------------------------------------------------------------------------- */
-
void closeMainWindow()
{
if (!v::gdConfirmWin("Warning", "Quit Giada: are you sure?"))
return;
+ v::updater::close();
G_MainWin->hide();
delete G_MainWin;
}
-
/* -------------------------------------------------------------------------- */
-
void reset()
-{
+{
u::gui::closeAllSubwindows();
- G_MainWin->clearKeyboard();
+ G_MainWin->clearKeyboard();
mh::close();
#ifdef WITH_VST
pluginHost::close();
#endif
+ model::init();
channelManager::init();
waveManager::init();
clock::init(conf::conf.samplerate, conf::conf.midiTCfps);
pluginManager::init(conf::conf.samplerate, kernelAudio::getRealBufSize());
#endif
-
u::gui::updateMainWinLabel(G_DEFAULT_PATCH_NAME);
u::gui::updateStaticWidgets();
}
-
/* -------------------------------------------------------------------------- */
-
void shutdown()
{
shutdownGUI_();
u::log::print("[init] Giada %s closed\n\n", G_VERSION_STR);
u::log::close();
}
-}}} // giada::m::init
+} // namespace giada::m::init
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_INIT_H
#define G_INIT_H
-
-namespace giada {
-namespace m {
-namespace init
+namespace giada::m::init
{
void startup(int argc, char** argv);
-void reset();
+void reset();
void closeMainWindow();
void shutdown();
-}}} // giada::m::init
+} // namespace giada::m::init
-#endif
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "deps/rtaudio/RtAudio.h"
-#include "utils/log.h"
-#include "glue/main.h"
-#include "core/model/model.h"
+#include "kernelAudio.h"
#include "conf.h"
#include "const.h"
+#include "core/clock.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/recManager.h"
+#include "deps/rtaudio/RtAudio.h"
+#include "glue/main.h"
#include "mixer.h"
-#include "const.h"
-#include "kernelAudio.h"
-
+#include "utils/log.h"
+#include "utils/vector.h"
namespace giada::m::kernelAudio
{
namespace
{
-RtAudio* rtSystem = nullptr;
-unsigned numDevs = 0;
-bool inputEnabled = false;
-unsigned realBufsize = 0; // Real buffer size from the soundcard
-int api = 0;
+std::vector<Device> devices_;
+RtAudio* rtSystem_ = nullptr;
+bool inputEnabled_ = false;
+unsigned realBufsize_ = 0; // Real buffer size from the soundcard
+int api_ = 0;
-#ifdef WITH_AUDIO_JACK
+/* -------------------------------------------------------------------------- */
-JackState jackState;
+#ifdef WITH_AUDIO_JACK
jack_client_t* jackGetHandle_()
{
- return static_cast<jack_client_t*>(rtSystem->HACK__getJackClient());
+ return static_cast<jack_client_t*>(rtSystem_->HACK__getJackClient());
}
#endif
-} // {anonymous}
+/* -------------------------------------------------------------------------- */
+
+Device fetchDevice_(size_t deviceIndex)
+{
+ try
+ {
+ RtAudio::DeviceInfo info = rtSystem_->getDeviceInfo(deviceIndex);
+
+ if (!info.probed)
+ {
+ u::log::print("[KA] Can't probe device %d\n", deviceIndex);
+ return {deviceIndex};
+ }
+
+ return {
+ deviceIndex,
+ true,
+ info.name,
+ static_cast<int>(info.outputChannels),
+ static_cast<int>(info.inputChannels),
+ static_cast<int>(info.duplexChannels),
+ info.isDefaultOutput,
+ info.isDefaultInput,
+ u::vector::cast<int>(info.sampleRates)};
+ }
+ catch (RtAudioError& e)
+ {
+ u::log::print("[KA] Error fetching device %d: %s\n", deviceIndex, e.getMessage());
+ return {0};
+ }
+}
/* -------------------------------------------------------------------------- */
+
+std::vector<Device> fetchDevices_()
+{
+ std::vector<Device> out;
+ for (unsigned i = 0; i < rtSystem_->getDeviceCount(); i++)
+ out.push_back(fetchDevice_(i));
+ return out;
+}
+
/* -------------------------------------------------------------------------- */
+
+void printDevices_(const std::vector<Device>& devices)
+{
+ u::log::print("[KA] %d device(s) found\n", devices.size());
+ for (const Device& d : devices)
+ {
+ u::log::print(" %d) %s\n", d.index, d.name);
+ u::log::print(" ins=%d outs=%d duplex=%d\n", d.maxInputChannels, d.maxOutputChannels, d.maxDuplexChannels);
+ u::log::print(" isDefaultOut=%d isDefaultIn=%d\n", d.isDefaultOut, d.isDefaultIn);
+ u::log::print(" sampleRates:\n\t");
+ for (int s : d.sampleRates)
+ u::log::print("%d ", s);
+ u::log::print("\n");
+ }
+}
+
/* -------------------------------------------------------------------------- */
+bool canRender_()
+{
+ return model::get().kernel.audioReady && model::get().mixer.state->active.load() == true;
+}
+
+/* -------------------------------------------------------------------------- */
+
+int callback_(void* outBuf, void* inBuf, unsigned bufferSize, double /*streamTime*/,
+ RtAudioStreamStatus /*status*/, void* /*userData*/)
+{
+ AudioBuffer out(static_cast<float*>(outBuf), bufferSize, G_MAX_IO_CHANS);
+ AudioBuffer in;
+ if (isInputEnabled())
+ in = AudioBuffer(static_cast<float*>(inBuf), bufferSize, conf::conf.channelsInCount);
+
+ /* Clean up output buffer before any rendering. Do this even if mixer is
+ disabled to avoid audio leftovers during a temporary suspension (e.g. when
+ loading a new patch). */
+
+ out.clear();
+
+ if (!canRender_())
+ return 0;
+
+#ifdef WITH_AUDIO_JACK
+ if (getAPI() == G_SYS_API_JACK)
+ clock::recvJackSync();
+#endif
+
+ mixer::RenderInfo info;
+ info.isAudioReady = model::get().kernel.audioReady;
+ info.hasInput = isInputEnabled();
+ info.isClockActive = clock::isActive();
+ info.isClockRunning = clock::isRunning();
+ info.canLineInRec = recManager::isRecordingInput() && isInputEnabled();
+ info.limitOutput = conf::conf.limitOutput;
+ info.inToOut = mh::getInToOut();
+ info.maxFramesToRec = conf::conf.inputRecMode == InputRecMode::FREE ? clock::getMaxFramesInLoop() : clock::getFramesInLoop();
+ info.outVol = mh::getOutVol();
+ info.inVol = mh::getInVol();
+ info.recTriggerLevel = conf::conf.recTriggerLevel;
+
+ return mixer::render(out, in, info);
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
#ifdef WITH_AUDIO_JACK
#endif
-
/* -------------------------------------------------------------------------- */
-
bool isReady()
{
- model::KernelLock lock(model::kernel);
- return model::kernel.get()->audioReady;
+ return model::get().kernel.audioReady;
}
-
/* -------------------------------------------------------------------------- */
-
int openDevice()
{
- api = conf::conf.soundSystem;
- u::log::print("[KA] using system 0x%x\n", api);
+ api_ = conf::conf.soundSystem;
+ u::log::print("[KA] using system 0x%x\n", api_);
#if defined(__linux__) || defined(__FreeBSD__)
- if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
- rtSystem = new RtAudio(RtAudio::UNIX_JACK);
- else
- if (api == G_SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
- rtSystem = new RtAudio(RtAudio::LINUX_ALSA);
- else
- if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
- rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
+ if (api_ == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+ rtSystem_ = new RtAudio(RtAudio::UNIX_JACK);
+ else if (api_ == G_SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
+ rtSystem_ = new RtAudio(RtAudio::LINUX_ALSA);
+ else if (api_ == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+ rtSystem_ = new RtAudio(RtAudio::LINUX_PULSE);
#elif defined(__FreeBSD__)
- if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
- rtSystem = new RtAudio(RtAudio::UNIX_JACK);
- else
- if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
- rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
+ if (api_ == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+ rtSystem_ = new RtAudio(RtAudio::UNIX_JACK);
+ else if (api_ == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+ rtSystem_ = new RtAudio(RtAudio::LINUX_PULSE);
#elif defined(_WIN32)
- if (api == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
- rtSystem = new RtAudio(RtAudio::WINDOWS_DS);
- else
- if (api == G_SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
- rtSystem = new RtAudio(RtAudio::WINDOWS_ASIO);
- else
- if (api == G_SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
- rtSystem = new RtAudio(RtAudio::WINDOWS_WASAPI);
+ if (api_ == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
+ rtSystem_ = new RtAudio(RtAudio::WINDOWS_DS);
+ else if (api_ == G_SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
+ rtSystem_ = new RtAudio(RtAudio::WINDOWS_ASIO);
+ else if (api_ == G_SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
+ rtSystem_ = new RtAudio(RtAudio::WINDOWS_WASAPI);
#elif defined(__APPLE__)
- if (api == G_SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
- rtSystem = new RtAudio(RtAudio::MACOSX_CORE);
+ if (api_ == G_SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
+ rtSystem_ = new RtAudio(RtAudio::MACOSX_CORE);
#endif
- else {
+ else
+ {
u::log::print("[KA] No API available, nothing to do!\n");
return 0;
}
u::log::print("[KA] Opening device out=%d, in=%d, samplerate=%d\n",
- conf::conf.soundDeviceOut, conf::conf.soundDeviceIn, conf::conf.samplerate);
+ conf::conf.soundDeviceOut, conf::conf.soundDeviceIn, conf::conf.samplerate);
- numDevs = rtSystem->getDeviceCount();
+ devices_ = fetchDevices_();
+ printDevices_(devices_);
- if (numDevs < 1) {
- u::log::print("[KA] no devices found with this API\n");
+ /* Abort here if devices found are zero. */
+
+ if (devices_.size() == 0)
+ {
closeDevice();
return 0;
}
- else {
- u::log::print("[KA] %d device(s) found\n", numDevs);
- for (unsigned i=0; i<numDevs; i++)
- u::log::print(" %d) %s\n", i, getDeviceName(i));
- }
RtAudio::StreamParameters outParams;
RtAudio::StreamParameters inParams;
- outParams.deviceId = conf::conf.soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT ? getDefaultOut() : conf::conf.soundDeviceOut;
- outParams.nChannels = G_MAX_IO_CHANS;
- outParams.firstChannel = conf::conf.channelsOut * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ...
+ outParams.deviceId = conf::conf.soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT ? rtSystem_->getDefaultOutputDevice() : conf::conf.soundDeviceOut;
+ outParams.nChannels = conf::conf.channelsOutCount;
+ outParams.firstChannel = conf::conf.channelsOutStart;
/* Input device can be disabled. Unlike the output, here we are using all
channels and let the user choose which one to record from in the configuration
panel. */
- if (conf::conf.soundDeviceIn != -1) {
+ if (conf::conf.soundDeviceIn != -1)
+ {
inParams.deviceId = conf::conf.soundDeviceIn;
inParams.nChannels = conf::conf.channelsInCount;
inParams.firstChannel = conf::conf.channelsInStart;
- inputEnabled = true;
+ inputEnabled_ = true;
}
else
- inputEnabled = false;
+ inputEnabled_ = false;
RtAudio::StreamOptions options;
- options.streamName = G_APP_NAME;
- options.numberOfBuffers = 4;
+ options.streamName = G_APP_NAME;
+ options.numberOfBuffers = 4; // TODO - wtf?
- realBufsize = conf::conf.buffersize;
+ realBufsize_ = conf::conf.buffersize;
#ifdef WITH_AUDIO_JACK
- if (api == G_SYS_API_JACK) {
- conf::conf.samplerate = getFreq(conf::conf.soundDeviceOut, 0);
+ if (api_ == G_SYS_API_JACK)
+ {
+ conf::conf.samplerate = devices_[conf::conf.soundDeviceOut].sampleRates[0];
u::log::print("[KA] JACK in use, samplerate=%d\n", conf::conf.samplerate);
}
#endif
- try {
- rtSystem->openStream(
- &outParams, // output params
- conf::conf.soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected
- RTAUDIO_FLOAT32, // audio format
- conf::conf.samplerate, // sample rate
- &realBufsize, // buffer size in byte
- &mixer::masterPlay, // audio callback
- nullptr, // user data (unused)
- &options);
-
- model::onSwap(model::kernel, [](model::Kernel& k) {
- k.audioReady = true;
- });
+ try
+ {
+ rtSystem_->openStream(
+ &outParams, // output params
+ conf::conf.soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected
+ RTAUDIO_FLOAT32, // audio format
+ conf::conf.samplerate, // sample rate
+ &realBufsize_, // buffer size in byte
+ &callback_, // audio callback
+ nullptr, // user data (unused)
+ &options);
+
+ model::get().kernel.audioReady = true;
+ model::swap(model::SwapType::NONE);
return 1;
}
- catch (RtAudioError &e) {
- u::log::print("[KA] rtSystem init error: %s\n", e.getMessage());
+ catch (RtAudioError& e)
+ {
+ u::log::print("[KA] rtSystem_ init error: %s\n", e.getMessage());
closeDevice();
return 0;
}
}
-
/* -------------------------------------------------------------------------- */
-
int startStream()
{
- try {
- rtSystem->startStream();
- u::log::print("[KA] latency = %lu\n", rtSystem->getStreamLatency());
+ try
+ {
+ rtSystem_->startStream();
+ u::log::print("[KA] latency = %lu\n", rtSystem_->getStreamLatency());
return 1;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& e)
+ {
u::log::print("[KA] Start stream error: %s\n", e.getMessage());
return 0;
}
}
-
/* -------------------------------------------------------------------------- */
-
int stopStream()
{
- try {
- rtSystem->stopStream();
+ try
+ {
+ rtSystem_->stopStream();
return 1;
}
- catch (RtAudioError& /*e*/) {
+ catch (RtAudioError& /*e*/)
+ {
u::log::print("[KA] Stop stream error\n");
return 0;
}
}
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string getDeviceName(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).name;
- }
- catch (RtAudioError& /*e*/) {
- u::log::print("[KA] invalid device ID = %d\n", dev);
- return "";
- }
-}
-
-
/* -------------------------------------------------------------------------- */
-
int closeDevice()
{
- if (rtSystem->isStreamOpen()) {
- rtSystem->stopStream();
- rtSystem->closeStream();
- delete rtSystem;
- rtSystem = nullptr;
+ if (rtSystem_->isStreamOpen())
+ {
+ rtSystem_->stopStream();
+ rtSystem_->closeStream();
+ delete rtSystem_;
+ rtSystem_ = nullptr;
}
return 1;
}
-
/* -------------------------------------------------------------------------- */
-
-unsigned getMaxInChans(int dev)
-{
- if (dev == -1) return 0;
-
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).inputChannels;
- }
- catch (RtAudioError& /*e*/) {
- u::log::print("[KA] Unable to get input channels\n");
- return 0;
- }
-}
-
+unsigned getRealBufSize() { return realBufsize_; }
+bool isInputEnabled() { return inputEnabled_; }
/* -------------------------------------------------------------------------- */
-
-unsigned getMaxOutChans(unsigned dev)
+Device getDevice(const char* name)
{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).outputChannels;
- }
- catch (RtAudioError& /*e*/) {
- u::log::print("[KA] Unable to get output channels\n");
- return 0;
- }
+ for (Device device : devices_)
+ if (name == device.name)
+ return device;
+ return {0, false};
}
-
/* -------------------------------------------------------------------------- */
-
-bool isProbed(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).probed;
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-unsigned getDuplexChans(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).duplexChannels;
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool isDefaultIn(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultInput;
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool isDefaultOut(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultOutput;
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int getTotalFreqs(unsigned dev)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.size();
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int getFreq(unsigned dev, int i)
-{
- try {
- return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.at(i);
- }
- catch (RtAudioError& /*e*/) {
- return 0;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-unsigned getRealBufSize() { return realBufsize; }
-bool isInputEnabled() { return inputEnabled; }
-unsigned countDevices() { return numDevs; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int getDefaultIn()
-{
- return rtSystem->getDefaultInputDevice();
-}
-
-int getDefaultOut()
+const std::vector<Device>& getDevices()
{
- return rtSystem->getDefaultOutputDevice();
+ return devices_;
}
-
-/* -------------------------------------------------------------------------- */
-
-
-int getDeviceByName(const char* name)
-{
- for (unsigned i=0; i<numDevs; i++)
- if (name == getDeviceName(i))
- return i;
- return -1;
-}
-
-
/* -------------------------------------------------------------------------- */
-
bool hasAPI(int API)
{
std::vector<RtAudio::Api> APIs;
RtAudio::getCompiledApi(APIs);
- for (unsigned i=0; i<APIs.size(); i++)
+ for (unsigned i = 0; i < APIs.size(); i++)
if (APIs.at(i) == API)
return true;
return false;
}
-
-int getAPI() { return api; }
-
+int getAPI() { return api_; }
/* -------------------------------------------------------------------------- */
-
void logCompiledAPIs()
{
std::vector<RtAudio::Api> APIs;
u::log::print("[KA] Compiled RtAudio APIs: %d\n", APIs.size());
- for (const RtAudio::Api& api : APIs) {
- switch (api) {
- case RtAudio::Api::LINUX_ALSA:
- u::log::print(" ALSA\n"); break;
- case RtAudio::Api::LINUX_PULSE:
- u::log::print(" PulseAudio\n"); break;
- case RtAudio::Api::UNIX_JACK:
- u::log::print(" JACK\n"); break;
- case RtAudio::Api::MACOSX_CORE:
- u::log::print(" CoreAudio\n"); break;
- case RtAudio::Api::WINDOWS_WASAPI:
- u::log::print(" WASAPI\n"); break;
- case RtAudio::Api::WINDOWS_ASIO:
- u::log::print(" ASIO\n"); break;
- case RtAudio::Api::WINDOWS_DS:
- u::log::print(" DirectSound\n"); break;
- case RtAudio::Api::RTAUDIO_DUMMY:
- u::log::print(" Dummy\n"); break;
- default:
- u::log::print(" (unknown)\n"); break;
+ for (const RtAudio::Api& api_ : APIs)
+ {
+ switch (api_)
+ {
+ case RtAudio::Api::LINUX_ALSA:
+ u::log::print(" ALSA\n");
+ break;
+ case RtAudio::Api::LINUX_PULSE:
+ u::log::print(" PulseAudio\n");
+ break;
+ case RtAudio::Api::UNIX_JACK:
+ u::log::print(" JACK\n");
+ break;
+ case RtAudio::Api::MACOSX_CORE:
+ u::log::print(" CoreAudio\n");
+ break;
+ case RtAudio::Api::WINDOWS_WASAPI:
+ u::log::print(" WASAPI\n");
+ break;
+ case RtAudio::Api::WINDOWS_ASIO:
+ u::log::print(" ASIO\n");
+ break;
+ case RtAudio::Api::WINDOWS_DS:
+ u::log::print(" DirectSound\n");
+ break;
+ case RtAudio::Api::RTAUDIO_DUMMY:
+ u::log::print(" Dummy\n");
+ break;
+ default:
+ u::log::print(" (unknown)\n");
+ break;
}
}
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_AUDIO_JACK
JackState jackTransportQuery()
{
- if (api != G_SYS_API_JACK)
- return jackState;
+ if (api_ != G_SYS_API_JACK)
+ return {};
jack_position_t position;
jack_transport_state_t ts = jack_transport_query(jackGetHandle_(), &position);
return {
- ts != JackTransportStopped,
- position.beats_per_minute,
- position.frame
- };
+ ts != JackTransportStopped,
+ position.beats_per_minute,
+ position.frame};
}
-
/* -------------------------------------------------------------------------- */
-
void jackStart()
{
- if (api == G_SYS_API_JACK)
+ if (api_ == G_SYS_API_JACK)
jack_transport_start(jackGetHandle_());
}
-
/* -------------------------------------------------------------------------- */
-
void jackSetPosition(uint32_t frame)
{
- if (api != G_SYS_API_JACK)
- return;
+ if (api_ != G_SYS_API_JACK)
+ return;
jack_position_t position;
jack_transport_query(jackGetHandle_(), &position);
position.frame = frame;
jack_transport_reposition(jackGetHandle_(), &position);
}
-
/* -------------------------------------------------------------------------- */
-
void jackSetBpm(double bpm)
{
- if (api != G_SYS_API_JACK)
+ if (api_ != G_SYS_API_JACK)
return;
jack_position_t position;
jack_transport_query(jackGetHandle_(), &position);
- position.valid = jack_position_bits_t::JackPositionBBT;
- position.bar = 0; // no such info from Giada
- position.beat = 0; // no such info from Giada
- position.tick = 0; // no such info from Giada
+ position.valid = jack_position_bits_t::JackPositionBBT;
+ position.bar = 0; // no such info from Giada
+ position.beat = 0; // no such info from Giada
+ position.tick = 0; // no such info from Giada
position.beats_per_minute = bpm;
jack_transport_reposition(jackGetHandle_(), &position);
}
-
/* -------------------------------------------------------------------------- */
-
void jackStop()
{
- if (api == G_SYS_API_JACK)
+ if (api_ == G_SYS_API_JACK)
jack_transport_stop(jackGetHandle_());
}
-#endif // WITH_AUDIO_JACK
-} // giada::m::kernelAudio
+#endif // WITH_AUDIO_JACK
+} // namespace giada::m::kernelAudio
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_KERNELAUDIO_H
#define G_KERNELAUDIO_H
-
+#include <optional>
#include <string>
+#include <vector>
#ifdef WITH_AUDIO_JACK
- #include <jack/jack.h>
- #include <jack/intclient.h>
- #include <jack/transport.h>
+#include <jack/intclient.h>
+#include <jack/jack.h>
+#include <jack/transport.h>
#endif
-
namespace giada::m::kernelAudio
{
#ifdef WITH_AUDIO_JACK
#endif
+struct Device
+{
+ size_t index = 0;
+ bool probed = false;
+ std::string name = "";
+ int maxOutputChannels = 0;
+ int maxInputChannels = 0;
+ int maxDuplexChannels = 0;
+ bool isDefaultOut = false;
+ bool isDefaultIn = false;
+ std::vector<int> sampleRates = {};
+};
+
int openDevice();
int closeDevice();
int startStream();
int stopStream();
-bool isReady();
-bool isProbed(unsigned dev);
-bool isDefaultIn(unsigned dev);
-bool isDefaultOut(unsigned dev);
-bool isInputEnabled();
-std::string getDeviceName(unsigned dev);
-unsigned getMaxInChans(int dev);
-unsigned getMaxOutChans(unsigned dev);
-unsigned getDuplexChans(unsigned dev);
-unsigned getRealBufSize();
-unsigned countDevices();
-int getTotalFreqs(unsigned dev);
-int getFreq(unsigned dev, int i);
-int getDeviceByName(const char* name);
-int getDefaultOut();
-int getDefaultIn();
-bool hasAPI(int API);
-int getAPI();
-void logCompiledAPIs();
+bool isReady();
+bool isInputEnabled();
+unsigned getRealBufSize();
+bool hasAPI(int API);
+int getAPI();
+void logCompiledAPIs();
+Device getDevice(const char* name);
+const std::vector<Device>& getDevices();
#ifdef WITH_AUDIO_JACK
-void jackStart();
-void jackStop();
-void jackSetPosition(uint32_t frame);
-void jackSetBpm(double bpm);
+void jackStart();
+void jackStop();
+void jackSetPosition(uint32_t frame);
+void jackSetBpm(double bpm);
JackState jackTransportQuery();
#endif
-} // giada::m::kernelAudio::
-
+} // namespace giada::m::kernelAudio
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <RtMidi.h>
+#include "kernelMidi.h"
#include "const.h"
-#include "utils/log.h"
#include "midiDispatcher.h"
#include "midiMapConf.h"
-#include "kernelMidi.h"
-
+#include "utils/log.h"
+#include <RtMidi.h>
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace kernelMidi
{
namespace
{
-bool status_ = false;
-int api_ = 0;
-RtMidiOut* midiOut_ = nullptr;
-RtMidiIn* midiIn_ = nullptr;
-unsigned numOutPorts_ = 0;
-unsigned numInPorts_ = 0;
-
+bool status_ = false;
+int api_ = 0;
+RtMidiOut* midiOut_ = nullptr;
+RtMidiIn* midiIn_ = nullptr;
+unsigned numOutPorts_ = 0;
+unsigned numInPorts_ = 0;
static void callback_(double /*t*/, std::vector<unsigned char>* msg, void* /*data*/)
{
- if (msg->size() < 3) {
+ if (msg->size() < 3)
+ {
//u::log::print("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
//for (unsigned i=0; i<msg->size(); i++)
// u::log::print("%X", (int) msg->at(i));
midiDispatcher::dispatch(msg->at(0), msg->at(1), msg->at(2));
}
-
/* -------------------------------------------------------------------------- */
-
void sendMidiLightningInitMsgs_()
{
- for (const midimap::Message& m : midimap::midimap.initCommands) {
- if (m.value != 0x0 && m.channel != -1) {
+ for (const midimap::Message& m : midimap::midimap.initCommands)
+ {
+ if (m.value != 0x0 && m.channel != -1)
+ {
u::log::print("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", m.channel, m.value);
MidiEvent e(m.value);
e.setChannel(m.channel);
}
}
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void setApi(int api)
{
api_ = api;
u::log::print("[KM] using system 0x%x\n", api_);
}
-
/* -------------------------------------------------------------------------- */
-
int openOutDevice(int port)
{
- try {
- midiOut_ = new RtMidiOut((RtMidi::Api) api_, "Giada MIDI Output");
+ try
+ {
+ midiOut_ = new RtMidiOut((RtMidi::Api)api_, "Giada MIDI Output");
status_ = true;
}
- catch (RtMidiError &error) {
+ catch (RtMidiError& error)
+ {
u::log::print("[KM] MIDI out device error: %s\n", error.getMessage());
status_ = false;
return 0;
numOutPorts_ = midiOut_->getPortCount();
u::log::print("[KM] %d output MIDI ports found\n", numOutPorts_);
- for (unsigned i=0; i<numOutPorts_; i++)
+ for (unsigned i = 0; i < numOutPorts_; i++)
u::log::print(" %d) %s\n", i, getOutPortName(i));
/* try to open a port, if enabled */
- if (port != -1 && numOutPorts_ > 0) {
- try {
+ if (port != -1 && numOutPorts_ > 0)
+ {
+ try
+ {
midiOut_->openPort(port, getOutPortName(port));
u::log::print("[KM] MIDI out port %d open\n", port);
sendMidiLightningInitMsgs_();
return 1;
}
- catch (RtMidiError& error) {
+ catch (RtMidiError& error)
+ {
u::log::print("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage());
status_ = false;
return 0;
return 2;
}
-
/* -------------------------------------------------------------------------- */
-
int openInDevice(int port)
{
- try {
- midiIn_ = new RtMidiIn((RtMidi::Api) api_, "Giada MIDI input");
+ try
+ {
+ midiIn_ = new RtMidiIn((RtMidi::Api)api_, "Giada MIDI input");
status_ = true;
}
- catch (RtMidiError &error) {
+ catch (RtMidiError& error)
+ {
u::log::print("[KM] MIDI in device error: %s\n", error.getMessage());
status_ = false;
return 0;
numInPorts_ = midiIn_->getPortCount();
u::log::print("[KM] %d input MIDI ports found\n", numInPorts_);
- for (unsigned i=0; i<numInPorts_; i++)
+ for (unsigned i = 0; i < numInPorts_; i++)
u::log::print(" %d) %s\n", i, getInPortName(i));
/* try to open a port, if enabled */
- if (port != -1 && numInPorts_ > 0) {
- try {
+ if (port != -1 && numInPorts_ > 0)
+ {
+ try
+ {
midiIn_->openPort(port, getInPortName(port));
midiIn_->ignoreTypes(true, false, true); // ignore all system/time msgs, for now
u::log::print("[KM] MIDI in port %d open\n", port);
midiIn_->setCallback(&callback_);
return 1;
}
- catch (RtMidiError& error) {
+ catch (RtMidiError& error)
+ {
u::log::print("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage());
status_ = false;
return 0;
return 2;
}
-
/* -------------------------------------------------------------------------- */
-
bool hasAPI(int API)
{
std::vector<RtMidi::Api> APIs;
RtMidi::getCompiledApi(APIs);
- for (unsigned i=0; i<APIs.size(); i++)
+ for (unsigned i = 0; i < APIs.size(); i++)
if (APIs.at(i) == API)
return true;
return false;
}
-
/* -------------------------------------------------------------------------- */
-
std::string getOutPortName(unsigned p)
{
- try { return midiOut_->getPortName(p); }
- catch (RtMidiError& /*error*/) { return ""; }
+ try
+ {
+ return midiOut_->getPortName(p);
+ }
+ catch (RtMidiError& /*error*/)
+ {
+ return "";
+ }
}
std::string getInPortName(unsigned p)
{
- try { return midiIn_->getPortName(p); }
- catch (RtMidiError& /*error*/) { return ""; }
+ try
+ {
+ return midiIn_->getPortName(p);
+ }
+ catch (RtMidiError& /*error*/)
+ {
+ return "";
+ }
}
-
/* -------------------------------------------------------------------------- */
-
void send(uint32_t data)
{
if (!status_)
u::log::print("[KM::send] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
}
-
/* -------------------------------------------------------------------------- */
-
void send(int b1, int b2, int b3)
{
if (!status_)
u::log::print("[KM::send] send msg=(%X %X %X)\n", b1, b2, b3);
}
-
/* -------------------------------------------------------------------------- */
-
void sendMidiLightning(uint32_t learnt, const midimap::Message& m)
{
// Skip lightning message if not defined in midi map
return;
}
- u::log::print("[KM::sendMidiLightning] learnt=0x%X, chan=%d, msg=0x%X, offset=%d\n",
- learnt, m.channel, m.value, m.offset);
+ u::log::print("[KM::sendMidiLightning] learnt=0x%X, chan=%d, msg=0x%X, offset=%d\n",
+ learnt, m.channel, m.value, m.offset);
/* Isolate 'channel' from learnt message and offset it as requested by 'nn' in
the midimap configuration file. */
send(out);
}
-
/* -------------------------------------------------------------------------- */
-
-unsigned countInPorts() { return numInPorts_; }
+unsigned countInPorts() { return numInPorts_; }
unsigned countOutPorts() { return numOutPorts_; }
-bool getStatus() { return status_; }
-
+bool getStatus() { return status_; }
/* -------------------------------------------------------------------------- */
-
int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
-int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
-
+int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
uint32_t getIValue(int b1, int b2, int b3)
{
return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
}
-}}} // giada::m::kernelMidi::
+} // namespace kernelMidi
+} // namespace m
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_KERNELMIDI_H
#define G_KERNELMIDI_H
-
+#include "midiMapConf.h"
#include <cstdint>
#include <string>
-#include "midiMapConf.h"
-
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace kernelMidi
{
int getB1(uint32_t iValue);
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);
+void send(int b1, int b2 = -1, int b3 = -1);
/* sendMidiLightning
Sends a MIDI lightning message defined by 'msg'. */
bool hasAPI(int API);
-}}} // giada::m::kernelMidi::
-
+} // namespace kernelMidi
+} // namespace m
+} // namespace giada
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "metronome.h"
+#include "core/audioBuffer.h"
+
+namespace giada::m
+{
+void Metronome::trigger(Click c, Frame o)
+{
+ m_rendering = true;
+ m_click = c;
+ m_offset = o;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Metronome::render(AudioBuffer& outBuf)
+{
+ const float* data = m_click == Click::BEAT ? beat : bar;
+ for (Frame f = m_offset; f < outBuf.countFrames() && m_rendering; f++)
+ {
+ for (int c = 0; c < outBuf.countChannels(); c++)
+ outBuf[f][c] += data[m_tracker];
+ m_tracker = (m_tracker + 1) % CLICK_SIZE;
+ if (m_tracker == 0)
+ m_rendering = false;
+ }
+ m_offset = 0;
+}
+} // namespace giada::m
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_METRONOME_H
+#define G_METRONOME_H
+
+#include "core/types.h"
+
+namespace giada::m
+{
+class AudioBuffer;
+class Metronome
+{
+public:
+ enum class Click
+ {
+ BEAT,
+ BAR
+ };
+
+ void render(AudioBuffer& outBuf);
+ void trigger(Click c, Frame o);
+
+ bool running = false;
+
+ private:
+ static constexpr Frame CLICK_SIZE = 38;
+
+ static constexpr float beat[CLICK_SIZE] = {
+ 0.059033f, 0.117240f, 0.173807f, 0.227943f, 0.278890f, 0.325936f,
+ 0.368423f, 0.405755f, 0.437413f, 0.462951f, 0.482013f, 0.494333f,
+ 0.499738f, 0.498153f, 0.489598f, 0.474195f, 0.452159f, 0.423798f,
+ 0.389509f, 0.349771f, 0.289883f, 0.230617f, 0.173194f, 0.118739f,
+ 0.068260f, 0.022631f, -0.017423f, -0.051339f, -0.078721f, -0.099345f,
+ -0.113163f, -0.120295f, -0.121028f, -0.115804f, -0.105209f, -0.089954f,
+ -0.070862f, -0.048844f};
+
+ static constexpr float bar[CLICK_SIZE] = {
+ 0.175860f, 0.341914f, 0.488904f, 0.608633f, 0.694426f, 0.741500f,
+ 0.747229f, 0.711293f, 0.635697f, 0.524656f, 0.384362f, 0.222636f,
+ 0.048496f, -0.128348f, -0.298035f, -0.451105f, -0.579021f, -0.674653f,
+ -0.732667f, -0.749830f, -0.688924f, -0.594091f, -0.474481f, -0.340160f,
+ -0.201360f, -0.067752f, 0.052194f, 0.151746f, 0.226280f, 0.273493f,
+ 0.293425f, 0.288307f, 0.262252f, 0.220811f, 0.170435f, 0.117887f,
+ 0.069639f, 0.031320f};
+
+ Frame m_tracker = 0;
+ Frame m_offset = 0;
+ bool m_rendering = false;
+ Click m_click = Click::BEAT;
+};
+} // namespace giada::m
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <vector>
-#include "glue/plugin.h"
-#include "glue/events.h"
-#include "utils/log.h"
-#include "utils/math.h"
-#include "core/model/model.h"
+#include "core/midiDispatcher.h"
#include "core/conf.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
-#include "core/plugins/pluginHost.h"
+#include "core/model/model.h"
#include "core/plugins/plugin.h"
+#include "core/plugins/pluginHost.h"
#include "core/recManager.h"
#include "core/types.h"
-#include "core/midiDispatcher.h"
-
+#include "glue/events.h"
+#include "glue/plugin.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include <cassert>
+#include <vector>
-namespace giada {
-namespace m {
-namespace midiDispatcher
+namespace giada::m::midiDispatcher
{
namespace
{
std::function<void()> signalCb_ = nullptr;
std::function<void(MidiEvent)> learnCb_ = nullptr;
-
/* -------------------------------------------------------------------------- */
-
bool isMasterMidiInAllowed_(int c)
{
- model::MidiInLock l(model::midiIn);
- int filter = model::midiIn.get()->filter;
- bool enabled = model::midiIn.get()->enabled;
+ int filter = model::get().midiIn.filter;
+ bool enabled = model::get().midiIn.enabled;
return enabled && (filter == -1 || filter == c);
}
-
/* -------------------------------------------------------------------------- */
-
bool isChannelMidiInAllowed_(ID channelId, int c)
{
- model::ChannelsLock l(model::channels);
- return model::get(model::channels, channelId).midiLearner.state->isAllowed(c);
+ return model::get().getChannel(channelId).midiLearner.isAllowed(c);
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-void processPlugins_(const std::vector<ID>& ids, const MidiEvent& midiEvent)
+void processPlugins_(const std::vector<Plugin*>& plugins, const MidiEvent& midiEvent)
{
uint32_t pure = midiEvent.getRawNoVelocity();
float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, 1.0f);
parameter indexes match both the structure of Channel::midiInPlugins and the
vector of plugins. */
- m::model::PluginsLock l(m::model::plugins);
- for (ID id : ids) {
- const m::Plugin& p = m::model::get(m::model::plugins, id);
- for (const MidiLearnParam& param : p.midiInParams) {
+ for (Plugin* p : plugins)
+ {
+ for (const MidiLearnParam& param : p->midiInParams)
+ {
if (pure != param.getValue())
continue;
- c::events::setPluginParameter(id, param.getIndex(), vf, /*gui=*/false);
+ c::events::setPluginParameter(p->id, param.getIndex(), vf, /*gui=*/false);
u::log::print(" >>> [pluginId=%d paramIndex=%d] (pure=0x%X, value=%d, float=%f)\n",
- p.id, param.getIndex(), pure, midiEvent.getVelocity(), vf);
+ p->id, param.getIndex(), pure, midiEvent.getVelocity(), vf);
}
}
}
#endif
-
/* -------------------------------------------------------------------------- */
-
void processChannels_(const MidiEvent& midiEvent)
{
uint32_t pure = midiEvent.getRawNoVelocity();
- model::ChannelsLock lock(model::channels);
-
- for (const Channel* c : model::channels) {
+ for (const channel::Data& c : model::get().channels)
+ {
/* Do nothing on this channel if MIDI in is disabled or filtered out for
the current MIDI channel. */
-
- if (!c->midiLearner.state->isAllowed(midiEvent.getChannel()))
+ if (!c.midiLearner.isAllowed(midiEvent.getChannel()))
continue;
- if (pure == c->midiLearner.state->keyPress.getValue()) {
- u::log::print(" >>> keyPress, ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::pressChannel(c->id, midiEvent.getVelocity(), Thread::MIDI);
+ if (pure == c.midiLearner.keyPress.getValue())
+ {
+ u::log::print(" >>> keyPress, ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::pressChannel(c.id, midiEvent.getVelocity(), Thread::MIDI);
+ }
+ else if (pure == c.midiLearner.keyRelease.getValue())
+ {
+ u::log::print(" >>> keyRel ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::releaseChannel(c.id, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->keyRelease.getValue()) {
- u::log::print(" >>> keyRel ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::releaseChannel(c->id, Thread::MIDI);
+ else if (pure == c.midiLearner.mute.getValue())
+ {
+ u::log::print(" >>> mute ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::toggleMuteChannel(c.id, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->mute.getValue()) {
- u::log::print(" >>> mute ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::toggleMuteChannel(c->id, Thread::MIDI);
- }
- else if (pure == c->midiLearner.state->kill.getValue()) {
- u::log::print(" >>> kill ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::killChannel(c->id, Thread::MIDI);
- }
- else if (pure == c->midiLearner.state->arm.getValue()) {
- u::log::print(" >>> arm ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::toggleArmChannel(c->id, Thread::MIDI);
+ else if (pure == c.midiLearner.kill.getValue())
+ {
+ u::log::print(" >>> kill ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::killChannel(c.id, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->solo.getValue()) {
- u::log::print(" >>> solo ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::toggleSoloChannel(c->id, Thread::MIDI);
+ else if (pure == c.midiLearner.arm.getValue())
+ {
+ u::log::print(" >>> arm ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::toggleArmChannel(c.id, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->volume.getValue()) {
- float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
- u::log::print(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
- c->id, pure, midiEvent.getVelocity(), vf);
- c::events::setChannelVolume(c->id, vf, Thread::MIDI);
+ else if (pure == c.midiLearner.solo.getValue())
+ {
+ u::log::print(" >>> solo ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::toggleSoloChannel(c.id, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->pitch.getValue()) {
- float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_PITCH);
+ else if (pure == c.midiLearner.volume.getValue())
+ {
+ float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
+ u::log::print(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ c.id, pure, midiEvent.getVelocity(), vf);
+ c::events::setChannelVolume(c.id, vf, Thread::MIDI);
+ }
+ else if (pure == c.midiLearner.pitch.getValue())
+ {
+ float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_PITCH);
u::log::print(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
- c->id, pure, midiEvent.getVelocity(), vf);
- c::events::setChannelPitch(c->id, vf, Thread::MIDI);
+ c.id, pure, midiEvent.getVelocity(), vf);
+ c::events::setChannelPitch(c.id, vf, Thread::MIDI);
}
- else if (pure == c->midiLearner.state->readActions.getValue()) {
- u::log::print(" >>> toggle read actions ch=%d (pure=0x%X)\n", c->id, pure);
- c::events::toggleReadActionsChannel(c->id, Thread::MIDI);
+ else if (pure == c.midiLearner.readActions.getValue())
+ {
+ u::log::print(" >>> toggle read actions ch=%d (pure=0x%X)\n", c.id, pure);
+ c::events::toggleReadActionsChannel(c.id, Thread::MIDI);
}
#ifdef WITH_VST
/* Process learned plugins parameters. */
- processPlugins_(c->pluginIds, midiEvent);
+ processPlugins_(c.plugins, midiEvent);
#endif
/* Redirect raw MIDI message (pure + velocity) to plug-ins in armed
channels. */
-
- if (c->state->armed.load() == true)
- c::events::sendMidiToChannel(c->id, midiEvent, Thread::MIDI);
+ if (c.armed)
+ c::events::sendMidiToChannel(c.id, midiEvent, Thread::MIDI);
}
}
-
/* -------------------------------------------------------------------------- */
-
void processMaster_(const MidiEvent& midiEvent)
{
- m::model::MidiInLock l(m::model::midiIn);
-
const uint32_t pure = midiEvent.getRawNoVelocity();
- const model::MidiIn* midiIn = model::midiIn.get();
+ const model::MidiIn& midiIn = model::get().midiIn;
- if (pure == midiIn->rewind) {
+ if (pure == midiIn.rewind)
+ {
c::events::rewindSequencer(Thread::MIDI);
u::log::print(" >>> rewind (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->startStop) {
+ else if (pure == midiIn.startStop)
+ {
c::events::toggleSequencer(Thread::MIDI);
u::log::print(" >>> startStop (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->actionRec) {
+ else if (pure == midiIn.actionRec)
+ {
c::events::toggleActionRecording();
u::log::print(" >>> actionRec (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->inputRec) {
+ else if (pure == midiIn.inputRec)
+ {
c::events::toggleInputRecording();
u::log::print(" >>> inputRec (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->metronome) {
+ else if (pure == midiIn.metronome)
+ {
c::events::toggleMetronome();
u::log::print(" >>> metronome (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->volumeIn) {
- float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
+ else if (pure == midiIn.volumeIn)
+ {
+ float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
c::events::setMasterInVolume(vf, Thread::MIDI);
u::log::print(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, midiEvent.getVelocity(), vf);
+ pure, midiEvent.getVelocity(), vf);
}
- else if (pure == midiIn->volumeOut) {
- float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
+ else if (pure == midiIn.volumeOut)
+ {
+ float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
c::events::setMasterOutVolume(vf, Thread::MIDI);
u::log::print(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, midiEvent.getVelocity(), vf);
+ pure, midiEvent.getVelocity(), vf);
}
- else if (pure == midiIn->beatDouble) {
+ else if (pure == midiIn.beatDouble)
+ {
c::events::multiplyBeats();
u::log::print(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
}
- else if (pure == midiIn->beatHalf) {
+ else if (pure == midiIn.beatHalf)
+ {
c::events::divideBeats();
u::log::print(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
}
}
-
/* -------------------------------------------------------------------------- */
-
void learnChannel_(MidiEvent e, int param, ID channelId, std::function<void()> doneCb)
{
if (!isChannelMidiInAllowed_(channelId, e.getChannel()))
- return;
+ return;
uint32_t raw = e.getRawNoVelocity();
- model::onGet(model::channels, channelId, [param, raw](Channel& c)
- {
- switch (param) {
- case G_MIDI_IN_KEYPRESS: c.midiLearner.state->keyPress.setValue(raw); break;
- case G_MIDI_IN_KEYREL: c.midiLearner.state->keyRelease.setValue(raw); break;
- case G_MIDI_IN_KILL: c.midiLearner.state->kill.setValue(raw); break;
- case G_MIDI_IN_ARM: c.midiLearner.state->arm.setValue(raw); break;
- case G_MIDI_IN_MUTE: c.midiLearner.state->mute.setValue(raw); break;
- case G_MIDI_IN_SOLO: c.midiLearner.state->solo.setValue(raw); break;
- case G_MIDI_IN_VOLUME: c.midiLearner.state->volume.setValue(raw); break;
- case G_MIDI_IN_PITCH: c.midiLearner.state->pitch.setValue(raw); break;
- case G_MIDI_IN_READ_ACTIONS: c.midiLearner.state->readActions.setValue(raw); break;
- case G_MIDI_OUT_L_PLAYING: c.midiLighter.state->playing.setValue(raw); break;
- case G_MIDI_OUT_L_MUTE: c.midiLighter.state->mute.setValue(raw); break;
- case G_MIDI_OUT_L_SOLO: c.midiLighter.state->solo.setValue(raw); break;
- }
- });
+ channel::Data& ch = model::get().getChannel(channelId);
+
+ switch (param)
+ {
+ case G_MIDI_IN_KEYPRESS:
+ ch.midiLearner.keyPress.setValue(raw);
+ break;
+ case G_MIDI_IN_KEYREL:
+ ch.midiLearner.keyRelease.setValue(raw);
+ break;
+ case G_MIDI_IN_KILL:
+ ch.midiLearner.kill.setValue(raw);
+ break;
+ case G_MIDI_IN_ARM:
+ ch.midiLearner.arm.setValue(raw);
+ break;
+ case G_MIDI_IN_MUTE:
+ ch.midiLearner.mute.setValue(raw);
+ break;
+ case G_MIDI_IN_SOLO:
+ ch.midiLearner.solo.setValue(raw);
+ break;
+ case G_MIDI_IN_VOLUME:
+ ch.midiLearner.volume.setValue(raw);
+ break;
+ case G_MIDI_IN_PITCH:
+ ch.midiLearner.pitch.setValue(raw);
+ break;
+ case G_MIDI_IN_READ_ACTIONS:
+ ch.midiLearner.readActions.setValue(raw);
+ break;
+ case G_MIDI_OUT_L_PLAYING:
+ ch.midiLighter.playing.setValue(raw);
+ break;
+ case G_MIDI_OUT_L_MUTE:
+ ch.midiLighter.mute.setValue(raw);
+ break;
+ case G_MIDI_OUT_L_SOLO:
+ ch.midiLighter.solo.setValue(raw);
+ break;
+ }
+
+ model::swap(model::SwapType::SOFT);
stopLearn();
doneCb();
}
-
void learnMaster_(MidiEvent e, int param, std::function<void()> doneCb)
{
if (!isMasterMidiInAllowed_(e.getChannel()))
uint32_t raw = e.getRawNoVelocity();
- model::onSwap(model::midiIn, [param, raw](model::MidiIn& m)
+ switch (param)
{
- switch (param) {
- case G_MIDI_IN_REWIND: m.rewind = raw; break;
- case G_MIDI_IN_START_STOP: m.startStop = raw; break;
- case G_MIDI_IN_ACTION_REC: m.actionRec = raw; break;
- case G_MIDI_IN_INPUT_REC: m.inputRec = raw; break;
- case G_MIDI_IN_METRONOME: m.metronome = raw; break;
- case G_MIDI_IN_VOLUME_IN: m.volumeIn = raw; break;
- case G_MIDI_IN_VOLUME_OUT: m.volumeOut = raw; break;
- case G_MIDI_IN_BEAT_DOUBLE: m.beatDouble = raw; break;
- case G_MIDI_IN_BEAT_HALF: m.beatHalf = raw; break;
- }
- });
+ case G_MIDI_IN_REWIND:
+ model::get().midiIn.rewind = raw;
+ break;
+ case G_MIDI_IN_START_STOP:
+ model::get().midiIn.startStop = raw;
+ break;
+ case G_MIDI_IN_ACTION_REC:
+ model::get().midiIn.actionRec = raw;
+ break;
+ case G_MIDI_IN_INPUT_REC:
+ model::get().midiIn.inputRec = raw;
+ break;
+ case G_MIDI_IN_METRONOME:
+ model::get().midiIn.metronome = raw;
+ break;
+ case G_MIDI_IN_VOLUME_IN:
+ model::get().midiIn.volumeIn = raw;
+ break;
+ case G_MIDI_IN_VOLUME_OUT:
+ model::get().midiIn.volumeOut = raw;
+ break;
+ case G_MIDI_IN_BEAT_DOUBLE:
+ model::get().midiIn.beatDouble = raw;
+ break;
+ case G_MIDI_IN_BEAT_HALF:
+ model::get().midiIn.beatHalf = raw;
+ break;
+ }
+
+ model::swap(model::SwapType::SOFT);
stopLearn();
doneCb();
}
-
#ifdef WITH_VST
void learnPlugin_(MidiEvent e, std::size_t paramIndex, ID pluginId, std::function<void()> doneCb)
{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- assert(paramIndex < p.midiInParams.size());
- p.midiInParams[paramIndex].setValue(e.getRawNoVelocity());
- });
+ model::DataLock lock(model::SwapType::NONE);
+
+ Plugin* plugin = model::find<Plugin>(pluginId);
+
+ assert(plugin != nullptr);
+ assert(paramIndex < plugin->midiInParams.size());
+
+ plugin->midiInParams[paramIndex].setValue(e.getRawNoVelocity());
stopLearn();
doneCb();
#endif
-
/* -------------------------------------------------------------------------- */
-
void triggerSignalCb_()
{
- if (signalCb_ == nullptr)
+ if (signalCb_ == nullptr)
return;
signalCb_();
signalCb_ = nullptr;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void startChannelLearn(int param, ID channelId, std::function<void()> f)
{
learnCb_ = [=](m::MidiEvent e) { learnChannel_(e, param, channelId, f); };
}
-
-void startMasterLearn (int param, std::function<void()> f)
+void startMasterLearn(int param, std::function<void()> f)
{
learnCb_ = [=](m::MidiEvent e) { learnMaster_(e, param, f); };
}
-
#ifdef WITH_VST
-void startPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f)
+void startPluginLearn(std::size_t paramIndex, ID pluginId, std::function<void()> f)
{
learnCb_ = [=](m::MidiEvent e) { learnPlugin_(e, paramIndex, pluginId, f); };
}
#endif
-
void stopLearn()
{
learnCb_ = nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
void clearMasterLearn(int param, std::function<void()> f)
{
learnMaster_(MidiEvent(), param, f); // Empty event (0x0)
}
-
void clearChannelLearn(int param, ID channelId, std::function<void()> f)
{
learnChannel_(MidiEvent(), param, channelId, f); // Empty event (0x0)
}
-
#ifdef WITH_VST
-void clearPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f)
+void clearPluginLearn(std::size_t paramIndex, ID pluginId, std::function<void()> f)
{
learnPlugin_(MidiEvent(), paramIndex, pluginId, f); // Empty event (0x0)
}
#endif
-
/* -------------------------------------------------------------------------- */
-
void dispatch(int byte1, int byte2, int byte3)
{
/* Here we want to catch two things: a) note on/note off from a keyboard and
MidiEvent midiEvent(byte1, byte2, byte3);
midiEvent.fixVelocityZero();
- u::log::print("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(),
- midiEvent.getChannel());
+ u::log::print("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(),
+ midiEvent.getChannel());
/* Start dispatcher. If midi learn is on don't parse channels, just learn
incoming MIDI signal. Learn callback wants 'pure' MIDI event, i.e. with
then each channel in the stack. This way incoming signals don't get processed
by glue_* when MIDI learning is on. */
- if (learnCb_ != nullptr) {
- learnCb_(midiEvent);
- }
- else {
- processMaster_(midiEvent);
- processChannels_(midiEvent);
- triggerSignalCb_();
- }
-}
+ std::function<void()> f = [midiEvent]() {
+ if (learnCb_ != nullptr)
+ {
+ learnCb_(midiEvent);
+ }
+ else
+ {
+ processMaster_(midiEvent);
+ processChannels_(midiEvent);
+ triggerSignalCb_();
+ }
+ };
+ eventDispatcher::pumpMidiEvent({eventDispatcher::EventType::FUNCTION, 0, 0, f});
+}
/* -------------------------------------------------------------------------- */
-
void setSignalCallback(std::function<void()> f)
{
signalCb_ = f;
}
-
-}}} // giada::m::midiDispatcher::
-
+} // namespace giada::m::midiDispatcher
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIDI_DISPATCHER_H
#define G_MIDI_DISPATCHER_H
-
-#include <functional>
-#include <cstdint>
-#include "core/model/model.h"
#include "core/midiEvent.h"
+#include "core/model/model.h"
#include "core/types.h"
+#include <cstdint>
+#include <functional>
-
-namespace giada {
-namespace m {
-namespace midiDispatcher
+namespace giada::m::midiDispatcher
{
void startChannelLearn(int param, ID channelId, std::function<void()> f);
-void startMasterLearn (int param, std::function<void()> f);
+void startMasterLearn(int param, std::function<void()> f);
void stopLearn();
-void clearMasterLearn (int param, std::function<void()> f);
+void clearMasterLearn(int param, std::function<void()> f);
void clearChannelLearn(int param, ID channelId, std::function<void()> f);
#ifdef WITH_VST
-void startPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f);
-void clearPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f);
+void startPluginLearn(std::size_t paramIndex, ID pluginId, std::function<void()> f);
+void clearPluginLearn(std::size_t paramIndex, ID pluginId, std::function<void()> f);
#endif
void dispatch(int byte1, int byte2, int byte3);
void setSignalCallback(std::function<void()> f);
-}}} // giada::m::midiDispatcher::
-
+} // namespace giada::m::midiDispatcher
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
+#include "midiEvent.h"
#include "const.h"
#include "utils/math.h"
-#include "midiEvent.h"
-
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace m
{
namespace
{
constexpr int FLOAT_FACTOR = 10000;
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
MidiEvent::MidiEvent(uint32_t raw)
-: m_status ((raw & 0xF0000000) >> 24)
-, m_channel ((raw & 0x0F000000) >> 24)
-, m_note ((raw & 0x00FF0000) >> 16)
+: m_status((raw & 0xF0000000) >> 24)
+, m_channel((raw & 0x0F000000) >> 24)
+, m_note((raw & 0x00FF0000) >> 16)
, m_velocity((raw & 0x0000FF00) >> 8)
-, m_delta (0) // not used
+, m_delta(0) // not used
{
}
-
/* -------------------------------------------------------------------------- */
-
/* static_cast to avoid ambiguity with MidiEvent(float). */
MidiEvent::MidiEvent(int byte1, int byte2, int byte3)
: MidiEvent(static_cast<uint32_t>((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00)))
{
}
-
/* -------------------------------------------------------------------------- */
-
-MidiEvent::MidiEvent(float v)
+MidiEvent::MidiEvent(float v)
: MidiEvent(ENVELOPE, 0, 0)
{
m_velocity = static_cast<int>(v * FLOAT_FACTOR);
}
-
/* -------------------------------------------------------------------------- */
-
void MidiEvent::setDelta(int d)
{
m_delta = d;
}
-
/* -------------------------------------------------------------------------- */
-
void MidiEvent::setChannel(int c)
{
assert(c >= 0 && c < G_MAX_MIDI_CHANS);
m_channel = c;
}
-
void MidiEvent::setVelocity(int v)
{
assert(v >= 0 && v <= G_MAX_VELOCITY);
m_velocity = v;
}
-
/* -------------------------------------------------------------------------- */
-
void MidiEvent::fixVelocityZero()
{
if (m_status == NOTE_ON && m_velocity == 0)
m_status = NOTE_OFF;
}
-
/* -------------------------------------------------------------------------- */
-
int MidiEvent::getStatus() const
{
return m_status;
-}
-
+}
int MidiEvent::getChannel() const
{
return m_channel;
}
-
int MidiEvent::getNote() const
{
return m_note;
-}
-
+}
int MidiEvent::getVelocity() const
{
return m_velocity;
-}
-
+}
float MidiEvent::getVelocityFloat() const
{
return m_velocity / static_cast<float>(FLOAT_FACTOR);
}
-
bool MidiEvent::isNoteOnOff() const
{
return m_status == NOTE_ON || m_status == NOTE_OFF;
}
-
int MidiEvent::getDelta() const
{
return m_delta;
}
-
/* -------------------------------------------------------------------------- */
-
uint32_t MidiEvent::getRaw() const
{
return (m_status << 24) | (m_channel << 24) | (m_note << 16) | (m_velocity << 8) | (0x00);
}
-
uint32_t MidiEvent::getRawNoVelocity() const
{
return (m_status << 24) | (m_channel << 24) | (m_note << 16) | (0x00 << 8) | (0x00);
}
-
-}} // giada::m::
+} // namespace m
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIDI_EVENT_H
#define G_MIDI_EVENT_H
-
#include <cstdint>
-
-namespace giada {
+namespace giada
+{
namespace m
{
class MidiEvent
{
public:
-
static const int NOTE_ON = 0x90;
static const int NOTE_OFF = 0x80;
static const int NOTE_KILL = 0x70;
MidiEvent(float v);
- int getStatus() const;
- int getChannel() const;
- int getNote() const;
- int getVelocity() const;
+ int getStatus() const;
+ int getChannel() const;
+ int getNote() const;
+ int getVelocity() const;
float getVelocityFloat() const;
- bool isNoteOnOff() const;
- int getDelta() const;
+ bool isNoteOnOff() const;
+ int getDelta() const;
/* getRaw(), getRawNoVelocity()
Returns the raw MIDI message. If getRawNoVelocity(), the velocity value is
void fixVelocityZero();
-private:
-
+ private:
int m_status;
int m_channel;
int m_note;
int m_velocity;
int m_delta;
};
-}} // giada::m::
-
+} // namespace m
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#include "midiLearnParam.h"
-
namespace giada::m
{
MidiLearnParam::MidiLearnParam()
-: m_param(0x0)
-, m_index(0)
+: m_param(0)
+, m_index(0)
{
}
-
MidiLearnParam::MidiLearnParam(uint32_t v, std::size_t index)
: m_param(v)
-, m_index(index)
+, m_index(index)
{
}
-
-MidiLearnParam::MidiLearnParam(const MidiLearnParam& o)
-: m_param(o.m_param.load(std::memory_order_relaxed))
-, m_index(o.m_index)
-{
-}
-
-
/* -------------------------------------------------------------------------- */
-
uint32_t MidiLearnParam::getValue() const
{
- return m_param.load(std::memory_order_relaxed);
+ return m_param.load();
}
-
void MidiLearnParam::setValue(uint32_t v)
{
- m_param.store(v, std::memory_order_relaxed);
+ m_param.store(v);
}
-
std::size_t MidiLearnParam::getIndex() const
{
- return m_index;
+ return m_index;
}
-} // giada::m::
\ No newline at end of file
+} // namespace giada::m
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIDI_LEARN_PARAM_H
#define G_MIDI_LEARN_PARAM_H
-
+#include "core/weakAtomic.h"
#include <atomic>
-
namespace giada::m
{
class MidiLearnParam
{
public:
-
MidiLearnParam();
- MidiLearnParam(uint32_t v, std::size_t index=0);
- MidiLearnParam(const MidiLearnParam& o);
-
- uint32_t getValue() const;
- std::size_t getIndex() const;
- void setValue(uint32_t v);
+ MidiLearnParam(uint32_t v, std::size_t index = 0);
+ MidiLearnParam(const MidiLearnParam& o) = default;
-private:
+ uint32_t getValue() const;
+ std::size_t getIndex() const;
+ void setValue(uint32_t v);
- std::atomic<uint32_t> m_param;
- std::size_t m_index;
+ private:
+ WeakAtomic<uint32_t> m_param;
+ std::size_t m_index;
};
-} // giada::m::
-
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <fstream>
-#include <vector>
-#include <string>
-#include <cstring>
-#include <filesystem>
+#include "midiMapConf.h"
+#include "const.h"
#include "deps/json/single_include/nlohmann/json.hpp"
-#include "utils/string.h"
-#include "utils/log.h"
#include "utils/fs.h"
-#include "const.h"
-#include "midiMapConf.h"
-
+#include "utils/log.h"
+#include "utils/string.h"
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <vector>
namespace nl = nlohmann;
-
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace midimap
{
namespace
return true;
}
-
/* -------------------------------------------------------------------------- */
-
bool readCommand_(const nl::json& j, Message& m, const std::string& key)
{
if (j.find(key) == j.end())
return true;
}
-
/* -------------------------------------------------------------------------- */
-
void parse_(Message& message)
{
/* Remove '0x' part from the original string. */
zeros. */
std::string output;
- for (unsigned i=0, p=24; i<input.length(); i++, p-=4) {
- if (input[i] == 'n') {
+ for (unsigned i = 0, p = 24; i < input.length(); i++, p -= 4)
+ {
+ if (input[i] == 'n')
+ {
output += '0';
if (message.offset == -1) // do it once
message.offset = p;
message.value = strtoul(output.c_str(), nullptr, 16);
u::log::print("[parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
- message.channel, message.valueStr, message.value, message.offset);
+ message.channel, message.valueStr, message.value, message.offset);
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
MidiMap midimap;
std::string midimapsPath;
std::vector<std::string> maps;
-
/* -------------------------------------------------------------------------- */
-
void init()
{
midimapsPath = u::fs::getHomePath() + G_SLASH + "midimaps" + G_SLASH;
/* scan dir of midi maps and load the filenames into <>maps. */
u::log::print("[midiMapConf::init] scanning midimaps directory '%s'...\n",
- midimapsPath);
+ midimapsPath);
- if (!std::filesystem::exists(midimapsPath)) {
+ if (!std::filesystem::exists(midimapsPath))
+ {
u::log::print("[midiMapConf::init] unable to scan midimaps directory!\n");
return;
}
- for (const auto& d : std::filesystem::directory_iterator(midimapsPath)) {
+ for (const auto& d : std::filesystem::directory_iterator(midimapsPath))
+ {
// TODO - check if is a valid midimap file (verify headers)
if (!d.is_regular_file())
continue;
u::log::print("[midiMapConf::init] total midimaps found: %d\n", maps.size());
}
-
/* -------------------------------------------------------------------------- */
-
void setDefault()
{
midimap = MidiMap();
}
-
/* -------------------------------------------------------------------------- */
-
bool isDefined(const Message& m)
{
return m.offset != -1;
}
-
/* -------------------------------------------------------------------------- */
-
int read(const std::string& file)
{
- if (file.empty()) {
+ if (file.empty())
+ {
u::log::print("[midiMapConf::read] midimap not specified, nothing to do\n");
return MIDIMAP_NOT_SPECIFIED;
}
midimap.brand = j[MIDIMAP_KEY_BRAND];
midimap.device = j[MIDIMAP_KEY_DEVICE];
-
- if (!readInitCommands_(j)) return MIDIMAP_UNREADABLE;
- if (readCommand_(j, midimap.muteOn, MIDIMAP_KEY_MUTE_ON)) parse_(midimap.muteOn);
- if (readCommand_(j, midimap.muteOff, MIDIMAP_KEY_MUTE_OFF)) parse_(midimap.muteOff);
- if (readCommand_(j, midimap.soloOn, MIDIMAP_KEY_SOLO_ON)) parse_(midimap.soloOn);
- if (readCommand_(j, midimap.soloOff, MIDIMAP_KEY_SOLO_OFF)) parse_(midimap.soloOff);
- if (readCommand_(j, midimap.waiting, MIDIMAP_KEY_WAITING)) parse_(midimap.waiting);
- if (readCommand_(j, midimap.playing, MIDIMAP_KEY_PLAYING)) parse_(midimap.playing);
- if (readCommand_(j, midimap.stopping, MIDIMAP_KEY_STOPPING)) parse_(midimap.stopping);
- if (readCommand_(j, midimap.stopped, MIDIMAP_KEY_STOPPED)) parse_(midimap.stopped);
- if (readCommand_(j, midimap.playingInaudible, MIDIMAP_KEY_PLAYING_INAUDIBLE)) parse_(midimap.playingInaudible);
+
+ if (!readInitCommands_(j))
+ return MIDIMAP_UNREADABLE;
+ if (readCommand_(j, midimap.muteOn, MIDIMAP_KEY_MUTE_ON))
+ parse_(midimap.muteOn);
+ if (readCommand_(j, midimap.muteOff, MIDIMAP_KEY_MUTE_OFF))
+ parse_(midimap.muteOff);
+ if (readCommand_(j, midimap.soloOn, MIDIMAP_KEY_SOLO_ON))
+ parse_(midimap.soloOn);
+ if (readCommand_(j, midimap.soloOff, MIDIMAP_KEY_SOLO_OFF))
+ parse_(midimap.soloOff);
+ if (readCommand_(j, midimap.waiting, MIDIMAP_KEY_WAITING))
+ parse_(midimap.waiting);
+ if (readCommand_(j, midimap.playing, MIDIMAP_KEY_PLAYING))
+ parse_(midimap.playing);
+ if (readCommand_(j, midimap.stopping, MIDIMAP_KEY_STOPPING))
+ parse_(midimap.stopping);
+ if (readCommand_(j, midimap.stopped, MIDIMAP_KEY_STOPPED))
+ parse_(midimap.stopped);
+ if (readCommand_(j, midimap.playingInaudible, MIDIMAP_KEY_PLAYING_INAUDIBLE))
+ parse_(midimap.playingInaudible);
return MIDIMAP_READ_OK;
}
-}}} // giada::m::midimap::
+} // namespace midimap
+} // namespace m
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIDIMAPCONF_H
#define G_MIDIMAPCONF_H
-
-#include <vector>
#include <string>
+#include <vector>
-
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace midimap
{
struct Message
{
- int channel = 0;
- std::string valueStr = "";
+ int channel = 0;
+ std::string valueStr = "";
int offset = -1;
uint32_t value = 0;
};
-
struct MidiMap
{
- std::string brand;
- std::string device;
- std::vector<Message> initCommands;
- Message muteOn;
- Message muteOff;
- Message soloOn;
- Message soloOff;
- Message waiting;
- Message playing;
- Message stopping;
- Message stopped;
- Message playingInaudible;
+ std::string brand;
+ std::string device;
+ std::vector<Message> initCommands;
+ Message muteOn;
+ Message muteOff;
+ Message soloOn;
+ Message soloOff;
+ Message waiting;
+ Message playing;
+ Message stopping;
+ Message stopped;
+ Message playingInaudible;
};
-
/* -------------------------------------------------------------------------- */
/* midimap
extern std::vector<std::string> maps;
-
/* -------------------------------------------------------------------------- */
/* init
Reads a midi map from file 'file'. */
int read(const std::string& file);
-}}} // giada::m::midimap::
+} // namespace midimap
+} // namespace m
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <cstring>
-#include "deps/rtaudio/RtAudio.h"
-#include "utils/log.h"
-#include "utils/math.h"
-#include "core/model/model.h"
-#include "core/wave.h"
-#include "core/kernelAudio.h"
-#include "core/recorder.h"
-#include "core/recManager.h"
-#include "core/plugins/pluginHost.h"
-#include "core/conf.h"
-#include "core/mixerHandler.h"
-#include "core/clock.h"
-#include "core/const.h"
+#include "core/mixer.h"
#include "core/audioBuffer.h"
-#include "core/action.h"
+#include "core/const.h"
+#include "core/model/model.h"
#include "core/sequencer.h"
-#include "core/mixer.h"
-
+#include "utils/log.h"
+#include "utils/math.h"
-namespace giada {
-namespace m {
-namespace mixer
+namespace giada::m::mixer
{
namespace
{
std::function<void()> signalCb_ = nullptr;
-std::atomic<bool> processing_(false);
-std::atomic<bool> active_(false);
+/* endOfRecCb_
+Callback triggered when the end of the internal recording buffer has been
+reached.*/
-/* eventBuffer_
-Buffer of events sent to channels for event parsing. This is filled with Events
-coming from the two event queues.*/
+std::function<void()> endOfRecCb_ = nullptr;
-EventBuffer eventBuffer_;
+/* signalCbFired_
+Boolean guard to determine whether the signal callback has been fired or not.
+Checking if signalCb_ != null (i.e. a callback is still present, so not fired
+yet) is not enough, as the actual firing takes place on a different thread in
+a slightly different moment (see fireSignalCb_() below). */
+bool signalCbFired_ = false;
/* -------------------------------------------------------------------------- */
+/* fireSignalCb_
+Invokes the signal callback. This is done by pumping a FUNCTION event to the
+event dispatcher, rather than invoking the callback directly. This is done on
+purpose: the callback might (and surely will) contain blocking stuff from
+model:: that the realtime thread cannot perform directly. */
-bool isChannelAudible_(const Channel& c)
+void fireSignalCb_()
{
- if (c.getType() == ChannelType::MASTER || c.getType() == ChannelType::PREVIEW)
- return true;
- if (c.state->mute.load() == true)
- return false;
- model::MixerLock ml(model::mixer);
- bool hasSolos = model::mixer.get()->hasSolos;
- return !hasSolos || (hasSolos && c.state->solo.load() == true);
+ eventDispatcher::pumpUIevent({eventDispatcher::EventType::FUNCTION, 0, 0, []() {
+ signalCb_();
+ signalCb_ = nullptr;
+ }});
}
+/* -------------------------------------------------------------------------- */
+
+/* fireEndOfRecCb_
+Same rationale of fireSignalCb_, for the endOfRecCb_ callback. */
+
+void fireEndOfRecCb_()
+{
+ eventDispatcher::pumpUIevent({eventDispatcher::EventType::FUNCTION, 0, 0, []() {
+ endOfRecCb_();
+ endOfRecCb_ = nullptr;
+ }});
+}
/* -------------------------------------------------------------------------- */
/* lineInRec
-Records from line in. */
+Records from line in. 'maxFrames' determines how many frames to record before
+the internal tracker loops over. The value changes whether you are recording
+in RIGID or FREE mode. */
-void lineInRec_(const AudioBuffer& inBuf)
+void lineInRec_(const AudioBuffer& inBuf, Frame maxFrames, float inVol)
{
- if (!recManager::isRecordingInput() || !kernelAudio::isInputEnabled())
+ assert(maxFrames <= recBuffer_.countFrames());
+
+ if (inputTracker_ >= maxFrames && endOfRecCb_ != nullptr)
+ {
+ fireEndOfRecCb_();
return;
-
- float inVol = mh::getInVol();
- int framesInLoop = clock::getFramesInLoop();
+ }
- for (int i = 0; i < inBuf.countFrames(); i++, inputTracker_++)
- for (int j = 0; j < inBuf.countChannels(); j++)
- recBuffer_[inputTracker_ % framesInLoop][j] += inBuf[i][j] * inVol; // adding: overdub!
-}
+ const Frame framesToCopy = -1; // copy everything
+ const Frame srcOffset = 0;
+ const Frame destOffset = inputTracker_ % maxFrames; // loop over at maxFrames
+ recBuffer_.sum(inBuf, framesToCopy, srcOffset, destOffset, inVol);
+
+ inputTracker_ += inBuf.countFrames();
+}
/* -------------------------------------------------------------------------- */
Computes line in peaks and prepares the internal working buffer for input
recording. */
-void processLineIn_(const AudioBuffer& inBuf)
+void processLineIn_(const model::Mixer& mixer, const AudioBuffer& inBuf,
+ float inVol, float recTriggerLevel)
{
- if (!kernelAudio::isInputEnabled())
- return;
+ float peak = inBuf.getPeak();
- peakIn.store(inBuf.getPeak());
-
- if (signalCb_ != nullptr && u::math::linearToDB(peakIn) > conf::conf.recTriggerLevel) {
-G_DEBUG("Signal > threshold!");
- signalCb_();
- signalCb_ = nullptr;
+ if (signalCb_ != nullptr && u::math::linearToDB(peak) > recTriggerLevel && !signalCbFired_)
+ {
+ G_DEBUG("Signal > threshold!");
+ fireSignalCb_();
+ signalCbFired_ = true;
}
+ mixer.state->peakIn.store(peak);
+
/* Prepare the working buffer for input stream, which will be processed
later on by the Master Input Channel with plug-ins. */
-
+
assert(inBuf.countChannels() <= inBuffer_.countChannels());
- model::MixerLock lock(model::mixer);
- inBuffer_.copyData(inBuf, mh::getInVol());
+ inBuffer_.set(inBuf, inVol);
}
-
/* -------------------------------------------------------------------------- */
-
-void fillEventBuffer_()
+void processChannels_(const model::Layout& layout, AudioBuffer& out, AudioBuffer& in)
{
- eventBuffer_.clear();
-
- Event e;
- while (UIevents.pop(e)) eventBuffer_.push_back(e);
- while (MidiEvents.pop(e)) eventBuffer_.push_back(e);
-
-#ifdef G_DEBUG_MODE
- for (const Event& e : eventBuffer_)
- G_DEBUG("Event type=" << (int) e.type << ", channel=" << e.action.channelId
- << ", delta=" << e.delta << ", globalFrame=" << clock::getCurrentFrame());
-#endif
+ for (const channel::Data& c : layout.channels)
+ if (!c.isInternal())
+ channel::render(c, &out, &in, isChannelAudible(c));
}
-
/* -------------------------------------------------------------------------- */
-
-void processChannels_(AudioBuffer& out, AudioBuffer& in)
+void processSequencer_(const model::Layout& layout, AudioBuffer& out, const AudioBuffer& in)
{
- model::ChannelsLock lock(model::channels);
-
- for (const Channel* c : model::channels) {
- bool audible = isChannelAudible_(*c);
- c->parse(eventBuffer_, audible);
- if (c->getType() != ChannelType::MASTER) {
- c->advance(out.countFrames());
- c->render(&out, &in, audible);
- }
- }
-}
+ /* Advance sequencer first, then render it (rendering is just about
+ generating metronome audio). This way the metronome is aligned with
+ everything else. */
+ const sequencer::EventBuffer& events = sequencer::advance(in.countFrames());
+ sequencer::render(out);
-/* -------------------------------------------------------------------------- */
+ /* No channel processing if layout is locked: another thread is changing
+ data (e.g. Plugins or Waves). */
+ if (layout.locked)
+ return;
-void processSequencer_(AudioBuffer& in)
-{
- sequencer::parse(eventBuffer_);
- if (clock::isActive()) {
- if (clock::isRunning())
- sequencer::run(in.countFrames());
- lineInRec_(in);
- }
+ for (const channel::Data& c : layout.channels)
+ if (!c.isInternal())
+ channel::advance(c, events);
}
-
/* -------------------------------------------------------------------------- */
-
-void renderMasterIn_(AudioBuffer& in)
+void renderMasterIn_(const model::Layout& layout, AudioBuffer& in)
{
- model::ChannelsLock lock(model::channels);
- model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).render(nullptr, &in, true);
+ channel::render(layout.getChannel(mixer::MASTER_IN_CHANNEL_ID), nullptr, &in, true);
}
-void renderMasterOut_(AudioBuffer& out)
+void renderMasterOut_(const model::Layout& layout, AudioBuffer& out)
{
- model::ChannelsLock lock(model::channels);
- model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).render(&out, nullptr, true);
+ channel::render(layout.getChannel(mixer::MASTER_OUT_CHANNEL_ID), &out, nullptr, true);
}
-
-/* -------------------------------------------------------------------------- */
-
-/* prepareBuffers
-Cleans up every buffer. */
-
-void prepareBuffers_(AudioBuffer& outBuf)
+void renderPreview_(const model::Layout& layout, AudioBuffer& out)
{
- outBuf.clear();
- inBuffer_.clear();
+ channel::render(layout.getChannel(mixer::PREVIEW_CHANNEL_ID), &out, nullptr, true);
}
-
/* -------------------------------------------------------------------------- */
/* limit_
void limit_(AudioBuffer& outBuf)
{
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++)
+ for (int i = 0; i < outBuf.countFrames(); i++)
+ for (int j = 0; j < outBuf.countChannels(); j++)
outBuf[i][j] = std::max(-1.0f, std::min(outBuf[i][j], 1.0f));
}
-
/* -------------------------------------------------------------------------- */
/* finalizeOutput
Last touches after the output has been rendered: apply inToOut if any, apply
output volume, compute peak. */
-void finalizeOutput_(AudioBuffer& outBuf)
+void finalizeOutput_(const model::Mixer& mixer, AudioBuffer& outBuf,
+ const RenderInfo& info)
{
- bool inToOut = mh::getInToOut();
- float outVol = mh::getOutVol();
-
- if (inToOut)
- outBuf.addData(inBuffer_, outVol);
+ if (info.inToOut)
+ outBuf.sum(inBuffer_, info.outVol);
else
- outBuf.applyGain(outVol);
+ outBuf.applyGain(info.outVol);
- if (conf::conf.limitOutput)
+ if (info.limitOutput)
limit_(outBuf);
-
- peakOut.store(outBuf.getPeak());
-}
-} // {anonymous}
+ mixer.state->peakOut.store(outBuf.getPeak());
+}
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-std::atomic<float> peakOut(0.0);
-std::atomic<float> peakIn(0.0);
-
-Queue<Event, G_MAX_QUEUE_EVENTS> UIevents;
-Queue<Event, G_MAX_QUEUE_EVENTS> MidiEvents;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void init(Frame framesInSeq, Frame framesInBuffer)
+void init(Frame maxFramesInLoop, Frame framesInBuffer)
{
- /* Allocate virtual inputs. recBuffer_ has variable size: it depends
- on how many frames there are in sequencer. */
-
- recBuffer_.alloc(framesInSeq, G_MAX_IO_CHANS);
+ /* Allocate working buffers. recBuffer_ has variable size: it depends on how
+ many frames there are in the current loop. */
+
+ recBuffer_.alloc(maxFramesInLoop, G_MAX_IO_CHANS);
inBuffer_.alloc(framesInBuffer, G_MAX_IO_CHANS);
- u::log::print("[mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n",
- framesInSeq, framesInBuffer);
+ u::log::print("[mixer::init] buffers ready - maxFramesInLoop=%d, framesInBuffer=%d\n",
+ maxFramesInLoop, framesInBuffer);
}
-
/* -------------------------------------------------------------------------- */
-
void enable()
-{
- active_.store(true);
+{
+ model::get().mixer.state->active.store(true);
u::log::print("[mixer::enable] enabled\n");
}
-
-void disable()
-{
- active_.store(false);
- while (processing_.load() == true)
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
+void disable()
+{
+ model::get().mixer.state->active.store(false);
+ while (model::isLocked())
+ ;
u::log::print("[mixer::disable] disabled\n");
}
-
/* -------------------------------------------------------------------------- */
-
void allocRecBuffer(Frame frames)
{
recBuffer_.alloc(frames, G_MAX_IO_CHANS);
}
-
void clearRecBuffer()
{
recBuffer_.clear();
}
-
const AudioBuffer& getRecBuffer()
{
return recBuffer_;
}
-
/* -------------------------------------------------------------------------- */
-
-int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
- double /*streamTime*/, RtAudioStreamStatus /*status*/, void* /*userData*/)
+int render(AudioBuffer& out, const AudioBuffer& in, const RenderInfo& info)
{
- if (!kernelAudio::isReady() || active_.load() == false)
- return 0;
+ const model::Lock rtLock = model::get_RT();
+ const model::Mixer& mixer = rtLock.get().mixer;
- processing_.store(true);
-
-#ifdef WITH_AUDIO_JACK
- if (kernelAudio::getAPI() == G_SYS_API_JACK)
- clock::recvJackSync();
-#endif
-
- AudioBuffer out, in;
- out.setData(static_cast<float*>(outBuf), bufferSize, G_MAX_IO_CHANS);
- if (kernelAudio::isInputEnabled())
- in.setData(static_cast<float*>(inBuf), bufferSize, conf::conf.channelsInCount);
+ inBuffer_.clear();
/* Reset peak computation. */
- peakOut = 0.0;
- peakIn = 0.0;
+ mixer.state->peakOut.store(0.0);
+ mixer.state->peakIn.store(0.0);
- prepareBuffers_(out);
- processLineIn_(in);
+ /* Process line IN if input has been enabled in KernelAudio. */
-//out[0][0] = 3.0f;
-
- renderMasterIn_(inBuffer_);
-
- fillEventBuffer_();
- processSequencer_(inBuffer_);
- processChannels_(out, inBuffer_);
+ if (info.hasInput)
+ {
+ processLineIn_(mixer, in, info.inVol, info.recTriggerLevel);
+ renderMasterIn_(rtLock.get(), inBuffer_);
+ }
- renderMasterOut_(out);
+ /* Record input audio and advance the sequencer only if clock is active:
+ can't record stuff with the sequencer off. */
- /* Advance sequencer only when rendering is done. */
+ if (info.isClockActive)
+ {
+ if (info.canLineInRec)
+ lineInRec_(in, info.maxFramesToRec, info.inVol);
+ if (info.isClockRunning)
+ processSequencer_(rtLock.get(), out, inBuffer_);
+ }
- if (clock::isActive())
- sequencer::advance(out);
+ /* Channel processing. Don't do it if layout is locked: another thread is
+ changing data (e.g. Plugins or Waves). */
- /* Post processing. */
+ if (!rtLock.get().locked)
+ processChannels_(rtLock.get(), out, inBuffer_);
- finalizeOutput_(out);
+ /* Render remaining internal channels. */
- /* Unset data in buffers. If you don't do this, buffers go out of scope and
- destroy memory allocated by RtAudio ---> havoc. */
+ renderMasterOut_(rtLock.get(), out);
+ renderPreview_(rtLock.get(), out);
- out.setData(nullptr, 0, 0);
- in.setData (nullptr, 0, 0);
+ /* Post processing. */
- processing_.store(false);
+ finalizeOutput_(mixer, out, info);
return 0;
}
-
/* -------------------------------------------------------------------------- */
-
-void close()
+void startInputRec(Frame from)
{
- clock::setStatus(ClockStatus::STOPPED);
+ inputTracker_ = from;
+ signalCbFired_ = false;
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void startInputRec()
+Frame stopInputRec()
{
- /* Start inputTracker_ from the current frame, not the beginning. */
- inputTracker_ = clock::getCurrentFrame();
+ Frame ret = inputTracker_;
+ inputTracker_ = 0;
+ signalCbFired_ = false;
+ return ret;
}
+/* -------------------------------------------------------------------------- */
-void stopInputRec()
-{
- inputTracker_ = 0;
-}
-
+void setSignalCallback(std::function<void()> f) { signalCb_ = f; }
+void setEndOfRecCallback(std::function<void()> f) { endOfRecCb_ = f; }
/* -------------------------------------------------------------------------- */
-
-void setSignalCallback(std::function<void()> f)
+bool isChannelAudible(const channel::Data& c)
{
- signalCb_ = f;
+ if (c.isInternal())
+ return true;
+ if (c.mute)
+ return false;
+ bool hasSolos = model::get().mixer.hasSolos;
+ return !hasSolos || (hasSolos && c.solo);
}
-
/* -------------------------------------------------------------------------- */
+float getPeakOut() { return m::model::get().mixer.state->peakOut.load(); }
+float getPeakIn() { return m::model::get().mixer.state->peakIn.load(); }
+
+/* -------------------------------------------------------------------------- */
-void pumpEvent(Event e)
+RecordInfo getRecordInfo()
{
- eventBuffer_.push_back(e);
+ return {inputTracker_, recBuffer_.countFrames()};
}
-}}} // giada::m::mixer::
+} // namespace giada::m::mixer
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIXER_H
#define G_MIXER_H
-
-#include <atomic>
-#include <functional>
-#include <vector>
-#include "deps/rtaudio/RtAudio.h"
-#include "core/ringBuffer.h"
+#include "core/midiEvent.h"
+#include "core/queue.h"
#include "core/recorder.h"
+#include "core/ringBuffer.h"
#include "core/types.h"
-#include "core/queue.h"
-#include "core/midiEvent.h"
-
+#include "deps/rtaudio/RtAudio.h"
+#include <functional>
-namespace giada {
-namespace m
+namespace giada::m
{
struct Action;
-class Channel;
class AudioBuffer;
-
-namespace mixer
+} // namespace giada::m
+namespace giada::m::channel
{
-enum class EventType
+struct Data;
+}
+namespace giada::m::mixer
{
- KEY_PRESS,
- KEY_RELEASE,
- KEY_KILL,
- SEQUENCER_FIRST_BEAT, // 3
- SEQUENCER_BAR, // 4
- SEQUENCER_START, // 5
- SEQUENCER_STOP, // 6
- SEQUENCER_REWIND, // 7
- SEQUENCER_REWIND_REQ, // 8
- MIDI,
- ACTION,
- CHANNEL_TOGGLE_READ_ACTIONS,
- CHANNEL_KILL_READ_ACTIONS,
- CHANNEL_TOGGLE_ARM,
- CHANNEL_MUTE,
- CHANNEL_SOLO,
- CHANNEL_VOLUME,
- CHANNEL_PITCH,
- CHANNEL_PAN
-};
-
-struct Event
-{
- EventType type;
- Frame delta;
- Action action;
-};
-
-/* EventBuffer
-Alias for a RingBuffer containing events to be sent to engine. The double size
-is due to the presence of two distinct Queues for collecting events coming from
-other threads. See below. */
-
-using EventBuffer = RingBuffer<Event, G_MAX_QUEUE_EVENTS * 2>;
-
constexpr int MASTER_OUT_CHANNEL_ID = 1;
constexpr int MASTER_IN_CHANNEL_ID = 2;
constexpr int PREVIEW_CHANNEL_ID = 3;
-extern std::atomic<float> peakOut; // TODO - move to model::
-extern std::atomic<float> peakIn; // TODO - move to model::
+/* RenderInfo
+Struct of parameters passed to Mixer for rendering. */
-/* Channel Event queues
-Collect events coming from the UI or MIDI devices to be sent to channels. Our
-poor's man Queue is a single-producer/single-consumer one, so we need two queues
-for two writers. TODO - let's add a multi-producer queue sooner or later! */
+struct RenderInfo
+{
+ bool isAudioReady;
+ bool hasInput;
+ bool isClockActive;
+ bool isClockRunning;
+ bool canLineInRec;
+ bool limitOutput;
+ bool inToOut;
+ Frame maxFramesToRec;
+ float outVol;
+ float inVol;
+ float recTriggerLevel;
+};
-extern Queue<Event, G_MAX_QUEUE_EVENTS> UIevents;
-extern Queue<Event, G_MAX_QUEUE_EVENTS> MidiEvents;
+/* RecordInfo
+Information regarding the input recording progress. */
-void init(Frame framesInSeq, Frame framesInBuffer);
+struct RecordInfo
+{
+ Frame position;
+ Frame maxLength;
+};
+
+void init(Frame framesInLoop, Frame framesInBuffer);
/* enable, disable
-Toggles master callback processing. Useful when loading a new patch. Mixer
-will flush itself to wait for a processing cycle to finish when disable() is
-called. */
+Toggles master callback processing. Useful to suspend the rendering. */
void enable();
void disable();
/* allocRecBuffer
-Allocates new memory for the virtual input channel. Call this whenever you
-shrink or resize the sequencer. */
+Allocates new memory for the virtual input channel. */
void allocRecBuffer(Frame frames);
Returns a read-only reference to the internal virtual channel. Use this to
merge data into channel after an input recording session. */
-const AudioBuffer& getRecBuffer();
+const AudioBuffer& getRecBuffer();
-void close();
+/* render
+Core rendering function. */
-/* masterPlay
-Core method (callback) */
-
-int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime,
- RtAudioStreamStatus status, void* userData);
+int render(AudioBuffer& out, const AudioBuffer& in, const RenderInfo& info);
/* startInputRec, stopInputRec
-Starts/stops input recording on frame clock::getCurrentFrame(). */
+Starts/stops input recording on frame 'from'. The latter returns the number of
+recorded frames. */
+
+void startInputRec(Frame from);
+Frame stopInputRec();
-void startInputRec();
-void stopInputRec();
+/* setSignalCallback
+Registers the function to be called when the audio signal reaches a certain
+threshold (record-on-signal mode). */
void setSignalCallback(std::function<void()> f);
-/* pumpEvent
-Pumps a new mixer::Event into the event vector. Use this function when you want
-to inject a new event for the **current** block. Push the event in the two
-queues UIevents and MIDIevents above if the event can be processed in the next
-block instead. */
+/* setEndOfRecCallback
+Registers the function to be called when the end of the internal recording
+buffer has been reached. */
+
+void setEndOfRecCallback(std::function<void()> f);
+
+/* isChannelAudible
+True if the channel 'c' is currently audible: not muted or not included in a
+solo session. */
+
+bool isChannelAudible(const channel::Data& c);
-void pumpEvent(Event e);
-}}} // giada::m::mixer::;
+float getPeakOut();
+float getPeakIn();
+RecordInfo getRecordInfo();
+} // namespace giada::m::mixer
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <vector>
-#include <algorithm>
-#include "utils/fs.h"
-#include "utils/string.h"
-#include "utils/log.h"
-#include "utils/vector.h"
-#include "glue/main.h"
-#include "glue/channel.h"
-#include "core/model/model.h"
+#include "core/mixerHandler.h"
#include "core/channels/channelManager.h"
-#include "core/kernelMidi.h"
-#include "core/mixer.h"
+#include "core/clock.h"
+#include "core/conf.h"
#include "core/const.h"
#include "core/init.h"
+#include "core/kernelAudio.h"
+#include "core/kernelMidi.h"
+#include "core/midiMapConf.h"
+#include "core/mixer.h"
+#include "core/model/model.h"
+#include "core/patch.h"
+#include "core/plugins/plugin.h"
#include "core/plugins/pluginHost.h"
#include "core/plugins/pluginManager.h"
-#include "core/plugins/plugin.h"
-#include "core/waveFx.h"
-#include "core/conf.h"
-#include "core/patch.h"
+#include "core/recManager.h"
#include "core/recorder.h"
#include "core/recorderHandler.h"
-#include "core/recManager.h"
-#include "core/clock.h"
-#include "core/kernelAudio.h"
-#include "core/midiMapConf.h"
#include "core/wave.h"
+#include "core/waveFx.h"
#include "core/waveManager.h"
-#include "core/mixerHandler.h"
-
+#include "glue/channel.h"
+#include "glue/main.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include "utils/vector.h"
+#include <algorithm>
+#include <cassert>
+#include <vector>
-namespace giada {
-namespace m {
-namespace mh
+namespace giada::m::mh
{
namespace
{
-std::unique_ptr<Channel> createChannel_(ChannelType type, ID columnId, ID channelId=0)
+channel::Data& addChannel_(ChannelType type, ID columnId)
{
- std::unique_ptr<Channel> ch = channelManager::create(type, columnId, conf::conf);
+ model::get().channels.push_back(channelManager::create(/*id=*/0, type, columnId));
+ model::swap(model::SwapType::HARD);
- if (type == ChannelType::MASTER) {
- assert(channelId != 0);
- ch->id = channelId;
- }
-
- return ch;
+ return model::get().channels.back();
}
-
/* -------------------------------------------------------------------------- */
-
waveManager::Result createWave_(const std::string& fname)
{
- return waveManager::createFromFile(fname, /*ID=*/0, conf::conf.samplerate,
- conf::conf.rsmpQuality);
+ return waveManager::createFromFile(fname, /*id=*/0, conf::conf.samplerate,
+ conf::conf.rsmpQuality);
}
-
/* -------------------------------------------------------------------------- */
-
-bool anyChannel_(std::function<bool(const Channel*)> f)
+bool anyChannel_(std::function<bool(const channel::Data&)> f)
{
- model::ChannelsLock lock(model::channels);
- return std::any_of(model::channels.begin(), model::channels.end(), f);
+ return std::any_of(model::get().channels.begin(), model::get().channels.end(), f);
}
-
/* -------------------------------------------------------------------------- */
-
template <typename F>
-std::vector<ID> getChannelsIf_(F f)
-{
- model::ChannelsLock l(model::channels);
-
- std::vector<ID> ids;
- for (const Channel* c : model::channels)
- if (f(c)) ids.push_back(c->id);
-
- return ids;
-}
-
-
-std::vector<ID> getChannelsWithWave_()
-{
- return getChannelsIf_([] (const Channel* c)
- {
- return c->samplePlayer && c->samplePlayer->hasWave();
- });
-}
-
-
-std::vector<ID> getRecordableChannels_()
+std::vector<channel::Data*> getChannelsIf_(F f)
{
- return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && !c->hasWave(); });
+ std::vector<channel::Data*> out;
+ for (channel::Data& ch : model::get().channels)
+ if (f(ch))
+ out.push_back(&ch);
+ return out;
}
-
-std::vector<ID> getOverdubbableChannels_()
+std::vector<channel::Data*> getRecordableChannels_()
{
- return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && c->hasWave(); });
+ return getChannelsIf_([](const channel::Data& c) { return c.canInputRec() && !c.hasWave(); });
}
-
-/* -------------------------------------------------------------------------- */
-
-/* pushWave_
-Pushes a new wave into Channel 'ch' and into the corresponding Wave list.
-Use this when modifying a local model, before swapping it. */
-
-void pushWave_(Channel& ch, std::unique_ptr<Wave>&& w)
+std::vector<channel::Data*> getOverdubbableChannels_()
{
- assert(ch.getType() == ChannelType::SAMPLE);
-
- model::waves.push(std::move(w));
-
- model::WavesLock l(model::waves);
- ch.samplePlayer->loadWave(model::waves.back());
+ return getChannelsIf_([](const channel::Data& c) { return c.canInputRec() && c.hasWave(); });
}
-
/* -------------------------------------------------------------------------- */
-
-void setupChannelPostRecording_(Channel& c)
+void setupChannelPostRecording_(channel::Data& ch)
{
/* Start sample channels in loop mode right away. */
- if (c.samplePlayer->state->isAnyLoopMode())
- c.samplePlayer->kickIn(clock::getCurrentFrame());
+ if (ch.samplePlayer->isAnyLoopMode())
+ samplePlayer::kickIn(ch, clock::getCurrentFrame());
/* Disable 'arm' button if overdub protection is on. */
- if (c.audioReceiver->state->overdubProtection.load() == true)
- c.state->armed.store(false);
+ if (ch.audioReceiver->overdubProtection == true)
+ ch.armed = false;
}
-
/* -------------------------------------------------------------------------- */
-
/* recordChannel_
Records the current Mixer audio input data into an empty channel. */
-void recordChannel_(ID channelId)
+void recordChannel_(channel::Data& ch, Frame recordedFrames)
{
- /* Create a new Wave with audio coming from Mixer's virtual input. */
+ /* Create a new Wave with audio coming from Mixer's input buffer. */
- std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav";
+ std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav";
+ std::unique_ptr<Wave> wave = waveManager::createEmpty(recordedFrames, G_MAX_IO_CHANS,
+ conf::conf.samplerate, filename);
- std::unique_ptr<Wave> wave = waveManager::createEmpty(clock::getFramesInLoop(),
- G_MAX_IO_CHANS, conf::conf.samplerate, filename);
+ G_DEBUG("Created new Wave, size=" << wave->getBuffer().countFrames());
- wave->copyData(mixer::getRecBuffer());
+ /* Copy up to wave.getSize() from the mixer's input buffer into wave's. */
- /* Update Channel with the new Wave. The function pushWave_ will take
- care of pushing it into the Wave stack first. */
+ wave->getBuffer().set(mixer::getRecBuffer(), wave->getBuffer().countFrames());
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- pushWave_(c, std::move(wave));
- setupChannelPostRecording_(c);
- });
-}
+ /* Update channel with the new Wave. */
+ model::add(std::move(wave));
+ samplePlayer::loadWave(ch, &model::back<Wave>());
+ setupChannelPostRecording_(ch);
-/* -------------------------------------------------------------------------- */
+ model::swap(model::SwapType::HARD);
+}
+/* -------------------------------------------------------------------------- */
/* overdubChannel_
Records the current Mixer audio input data into a channel with an existing
Wave, overdub mode. */
-void overdubChannel_(ID channelId)
+void overdubChannel_(channel::Data& ch)
{
- ID waveId;
- model::onGet(model::channels, channelId, [&](Channel& c)
- {
- waveId = c.samplePlayer->getWaveId();
- });
+ Wave* wave = ch.samplePlayer->getWave();
- model::onGet(m::model::waves, waveId, [&](Wave& w)
- {
- w.addData(mixer::getRecBuffer());
- w.setLogical(true);
- });
+ /* Need model::DataLock here, as data might be being read by the audio
+ thread at the same time. */
- model::onGet(model::channels, channelId, [&](Channel& c)
- {
- setupChannelPostRecording_(c);
- });
-}
-} // {anonymous}
+ model::DataLock lock;
+ wave->getBuffer().sum(mixer::getRecBuffer(), /*gain=*/1.0f);
+ wave->setLogical(true);
+ setupChannelPostRecording_(ch);
+}
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init()
{
- mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
-
- model::channels.push(createChannel_(ChannelType::MASTER, /*column=*/0,
- mixer::MASTER_OUT_CHANNEL_ID));
- model::channels.push(createChannel_(ChannelType::MASTER, /*column=*/0,
- mixer::MASTER_IN_CHANNEL_ID));
- model::channels.push(createChannel_(ChannelType::PREVIEW, /*column=*/0,
- mixer::PREVIEW_CHANNEL_ID));
-}
+ mixer::init(clock::getMaxFramesInLoop(), kernelAudio::getRealBufSize());
+ model::get().channels.clear();
-/* -------------------------------------------------------------------------- */
+ model::get().channels.push_back(channelManager::create(
+ mixer::MASTER_OUT_CHANNEL_ID, ChannelType::MASTER, /*columnId=*/0));
+ model::get().channels.push_back(channelManager::create(
+ mixer::MASTER_IN_CHANNEL_ID, ChannelType::MASTER, /*columnId=*/0));
+ model::get().channels.push_back(channelManager::create(
+ mixer::PREVIEW_CHANNEL_ID, ChannelType::PREVIEW, /*columnId=*/0));
+ model::swap(model::SwapType::NONE);
+}
+
+/* -------------------------------------------------------------------------- */
void close()
{
mixer::disable();
- model::channels.clear();
- model::waves.clear();
- mixer::close();
}
-
/* -------------------------------------------------------------------------- */
-
void addChannel(ChannelType type, ID columnId)
{
- model::channels.push(createChannel_(type, columnId));
+ addChannel_(type, columnId);
}
-
/* -------------------------------------------------------------------------- */
-
int loadChannel(ID channelId, const std::string& fname)
{
- waveManager::Result res = createWave_(fname);
+ waveManager::Result res = createWave_(fname);
- if (res.status != G_RES_OK)
+ if (res.status != G_RES_OK)
return res.status;
- ID oldWaveId;
+ model::add(std::move(res.wave));
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- oldWaveId = c.samplePlayer->getWaveId();
- pushWave_(c, std::move(res.wave));
- });
+ Wave& wave = model::back<Wave>();
+ Wave* old = model::get().getChannel(channelId).samplePlayer->getWave();
- /* Remove old wave, if any. It is safe to do it now: the channel already
- points to the new one. */
+ samplePlayer::loadWave(model::get().getChannel(channelId), &wave);
+ model::swap(model::SwapType::HARD);
- if (oldWaveId != 0)
- model::waves.pop(model::getIndex(model::waves, oldWaveId));
+ /* Remove old wave, if any. It is safe to do it now: the audio thread is
+ already processing the new layout. */
+
+ if (old != nullptr)
+ model::remove<Wave>(*old);
+
+ recManager::refreshInputRecMode();
return res.status;
}
-
/* -------------------------------------------------------------------------- */
-
int addAndLoadChannel(ID columnId, const std::string& fname)
{
waveManager::Result res = createWave_(fname);
return res.status;
}
-
void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w)
{
- std::unique_ptr<Channel> ch = createChannel_(ChannelType::SAMPLE, columnId);
+ model::add(std::move(w));
- pushWave_(*ch.get(), std::move(w));
+ Wave& wave = model::back<Wave>();
+ channel::Data& channel = addChannel_(ChannelType::SAMPLE, columnId);
- /* Then add new channel to Channel list. */
+ samplePlayer::loadWave(channel, &wave);
+ model::swap(model::SwapType::HARD);
- model::channels.push(std::move(ch));
+ recManager::refreshInputRecMode();
}
-
/* -------------------------------------------------------------------------- */
-
void cloneChannel(ID channelId)
{
- model::ChannelsLock cl(model::channels);
- model::WavesLock wl(model::waves);
-
- const Channel& oldChannel = model::get(model::channels, channelId);
- std::unique_ptr<Channel> newChannel = channelManager::create(oldChannel);
+ channel::Data& oldChannel = model::get().getChannel(channelId);
+ channel::Data newChannel = channelManager::create(oldChannel);
/* Clone plugins, actions and wave first in their own lists. */
-
+
#ifdef WITH_VST
- newChannel->pluginIds = pluginHost::clonePlugins(oldChannel.pluginIds);
+ newChannel.plugins = pluginHost::clonePlugins(oldChannel.plugins);
#endif
- recorderHandler::cloneActions(channelId, newChannel->id);
-
- if (newChannel->samplePlayer && newChannel->samplePlayer->hasWave())
+ recorderHandler::cloneActions(channelId, newChannel.id);
+
+ if (newChannel.samplePlayer && newChannel.samplePlayer->hasWave())
{
- Wave& wave = model::get(model::waves, newChannel->samplePlayer->getWaveId());
- pushWave_(*newChannel, waveManager::createFromWave(wave, 0, wave.getSize()));
+ Wave* wave = newChannel.samplePlayer->getWave();
+ model::add(waveManager::createFromWave(*wave, 0, wave->getBuffer().countFrames()));
}
- /* Then push the new channel in the channels list. */
+ /* Then push the new channel in the channels vector. */
- model::channels.push(std::move(newChannel));
+ model::get().channels.push_back(newChannel);
+ model::swap(model::SwapType::HARD);
}
-
/* -------------------------------------------------------------------------- */
-
void freeChannel(ID channelId)
{
- ID waveId;
-
- /* Remove Wave reference from Channel. */
-
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- waveId = c.samplePlayer->getWaveId();
- c.samplePlayer->loadWave(nullptr);
- });
+ channel::Data& ch = model::get().getChannel(channelId);
- /* Then remove the actual Wave, if any. */
-
- if (waveId != 0)
- model::waves.pop(model::getIndex(model::waves, waveId));
-}
+ assert(ch.samplePlayer);
+ const Wave* wave = ch.samplePlayer->getWave();
-/* -------------------------------------------------------------------------- */
+ samplePlayer::loadWave(ch, nullptr);
+ model::swap(model::SwapType::HARD);
+
+ if (wave != nullptr)
+ model::remove<Wave>(*wave);
+
+ recManager::refreshInputRecMode();
+}
+/* -------------------------------------------------------------------------- */
void freeAllChannels()
{
- for (ID id : getChannelsWithWave_()) {
- model::onSwap(model::channels, id, [](Channel& c)
- {
- c.samplePlayer->loadWave(nullptr);
- });
- }
+ for (channel::Data& ch : model::get().channels)
+ if (ch.samplePlayer)
+ samplePlayer::loadWave(ch, nullptr);
- model::waves.clear();
-}
+ model::swap(model::SwapType::HARD);
+ model::clear<model::WavePtrs>();
+ recManager::refreshInputRecMode();
+}
/* -------------------------------------------------------------------------- */
-
void deleteChannel(ID channelId)
{
- ID waveId;
+ const channel::Data& ch = model::get().getChannel(channelId);
+ const Wave* wave = ch.samplePlayer ? ch.samplePlayer->getWave() : nullptr;
#ifdef WITH_VST
- std::vector<ID> pluginIds;
+ const std::vector<Plugin*> plugins = ch.plugins;
#endif
- model::onGet(model::channels, channelId, [&](const Channel& c)
- {
-#ifdef WITH_VST
- pluginIds = c.pluginIds;
-#endif
- waveId = c.samplePlayer ? c.samplePlayer->getWaveId() : 0;
+ u::vector::removeIf(model::get().channels, [channelId](const channel::Data& c) {
+ return c.id == channelId;
});
-
- model::channels.pop(model::getIndex(model::channels, channelId));
+ model::swap(model::SwapType::HARD);
- if (waveId != 0)
- model::waves.pop(model::getIndex(model::waves, waveId));
+ if (wave != nullptr)
+ model::remove<Wave>(*wave);
#ifdef WITH_VST
- pluginHost::freePlugins(pluginIds);
+ pluginHost::freePlugins(plugins);
#endif
-}
+ recManager::refreshInputRecMode();
+}
/* -------------------------------------------------------------------------- */
-
void renameChannel(ID channelId, const std::string& name)
{
- model::onGet(model::channels, channelId, [&](Channel& c)
- {
- c.state->name = name;
- }, /*rebuild=*/true);
+ model::get().getChannel(channelId).name = name;
+ model::swap(model::SwapType::HARD);
}
-
/* -------------------------------------------------------------------------- */
-
void updateSoloCount()
{
- model::onSwap(model::mixer, [](model::Mixer& m)
- {
- m.hasSolos = anyChannel_([](const Channel* ch) {
- return !ch->isInternal() && ch->state->solo.load() == true;
- });
+ bool hasSolos = anyChannel_([](const channel::Data& ch) {
+ return !ch.isInternal() && ch.solo;
});
-}
+ model::get().mixer.hasSolos = hasSolos;
+ model::swap(model::SwapType::NONE);
+}
/* -------------------------------------------------------------------------- */
-
void setInToOut(bool v)
{
- model::onSwap(model::mixer, [&](model::Mixer& m)
- {
- m.inToOut = v;
- });
+ model::get().mixer.inToOut = v;
+ model::swap(model::SwapType::NONE);
}
-
/* -------------------------------------------------------------------------- */
-
float getInVol()
{
- model::ChannelsLock l(model::channels);
- return model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).state->volume.load();
+ return model::get().getChannel(mixer::MASTER_IN_CHANNEL_ID).volume;
}
-
float getOutVol()
{
- model::ChannelsLock l(model::channels);
- return model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).state->volume.load();
+ return model::get().getChannel(mixer::MASTER_OUT_CHANNEL_ID).volume;
}
-
bool getInToOut()
{
- model::MixerLock lock(model::mixer); return model::mixer.get()->inToOut;
+ return model::get().mixer.inToOut;
}
-
/* -------------------------------------------------------------------------- */
-/* Push a new Wave into each recordable channel. Warning: this algorithm will
-require some changes when we will allow overdubbing (the previous existing Wave
-has to be overwritten somehow). */
-
-void finalizeInputRec()
+void finalizeInputRec(Frame recordedFrames)
{
- for (ID id : getRecordableChannels_())
- recordChannel_(id);
- for (ID id : getOverdubbableChannels_())
- overdubChannel_(id);
+ for (channel::Data* ch : getRecordableChannels_())
+ recordChannel_(*ch, recordedFrames);
+ for (channel::Data* ch : getOverdubbableChannels_())
+ overdubChannel_(*ch);
mixer::clearRecBuffer();
}
-
/* -------------------------------------------------------------------------- */
-
bool hasInputRecordableChannels()
{
- return anyChannel_([](const Channel* ch) { return ch->canInputRec(); });
+ return anyChannel_([](const channel::Data& ch) { return ch.canInputRec(); });
}
-
bool hasActionRecordableChannels()
{
- return anyChannel_([](const Channel* ch) { return ch->canActionRec(); });
+ return anyChannel_([](const channel::Data& ch) { return ch.canActionRec(); });
}
-
bool hasLogicalSamples()
{
- return anyChannel_([](const Channel* ch)
- {
- return ch->samplePlayer && ch->samplePlayer->hasLogicalWave(); }
- );
+ return anyChannel_([](const channel::Data& ch) { return ch.samplePlayer && ch.samplePlayer->hasLogicalWave(); });
}
-
bool hasEditedSamples()
{
- return anyChannel_([](const Channel* ch)
- {
- return ch->samplePlayer && ch->samplePlayer->hasEditedWave();
+ return anyChannel_([](const channel::Data& ch) {
+ return ch.samplePlayer && ch.samplePlayer->hasEditedWave();
});
}
-
bool hasActions()
{
- return anyChannel_([](const Channel* ch) { return ch->state->hasActions; });
+ return anyChannel_([](const channel::Data& ch) { return ch.hasActions; });
}
-
bool hasAudioData()
{
- return anyChannel_([](const Channel* ch)
- {
- return ch->samplePlayer && ch->samplePlayer->hasWave();
+ return anyChannel_([](const channel::Data& ch) {
+ return ch.samplePlayer && ch.samplePlayer->hasWave();
});
}
-}}} // giada::m::mh::
+} // namespace giada::m::mh
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MIXER_HANDLER_H
#define G_MIXER_HANDLER_H
-
+#include "types.h"
#include <memory>
#include <string>
-#include "types.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave;
class Channel;
class SampleChannel;
-
-namespace mh
+} // namespace giada::m
+namespace giada::m::mh
{
/* init
Initializes mixer. */
/* addAndLoadChannel (1)
Creates a new channels, fills it with a Wave and then add it to the stack. */
-int addAndLoadChannel(ID columnId, const std::string& fname);
+int addAndLoadChannel(ID columnId, const std::string& fname);
/* addAndLoadChannel (2)
Same as (1), but Wave is already provided. */
-void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w);
+void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w);
/* freeChannel
Unloads existing Wave from a Sample Channel. */
Fills armed Sample Channels with audio data coming from an input recording
session. */
-void finalizeInputRec();
+void finalizeInputRec(Frame recordedFrames);
/* hasLogicalSamples
True if 1 or more samples are logical (memory only, such as takes) */
float getInVol();
float getOutVol();
-bool getInToOut();
-}}} // giada::m::mh::
-
+bool getInToOut();
+} // namespace giada::m::mh
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
#include "core/model/model.h"
+#include <cassert>
#ifdef G_DEBUG_MODE
#include "core/channels/channelManager.h"
#endif
+namespace giada::m::model
+{
+namespace
+{
+struct State
+{
+ Clock::State clock;
+ Mixer::State mixer;
+ std::vector<std::unique_ptr<channel::State>> channels;
+};
-namespace giada {
-namespace m {
-namespace model
+struct Data
{
-RCUList<Clock> clock(std::make_unique<Clock>());
-RCUList<Mixer> mixer(std::make_unique<Mixer>());
-RCUList<Kernel> kernel(std::make_unique<Kernel>());
-RCUList<Recorder> recorder(std::make_unique<Recorder>());
-RCUList<MidiIn> midiIn(std::make_unique<MidiIn>());
-RCUList<Actions> actions(std::make_unique<Actions>());
-RCUList<Channel> channels;
-RCUList<Wave> waves;
+ std::vector<std::unique_ptr<channel::Buffer>> channels;
+ std::vector<std::unique_ptr<Wave>> waves;
+ recorder::ActionMap actions;
#ifdef WITH_VST
-RCUList<Plugin> plugins;
+ std::vector<std::unique_ptr<Plugin>> plugins;
#endif
+};
+/* -------------------------------------------------------------------------- */
-Actions::Actions(const Actions& o) : map(o.map)
+template <typename T>
+auto getIter_(const std::vector<std::unique_ptr<T>>& source, ID id)
{
- /* Needs to update all pointers of prev and next actions with addresses
- coming from the new 'actions' map. */
+ return u::vector::findIf(source, [id](const std::unique_ptr<T>& p) { return p->id == id; });
+}
+
+/* -------------------------------------------------------------------------- */
- recorder::updateMapPointers(map);
+template <typename S>
+auto* get_(S& source, ID id)
+{
+ auto it = getIter_(source, id);
+ return it == source.end() ? nullptr : it->get();
}
+/* -------------------------------------------------------------------------- */
-#ifdef G_DEBUG_MODE
+template <typename D, typename T>
+void remove_(D& dest, T& ref)
+{
+ u::vector::removeIf(dest, [&ref](const auto& other) { return other.get() == &ref; });
+}
+} // namespace
-void debug()
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+std::function<void(SwapType)> onSwap_ = nullptr;
+
+Swapper<Layout> layout;
+State state;
+Data data;
+
+/* -------------------------------------------------------------------------- */
+
+DataLock::DataLock(SwapType t)
+: m_swapType(t)
+{
+ get().locked = true;
+ swap(SwapType::NONE);
+}
+
+DataLock::~DataLock()
+{
+ get().locked = false;
+ swap(m_swapType);
+}
+
+/* -------------------------------------------------------------------------- */
+
+channel::Data& Layout::getChannel(ID id)
+{
+ return const_cast<channel::Data&>(const_cast<const Layout*>(this)->getChannel(id));
+}
+
+const channel::Data& Layout::getChannel(ID id) const
+{
+ auto it = std::find_if(channels.begin(), channels.end(), [id](const channel::Data& c) {
+ return c.id == id;
+ });
+ assert(it != channels.end());
+ return *it;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init()
+{
+ get().clock.state = &state.clock;
+ get().mixer.state = &state.mixer;
+ swap(SwapType::NONE);
+}
+
+/* -------------------------------------------------------------------------- */
+
+Layout& get()
+{
+ return layout.get();
+}
+
+Lock get_RT()
+{
+ return Lock(layout);
+}
+
+void swap(SwapType t)
+{
+ layout.swap();
+ if (onSwap_)
+ onSwap_(t);
+}
+
+void onSwap(std::function<void(SwapType)> f)
+{
+ onSwap_ = f;
+}
+
+/* -------------------------------------------------------------------------- */
+
+bool isLocked()
+{
+ return layout.isLocked();
+}
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+T& getAll()
{
- ChannelsLock chl(channels);
- ClockLock cl(clock);
- WavesLock wl(waves);
- ActionsLock al(actions);
#ifdef WITH_VST
- PluginsLock pl(plugins);
+ if constexpr (std::is_same_v<T, PluginPtrs>)
+ return data.plugins;
#endif
+ if constexpr (std::is_same_v<T, WavePtrs>)
+ return data.waves;
+ if constexpr (std::is_same_v<T, Actions>)
+ return data.actions;
+ if constexpr (std::is_same_v<T, ChannelBufferPtrs>)
+ return data.channels;
+ if constexpr (std::is_same_v<T, ChannelStatePtrs>)
+ return state.channels;
+ assert(false);
+}
+
+#ifdef WITH_VST
+template PluginPtrs& getAll<PluginPtrs>();
+#endif
+template WavePtrs& getAll<WavePtrs>();
+template Actions& getAll<Actions>();
+template ChannelBufferPtrs& getAll<ChannelBufferPtrs>();
+template ChannelStatePtrs& getAll<ChannelStatePtrs>();
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+T* find(ID id)
+{
+#ifdef WITH_VST
+ if constexpr (std::is_same_v<T, Plugin>)
+ return get_(data.plugins, id);
+#endif
+ if constexpr (std::is_same_v<T, Wave>)
+ return get_(data.waves, id);
+
+ assert(false);
+}
+
+#ifdef WITH_VST
+template Plugin* find<Plugin>(ID id);
+#endif
+template Wave* find<Wave>(ID id);
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+void add(T obj)
+{
+#ifdef WITH_VST
+ if constexpr (std::is_same_v<T, PluginPtr>)
+ data.plugins.push_back(std::move(obj));
+#endif
+ if constexpr (std::is_same_v<T, WavePtr>)
+ data.waves.push_back(std::move(obj));
+ if constexpr (std::is_same_v<T, ChannelBufferPtr>)
+ data.channels.push_back(std::move(obj));
+ if constexpr (std::is_same_v<T, ChannelStatePtr>)
+ state.channels.push_back(std::move(obj));
+}
+
+#ifdef WITH_VST
+template void add<PluginPtr>(PluginPtr p);
+#endif
+template void add<WavePtr>(WavePtr p);
+template void add<ChannelBufferPtr>(ChannelBufferPtr p);
+template void add<ChannelStatePtr>(ChannelStatePtr p);
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+void remove(const T& ref)
+{
+#ifdef WITH_VST
+ if constexpr (std::is_same_v<T, Plugin>)
+ remove_(data.plugins, ref);
+#endif
+ if constexpr (std::is_same_v<T, Wave>)
+ remove_(data.waves, ref);
+}
+
+#ifdef WITH_VST
+template void remove<Plugin>(const Plugin& t);
+#endif
+template void remove<Wave>(const Wave& t);
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+T& back()
+{
+#ifdef WITH_VST
+ if constexpr (std::is_same_v<T, Plugin>)
+ return *data.plugins.back().get();
+#endif
+ if constexpr (std::is_same_v<T, Wave>)
+ return *data.waves.back().get();
+ if constexpr (std::is_same_v<T, channel::State>)
+ return *state.channels.back().get();
+ if constexpr (std::is_same_v<T, channel::Buffer>)
+ return *data.channels.back().get();
+}
+
+#ifdef WITH_VST
+template Plugin& back<Plugin>();
+#endif
+template Wave& back<Wave>();
+template channel::State& back<channel::State>();
+template channel::Buffer& back<channel::Buffer>();
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T>
+void clear()
+{
+#ifdef WITH_VST
+ if constexpr (std::is_same_v<T, PluginPtrs>)
+ data.plugins.clear();
+#endif
+ if constexpr (std::is_same_v<T, WavePtrs>)
+ data.waves.clear();
+}
+
+#ifdef WITH_VST
+template void clear<PluginPtrs>();
+#endif
+template void clear<WavePtrs>();
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef G_DEBUG_MODE
+
+void debug()
+{
puts("======== SYSTEM STATUS ========");
-
- puts("model::channels");
+
+ puts("model::layout");
int i = 0;
- for (const Channel* c : channels) {
- printf("\t%d) %p - ID=%d name='%s' type=%d columnID=%d\n",
- i++, (void*) c, c->state->id, c->state->name.c_str(), (int) c->getType(), c->getColumnId());
-/*
- if (c->hasData())
- printf("\t\twave: ID=%d\n", static_cast<const SampleChannel*>(c)->waveId);
-*/
+ for (const channel::Data& c : get().channels)
+ {
+ printf("\t%d) - ID=%d name='%s' type=%d columnID=%d state=%p\n",
+ i++, c.id, c.name.c_str(), (int)c.type, c.columnId, (void*)&c.state);
#ifdef WITH_VST
- if (c->pluginIds.size() > 0) {
+ if (c.plugins.size() > 0)
+ {
puts("\t\tplugins:");
- for (ID id : c->pluginIds)
- printf("\t\t\tID=%d\n", id);
+ for (const auto& p : c.plugins)
+ printf("\t\t\t%p - ID=%d\n", (void*)p, p->id);
}
#endif
}
- puts("model::waves");
+ puts("model::state.channels");
i = 0;
- for (const Wave* w : waves)
- printf("\t%d) %p - ID=%d name='%s'\n", i++, (void*)w, w->id, w->getPath().c_str());
-
-#ifdef WITH_VST
- puts("model::plugins");
+ for (const auto& c : state.channels)
+ {
+ printf("\t%d) - %p\n", i++, (void*)c.get());
+ }
+
+ puts("model::data.waves");
i = 0;
- for (const Plugin* p : plugins) {
- if (p->valid)
- printf("\t%d) %p - ID=%d name='%s'\n", i++, (void*)p, p->id, p->getName().c_str());
- else
- printf("\t%d) %p - ID=%d INVALID\n", i++, (void*)p, p->id);
+ for (const auto& w : data.waves)
+ printf("\t%d) %p - ID=%d name='%s'\n", i++, (void*)w.get(), w->id, w->getPath().c_str());
+
+ puts("model::data.actions");
+
+ for (const auto& [frame, actions] : getAll<Actions>())
+ {
+ printf("\tframe: %d\n", frame);
+ for (const Action& a : actions)
+ printf("\t\t(%p) - ID=%d, frame=%d, channel=%d, value=0x%X, prevId=%d, prev=%p, nextId=%d, next=%p\n",
+ (void*)&a, a.id, a.frame, a.channelId, a.event.getRaw(), a.prevId, (void*)a.prev, a.nextId, (void*)a.next);
}
-#endif
- puts("model::clock");
+#ifdef WITH_VST
- printf("\tclock.status = %d\n", static_cast<int>(clock.get()->status));
- printf("\tclock.bars = %d\n", clock.get()->bars);
- printf("\tclock.beats = %d\n", clock.get()->beats);
- printf("\tclock.bpm = %f\n", clock.get()->bpm);
- printf("\tclock.quantize = %d\n", clock.get()->quantize);
+ puts("model::data.plugins");
- puts("model::actions");
+ i = 0;
+ for (const auto& p : data.plugins)
+ printf("\t%d) %p - ID=%d\n", i++, (void*)p.get(), p->id);
- for (auto& kv : actions.get()->map) {
- printf("\tframe: %d\n", kv.first);
- for (const Action& a : kv.second)
- printf("\t\t(%p) - ID=%d, frame=%d, channel=%d, value=0x%X, prevId=%d, prev=%p, nextId=%d, next=%p\n",
- (void*) &a, a.id, a.frame, a.channelId, a.event.getRaw(), a.prevId, (void*) a.prev, a.nextId, (void*) a.next);
- }
-
- puts("===============================");
+#endif
}
#endif // G_DEBUG_MODE
-}}} // giada::m::model::
+} // namespace giada::m::model
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_RENDER_MODEL_H
#define G_RENDER_MODEL_H
-
-#include <algorithm>
-#include "core/model/traits.h"
#include "core/channels/channel.h"
-#include "core/channels/state.h"
#include "core/const.h"
-#include "core/wave.h"
#include "core/plugins/plugin.h"
-#include "core/rcuList.h"
#include "core/recorder.h"
+#include "core/swapper.h"
+#include "core/wave.h"
+#include "utils/vector.h"
+#include <algorithm>
-
-namespace giada {
-namespace m {
-namespace model
-{
-namespace
-{
-/* getIter_
-Returns an iterator of an element from list 'list' with given ID. */
-
-template<typename L>
-auto getIter_(L& list, ID id)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
- {
- return t->id == id;
- });
- assert(it != list.end());
- return it;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* onSwapByIndex_
-Swaps i-th element from list with a new one and applies a function f to it. */
-
-template<typename L>
-void onSwapByIndex_(L& list, std::size_t i, std::function<void(typename L::value_type&)> f)
-{
- std::unique_ptr<typename L::value_type> o = list.clone(i);
- f(*o.get());
- list.swap(std::move(o), i);
-}
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-struct Clock
-{
- ClockStatus status = ClockStatus::STOPPED;
- int framesInLoop = 0;
- int framesInBar = 0;
- int framesInBeat = 0;
- int framesInSeq = 0;
- int bars = G_DEFAULT_BARS;
- int beats = G_DEFAULT_BEATS;
- float bpm = G_DEFAULT_BPM;
- int quantize = G_DEFAULT_QUANTIZE;
-};
-
-struct Mixer
+namespace giada::m::model
{
- bool hasSolos = false;
- bool inToOut = false;
-};
-
-
struct Kernel
{
bool audioReady = false;
bool midiReady = false;
};
-
struct Recorder
{
bool isRecordingAction = false;
bool isRecordingInput = false;
};
-
struct MidiIn
{
bool enabled = false;
uint32_t volumeOut = 0x0;
uint32_t beatDouble = 0x0;
uint32_t beatHalf = 0x0;
- uint32_t metronome = 0x0;
+ uint32_t metronome = 0x0;
};
-
-struct Actions
+struct Clock
{
- Actions() = default;
- Actions(const Actions& o);
+ struct State
+ {
+ WeakAtomic<int> currentFrameWait = 0;
+ WeakAtomic<int> currentFrame = 0;
+ WeakAtomic<int> currentBeat = 0;
+ };
- recorder::ActionMap map;
+ State* state = nullptr;
+ ClockStatus status = ClockStatus::STOPPED;
+ int framesInLoop = 0;
+ int framesInBar = 0;
+ int framesInBeat = 0;
+ int framesInSeq = 0;
+ int bars = G_DEFAULT_BARS;
+ int beats = G_DEFAULT_BEATS;
+ float bpm = G_DEFAULT_BPM;
+ int quantize = G_DEFAULT_QUANTIZE;
};
+struct Mixer
+{
+ struct State
+ {
+ std::atomic<bool> active = false;
+ WeakAtomic<float> peakOut = 0.0f;
+ WeakAtomic<float> peakIn = 0.0f;
+ };
+
+ State* state = nullptr;
+ bool hasSolos = false;
+ bool inToOut = false;
+};
-using ClockLock = RCUList<Clock>::Lock;
-using MixerLock = RCUList<Mixer>::Lock;
-using KernelLock = RCUList<Kernel>::Lock;
-using RecorderLock = RCUList<Recorder>::Lock;
-using MidiInLock = RCUList<MidiIn>::Lock;
-using ActionsLock = RCUList<Actions>::Lock;
-using ChannelsLock = RCUList<Channel>::Lock;
-using WavesLock = RCUList<Wave>::Lock;
-#ifdef WITH_VST
-using PluginsLock = RCUList<Plugin>::Lock;
-#endif
-
-extern RCUList<Clock> clock;
-extern RCUList<Mixer> mixer;
-extern RCUList<Kernel> kernel;
-extern RCUList<Recorder> recorder;
-extern RCUList<MidiIn> midiIn;
-extern RCUList<Actions> actions;
-extern RCUList<Channel> channels;
-extern RCUList<Wave> waves;
-#ifdef WITH_VST
-extern RCUList<Plugin> plugins;
-#endif
-
+struct Layout
+{
+ channel::Data& getChannel(ID id);
+ const channel::Data& getChannel(ID id) const;
-/* -------------------------------------------------------------------------- */
+ Clock clock;
+ Mixer mixer;
+ Kernel kernel;
+ Recorder recorder;
+ MidiIn midiIn;
+ std::vector<channel::Data> channels;
-template<typename L>
-bool exists(L& list, ID id)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- typename L::Lock l(list);
- auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
- {
- return t->id == id;
- });
- return it != list.end();
-}
+ /* locked
+ If locked, Mixer won't process channels. This is used to allow editing the
+ data (e.g. Actions or Plugins) a channel points to without data races. */
+ bool locked = false;
+};
-/* -------------------------------------------------------------------------- */
+/* Lock
+Alias for a REALTIME scoped lock provided by the Swapper class. Use this in the
+real-time thread to lock the Layout. */
+using Lock = Swapper<Layout>::RtLock;
-/* getIndex (thread safe)
-Returns the index of element with ID from a list. */
+/* SwapType
+Type of Layout change.
+ Hard: the structure has changed (e.g. add a new channel);
+ Soft: a property has changed (e.g. change volume);
+ None: something has changed but we don't care.
+Used by model listeners to determine the type of change that occured in the
+layout. */
-template<typename L>
-std::size_t getIndex(L& list, ID id)
+enum class SwapType
{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- typename L::Lock l(list);
- return std::distance(list.begin(), getIter_(list, id));
-}
-
+ HARD,
+ SOFT,
+ NONE
+};
/* -------------------------------------------------------------------------- */
-
-/* getIndex (thread safe)
-Returns the element ID of the i-th element of a list. */
-
-template<typename L>
-ID getId(L& list, std::size_t i)
+class DataLock
{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- typename L::Lock l(list);
- return list.get(i)->id;
-}
+public:
+ DataLock(SwapType t = SwapType::HARD);
+ ~DataLock();
+private:
+ SwapType m_swapType;
+};
/* -------------------------------------------------------------------------- */
+/* init
+Initializes the internal layout. */
-template<typename L>
-typename L::value_type& get(L& list, ID id)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- return **getIter_(list, id);
-}
+void init();
+/* get
+Returns a reference to the NON-REALTIME layout structure. */
-/* -------------------------------------------------------------------------- */
+Layout& get();
+/* get_RT
+Returns a Lock object for REALTIME processing. Access layout by calling
+Lock::get() method (returns ready-only Layout). */
-/* onGet (1) (thread safe)
-Utility function for reading ID-based things from a RCUList. */
+Lock get_RT();
-template<typename L>
-void onGet(L& list, ID id, std::function<void(typename L::value_type&)> f, bool rebuild=false)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- typename L::Lock l(list);
- f(**getIter_(list, id));
- if (rebuild)
- list.changed.store(true);
-}
+/* swap
+Swap non-rt layout with the rt one. See 'SwapType' notes above. */
+void swap(SwapType t);
-/* onGet (2) (thread safe)
-Same as (1), for non-ID-based things. */
+/* onSwap
+Registers an optional callback fired when the layout has been swapped. Useful
+for listening to model changes. */
-template<typename L>
-void onGet(L& list, std::function<void(typename L::value_type&)> f)
-{
- static_assert(!has_id<typename L::value_type>(), "This type has ID");
- typename L::Lock l(list);
- f(*list.get());
-}
+void onSwap(std::function<void(SwapType)> f);
+bool isLocked();
-/* ---------------------------------------------------------------------------*/
+/* -------------------------------------------------------------------------- */
+/* Model utilities */
-/* onSwap (1) (thread safe)
-Utility function for swapping ID-based things in a RCUList. */
+#ifdef WITH_VST
+using PluginPtr = std::unique_ptr<Plugin>;
+#endif
+using WavePtr = std::unique_ptr<Wave>;
+using ChannelBufferPtr = std::unique_ptr<channel::Buffer>;
+using ChannelStatePtr = std::unique_ptr<channel::State>;
-template<typename L>
-void onSwap(L& list, ID id, std::function<void(typename L::value_type&)> f)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- onSwapByIndex_(list, getIndex(list, id), f);
-}
+#ifdef WITH_VST
+using PluginPtrs = std::vector<PluginPtr>;
+#endif
+using WavePtrs = std::vector<WavePtr>;
+using Actions = recorder::ActionMap;
+using ChannelBufferPtrs = std::vector<ChannelBufferPtr>;
+using ChannelStatePtrs = std::vector<ChannelStatePtr>;
+// TODO - are ID-based objects still necessary?
-/* onSwap (2) (thread safe)
-Utility function for swapping things in a RCUList when the list contains only
-a single element (and so with no ID). */
+template <typename T>
+T& getAll();
-template<typename L>
-void onSwap(L& list, std::function<void(typename L::value_type&)> f)
-{
- static_assert(!has_id<typename L::value_type>(), "This type has ID");
- onSwapByIndex_(list, 0, f);
-}
+/* find
+Finds something (Plugins or Waves) given an ID. Returns nullptr if the object is
+not found. */
+template <typename T>
+T* find(ID id);
-/* ---------------------------------------------------------------------------*/
+template <typename T>
+void add(T);
+template <typename T>
+void remove(const T&);
-#ifdef G_DEBUG_MODE
+template <typename T>
+T& back();
-void debug();
+template <typename T>
+void clear();
+#ifdef G_DEBUG_MODE
+void debug();
#endif
-}}} // giada::m::model::
-
+} // namespace giada::m::model
#endif
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/model/model.h"
+#include "core/model/storage.h"
#include "core/channels/channelManager.h"
+#include "core/conf.h"
#include "core/kernelAudio.h"
+#include "core/model/model.h"
#include "core/patch.h"
-#include "core/conf.h"
#include "core/plugins/pluginManager.h"
#include "core/recorderHandler.h"
-#include "core/waveManager.h"
#include "core/sequencer.h"
-#include "core/model/storage.h"
+#include "core/waveManager.h"
+#include <cassert>
+namespace giada::m::model
+{
+namespace
+{
+void loadChannels_(const std::vector<patch::Channel>& channels, int samplerate)
+{
+ float samplerateRatio = conf::conf.samplerate / static_cast<float>(samplerate);
-namespace giada {
-namespace m {
-namespace model
+ for (const patch::Channel& pchannel : channels)
+ get().channels.push_back(channelManager::deserializeChannel(pchannel, samplerateRatio));
+}
+
+/* -------------------------------------------------------------------------- */
+
+void loadActions_(const std::vector<patch::Action>& pactions)
{
+ getAll<Actions>() = std::move(recorderHandler::deserializeActions(pactions));
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
void store(patch::Patch& patch)
{
-#ifdef WITH_VST
- PluginsLock pl (plugins);
-#endif
- ActionsLock al (actions);
- WavesLock wl (waves);
- ClockLock cl (clock);
- ChannelsLock chl(channels);
-
- patch.bars = clock.get()->bars;
- patch.beats = clock.get()->beats;
- patch.bpm = clock.get()->bpm;
- patch.quantize = clock.get()->quantize;
- patch.metronome = sequencer::isMetronomeOn();
+ const Layout& layout = get();
+
+ patch.bars = layout.clock.bars;
+ patch.beats = layout.clock.beats;
+ patch.bpm = layout.clock.bpm;
+ patch.quantize = layout.clock.quantize;
+ patch.metronome = sequencer::isMetronomeOn(); // TODO - add bool metronome to Layout
patch.samplerate = conf::conf.samplerate;
#ifdef WITH_VST
- for (const Plugin* p : plugins)
+ for (const auto& p : getAll<PluginPtrs>())
patch.plugins.push_back(pluginManager::serializePlugin(*p));
#endif
- patch.actions = recorderHandler::serializeActions(actions.get()->map);
+ patch.actions = recorderHandler::serializeActions(getAll<Actions>());
- for (const Wave* w : waves)
+ for (const auto& w : getAll<WavePtrs>())
patch.waves.push_back(waveManager::serializeWave(*w));
- for (const Channel* c : channels)
- patch.channels.push_back(channelManager::serializeChannel(*c));
+ for (const channel::Data& c : layout.channels)
+ patch.channels.push_back(channelManager::serializeChannel(c));
}
-
/* -------------------------------------------------------------------------- */
-
void store(conf::Conf& conf)
{
- MidiInLock l(midiIn);
-
- conf.midiInEnabled = midiIn.get()->enabled;
- conf.midiInFilter = midiIn.get()->filter;
- conf.midiInRewind = midiIn.get()->rewind;
- conf.midiInStartStop = midiIn.get()->startStop;
- conf.midiInActionRec = midiIn.get()->actionRec;
- conf.midiInInputRec = midiIn.get()->inputRec;
- conf.midiInMetronome = midiIn.get()->metronome;
- conf.midiInVolumeIn = midiIn.get()->volumeIn;
- conf.midiInVolumeOut = midiIn.get()->volumeOut;
- conf.midiInBeatDouble = midiIn.get()->beatDouble;
- conf.midiInBeatHalf = midiIn.get()->beatHalf;
+ const Layout& layout = get();
+
+ conf.midiInEnabled = layout.midiIn.enabled;
+ conf.midiInFilter = layout.midiIn.filter;
+ conf.midiInRewind = layout.midiIn.rewind;
+ conf.midiInStartStop = layout.midiIn.startStop;
+ conf.midiInActionRec = layout.midiIn.actionRec;
+ conf.midiInInputRec = layout.midiIn.inputRec;
+ conf.midiInMetronome = layout.midiIn.metronome;
+ conf.midiInVolumeIn = layout.midiIn.volumeIn;
+ conf.midiInVolumeOut = layout.midiIn.volumeOut;
+ conf.midiInBeatDouble = layout.midiIn.beatDouble;
+ conf.midiInBeatHalf = layout.midiIn.beatHalf;
}
-
/* -------------------------------------------------------------------------- */
-
void load(const patch::Patch& patch)
{
- onSwap(clock, [&](Clock& c)
- {
- c.status = ClockStatus::STOPPED;
- c.bars = patch.bars;
- c.beats = patch.beats;
- c.bpm = patch.bpm;
- c.quantize = patch.quantize;
- });
-
- onSwap(actions, [&](Actions& a)
- {
- a.map = recorderHandler::deserializeActions(patch.actions);
- });
+ DataLock lock;
+
+ /* Clear and re-initialize channels first. */
+
+ get().channels = {};
+ getAll<ChannelBufferPtrs>().clear();
+ getAll<ChannelStatePtrs>().clear();
+
+ /* Load external data first: plug-ins and waves. */
#ifdef WITH_VST
- for (const patch::Plugin& pplugin : patch.plugins)
- plugins.push(pluginManager::deserializePlugin(pplugin, patch.version));
+ getAll<PluginPtrs>().clear();
+ for (const patch::Plugin& pplugin : patch.plugins)
+ getAll<PluginPtrs>().push_back(pluginManager::deserializePlugin(pplugin, patch.version));
#endif
-
- for (const patch::Wave& pwave : patch.waves) {
+
+ getAll<WavePtrs>().clear();
+ for (const patch::Wave& pwave : patch.waves)
+ {
std::unique_ptr<Wave> w = waveManager::deserializeWave(pwave, conf::conf.samplerate,
- conf::conf.rsmpQuality);
+ conf::conf.rsmpQuality);
if (w != nullptr)
- waves.push(std::move(w));
+ getAll<WavePtrs>().push_back(std::move(w));
}
- channels.clear();
- for (const patch::Channel& pchannel : patch.channels)
- channels.push(channelManager::deserializeChannel(pchannel, kernelAudio::getRealBufSize()));
-
- /* Load Waves into Channels. */
-
- ChannelsLock cl(channels);
- WavesLock wl(waves);
-
- float samplerateRatio = conf::conf.samplerate / static_cast<float>(patch::patch.samplerate);
-
- for (Channel* c : channels) {
- if (!c->samplePlayer)
- continue;
- if (exists(waves, c->samplePlayer->getWaveId()))
- c->samplePlayer->setWave(get(waves, c->samplePlayer->getWaveId()), samplerateRatio);
- else
- c->samplePlayer->setInvalidWave();
- }
-}
+ /* Then load up channels, actions and global properties. */
+ loadChannels_(patch.channels, patch::patch.samplerate);
+ loadActions_(patch.actions);
-/* -------------------------------------------------------------------------- */
+ get().clock.status = ClockStatus::STOPPED;
+ get().clock.bars = patch.bars;
+ get().clock.beats = patch.beats;
+ get().clock.bpm = patch.bpm;
+ get().clock.quantize = patch.quantize;
+}
+/* -------------------------------------------------------------------------- */
void load(const conf::Conf& c)
{
- onSwap(midiIn, [&](MidiIn& m)
- {
- m.enabled = c.midiInEnabled;
- m.filter = c.midiInFilter;
- m.rewind = c.midiInRewind;
- m.startStop = c.midiInStartStop;
- m.actionRec = c.midiInActionRec;
- m.inputRec = c.midiInInputRec;
- m.volumeIn = c.midiInVolumeIn;
- m.volumeOut = c.midiInVolumeOut;
- m.beatDouble = c.midiInBeatDouble;
- m.beatHalf = c.midiInBeatHalf;
- m.metronome = c.midiInMetronome;
- });
+ get().midiIn.enabled = c.midiInEnabled;
+ get().midiIn.filter = c.midiInFilter;
+ get().midiIn.rewind = c.midiInRewind;
+ get().midiIn.startStop = c.midiInStartStop;
+ get().midiIn.actionRec = c.midiInActionRec;
+ get().midiIn.inputRec = c.midiInInputRec;
+ get().midiIn.volumeIn = c.midiInVolumeIn;
+ get().midiIn.volumeOut = c.midiInVolumeOut;
+ get().midiIn.beatDouble = c.midiInBeatDouble;
+ get().midiIn.beatHalf = c.midiInBeatHalf;
+ get().midiIn.metronome = c.midiInMetronome;
+
+ swap(SwapType::NONE);
}
-}}} // giada::m::model::
+} // namespace giada::m::model
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MODEL_STORAGE_H
#define G_MODEL_STORAGE_H
-
-namespace giada {
-namespace m {
-namespace patch
+namespace giada::m::patch
{
struct Patch;
}
-namespace conf
+namespace giada::m::conf
{
struct Conf;
}
-namespace model
+namespace giada::m::model
{
void store(conf::Conf& c);
void store(patch::Patch& p);
void load(const patch::Patch& p);
void load(const conf::Conf& c);
-}}} // giada::m::model::
-
+} // namespace giada::m::model
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_MODEL_TRAITS_H
-#define G_MODEL_TRAITS_H
-
-
-#include <type_traits>
-#include "core/wave.h"
-#include "core/plugins/plugin.h"
-#include "core/channels/channel.h"
-
-
-namespace giada {
-namespace m {
-namespace model
-{
-template <typename T> struct has_id : std::false_type {};
-template <> struct has_id<Channel> : std::true_type {};
-template <> struct has_id<Wave> : std::true_type {};
-#ifdef WITH_VST
-template <> struct has_id<Plugin> : std::true_type {};
-#endif
-}}} // giada::m::model::
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <fstream>
+#include "patch.h"
+#include "core/mixer.h"
#include "deps/json/single_include/nlohmann/json.hpp"
-#include "utils/math.h"
#include "utils/log.h"
-#include "core/mixer.h"
-#include "patch.h"
-
+#include "utils/math.h"
+#include <fstream>
namespace nl = nlohmann;
-
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace patch
{
namespace
patch.metronome = j.value(PATCH_KEY_METRONOME, false);
}
-
/* -------------------------------------------------------------------------- */
-
void readColumns_(const nl::json& j)
{
ID id = 0;
- for (const auto& jcol : j[PATCH_KEY_COLUMNS]) {
+ for (const auto& jcol : j[PATCH_KEY_COLUMNS])
+ {
Column c;
c.id = jcol.value(PATCH_KEY_COLUMN_ID, ++id);
- c.width = jcol.value(PATCH_KEY_COLUMN_WIDTH, G_DEFAULT_COLUMN_WIDTH);
+ c.width = jcol.value(PATCH_KEY_COLUMN_WIDTH, G_DEFAULT_COLUMN_WIDTH);
patch.columns.push_back(c);
}
}
-
/* -------------------------------------------------------------------------- */
#ifdef WITH_VST
-
void readPlugins_(const nl::json& j)
{
- if (!j.contains(PATCH_KEY_PLUGINS))
+ if (!j.contains(PATCH_KEY_PLUGINS))
return;
ID id = 0;
- for (const auto& jplugin : j[PATCH_KEY_PLUGINS]) {
+ for (const auto& jplugin : j[PATCH_KEY_PLUGINS])
+ {
Plugin p;
p.id = jplugin.value(PATCH_KEY_PLUGIN_ID, ++id);
p.path = jplugin.value(PATCH_KEY_PLUGIN_PATH, "");
p.state = jplugin.value(PATCH_KEY_PLUGIN_STATE, "");
for (const auto& jmidiParam : jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS])
- p.midiInParams.push_back(jmidiParam);
+ p.midiInParams.push_back(jmidiParam);
patch.plugins.push_back(p);
}
}
-
#endif
-
/* -------------------------------------------------------------------------- */
-
void readWaves_(const nl::json& j, const std::string& basePath)
{
- if (!j.contains(PATCH_KEY_WAVES))
+ if (!j.contains(PATCH_KEY_WAVES))
return;
ID id = 0;
- for (const auto& jwave : j[PATCH_KEY_WAVES]) {
+ for (const auto& jwave : j[PATCH_KEY_WAVES])
+ {
Wave w;
w.id = jwave.value(PATCH_KEY_WAVE_ID, ++id);
w.path = basePath + jwave.value(PATCH_KEY_WAVE_PATH, "");
}
}
-
/* -------------------------------------------------------------------------- */
-
void readActions_(const nl::json& j)
{
- if (!j.contains(PATCH_KEY_ACTIONS))
+ if (!j.contains(PATCH_KEY_ACTIONS))
return;
ID id = 0;
- for (const auto& jaction : j[PATCH_KEY_ACTIONS]) {
+ for (const auto& jaction : j[PATCH_KEY_ACTIONS])
+ {
Action a;
a.id = jaction.value(G_PATCH_KEY_ACTION_ID, ++id);
a.channelId = jaction.value(G_PATCH_KEY_ACTION_CHANNEL, 0);
}
}
-
/* -------------------------------------------------------------------------- */
-
void readChannels_(const nl::json& j)
{
- if (!j.contains(PATCH_KEY_CHANNELS))
+ if (!j.contains(PATCH_KEY_CHANNELS))
return;
ID defaultId = mixer::PREVIEW_CHANNEL_ID;
- for (const auto& jchannel : j[PATCH_KEY_CHANNELS]) {
+ for (const auto& jchannel : j[PATCH_KEY_CHANNELS])
+ {
Channel c;
c.id = jchannel.value(PATCH_KEY_CHANNEL_ID, ++defaultId);
c.type = static_cast<ChannelType>(jchannel.value(PATCH_KEY_CHANNEL_TYPE, 1));
- c.volume = jchannel.value(PATCH_KEY_CHANNEL_VOLUME, G_DEFAULT_VOL);
+ c.volume = jchannel.value(PATCH_KEY_CHANNEL_VOLUME, G_DEFAULT_VOL);
c.height = jchannel.value(PATCH_KEY_CHANNEL_SIZE, G_GUI_UNIT);
c.name = jchannel.value(PATCH_KEY_CHANNEL_NAME, "");
c.columnId = jchannel.value(PATCH_KEY_CHANNEL_COLUMN, 1);
c.midiOutChan = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, 0);
#ifdef WITH_VST
- if (jchannel.contains(PATCH_KEY_CHANNEL_PLUGINS))
- for (const auto& jplugin : jchannel[PATCH_KEY_CHANNEL_PLUGINS])
+ if (jchannel.contains(PATCH_KEY_CHANNEL_PLUGINS))
+ for (const auto& jplugin : jchannel[PATCH_KEY_CHANNEL_PLUGINS])
c.pluginIds.push_back(jplugin);
#endif
}
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
void writePlugins_(nl::json& j)
{
j[PATCH_KEY_PLUGINS] = nl::json::array();
- for (const Plugin& p : patch.plugins) {
+ for (const Plugin& p : patch.plugins)
+ {
nl::json jplugin;
jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS] = nl::json::array();
for (uint32_t param : p.midiInParams)
jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS].push_back(param);
-
+
j[PATCH_KEY_PLUGINS].push_back(jplugin);
}
}
#endif
-
/* -------------------------------------------------------------------------- */
-
void writeColumns_(nl::json& j)
{
j[PATCH_KEY_COLUMNS] = nl::json::array();
- for (const Column& column : patch.columns) {
+ for (const Column& column : patch.columns)
+ {
nl::json jcolumn;
jcolumn[PATCH_KEY_COLUMN_ID] = column.id;
jcolumn[PATCH_KEY_COLUMN_WIDTH] = column.width;
}
}
-
/* -------------------------------------------------------------------------- */
-
void writeActions_(nl::json& j)
{
j[PATCH_KEY_ACTIONS] = nl::json::array();
- for (const Action& a : patch.actions) {
+ for (const Action& a : patch.actions)
+ {
nl::json jaction;
jaction[G_PATCH_KEY_ACTION_ID] = a.id;
jaction[G_PATCH_KEY_ACTION_CHANNEL] = a.channelId;
}
}
-
/* -------------------------------------------------------------------------- */
-
void writeWaves_(nl::json& j)
{
j[PATCH_KEY_WAVES] = nl::json::array();
- for (const Wave& w : patch.waves) {
+ for (const Wave& w : patch.waves)
+ {
nl::json jwave;
jwave[PATCH_KEY_WAVE_ID] = w.id;
jwave[PATCH_KEY_WAVE_PATH] = w.path;
/* -------------------------------------------------------------------------- */
-
void writeCommons_(nl::json& j)
{
j[PATCH_KEY_HEADER] = "GIADAPTC";
j[PATCH_KEY_METRONOME] = patch.metronome;
}
-
/* -------------------------------------------------------------------------- */
-
void writeChannels_(nl::json& j)
{
j[PATCH_KEY_CHANNELS] = nl::json::array();
- for (const Channel& c : patch.channels) {
+ for (const Channel& c : patch.channels)
+ {
nl::json jchannel;
}
}
-
/* -------------------------------------------------------------------------- */
-
void modernize_()
{
- for (Channel& c : patch.channels) {
+ for (Channel& c : patch.channels)
+ {
/* 0.16.3
Make sure that ChannelType is correct: ID 1, 2 are MASTER channels, ID 3
is PREVIEW channel. */
if (c.id == mixer::MASTER_OUT_CHANNEL_ID || c.id == mixer::MASTER_IN_CHANNEL_ID)
c.type = ChannelType::MASTER;
- else
- if (c.id == mixer::PREVIEW_CHANNEL_ID)
+ else if (c.id == mixer::PREVIEW_CHANNEL_ID)
c.type = ChannelType::PREVIEW;
-
+
/* 0.16.4
Make sure internal channels are never armed. */
if (c.type == ChannelType::PREVIEW || c.type == ChannelType::MASTER)
c.armed = false;
-
+
/* 0.16.3
Set panning to default (0.5) and waveId to 0 for non-Sample Channels. */
- if (c.type != ChannelType::SAMPLE) {
+ if (c.type != ChannelType::SAMPLE)
+ {
c.pan = G_DEFAULT_PAN;
c.waveId = 0;
}
- }
+ }
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Patch patch;
-
/* -------------------------------------------------------------------------- */
-
-bool Version::operator ==(const Version& o) const
+bool Version::operator==(const Version& o) const
{
- return major == o.major && minor == o.minor && patch == o.patch;
+ return major == o.major && minor == o.minor && patch == o.patch;
}
-
-bool Version::operator <(const Version& o) const
+bool Version::operator<(const Version& o) const
{
- if (major < o.major) return true;
- if (minor < o.minor) return true;
- if (patch < o.patch) return true;
+ if (major < o.major)
+ return true;
+ if (minor < o.minor)
+ return true;
+ if (patch < o.patch)
+ return true;
return false;
}
-
/* -------------------------------------------------------------------------- */
-
void init()
{
patch = Patch();
}
-
/* -------------------------------------------------------------------------- */
-
bool write(const std::string& file)
{
nl::json j;
writePlugins_(j);
#endif
- std::ofstream ofs(file);
+ std::ofstream ofs(file);
if (!ofs.good())
return false;
- ofs << j;
+ ofs << j;
return true;
}
-
/* -------------------------------------------------------------------------- */
-
int read(const std::string& file, const std::string& basePath)
{
std::ifstream ifs(file);
if (!ifs.good())
return G_PATCH_UNREADABLE;
-
+
nl::json j = nl::json::parse(ifs);
if (j[PATCH_KEY_HEADER] != "GIADAPTC")
return G_PATCH_INVALID;
-
+
patch.version = {
- static_cast<int>(j[PATCH_KEY_VERSION_MAJOR]),
- static_cast<int>(j[PATCH_KEY_VERSION_MINOR]),
- static_cast<int>(j[PATCH_KEY_VERSION_PATCH])
- };
+ static_cast<int>(j[PATCH_KEY_VERSION_MAJOR]),
+ static_cast<int>(j[PATCH_KEY_VERSION_MINOR]),
+ static_cast<int>(j[PATCH_KEY_VERSION_PATCH])};
if (patch.version < Version{0, 16, 0})
return G_PATCH_UNSUPPORTED;
- try {
+ try
+ {
readCommons_(j);
readColumns_(j);
#ifdef WITH_VST
readChannels_(j);
modernize_();
}
- catch (nl::json::exception& e) {
+ catch (nl::json::exception& e)
+ {
u::log::print("[patch::read] Exception thrown: %s\n", e.what());
return G_PATCH_INVALID;
}
return G_PATCH_OK;
}
-}}} // giada::m::patch::
+} // namespace patch
+} // namespace m
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_PATCH_H
#define G_PATCH_H
-
+#include "core/const.h"
+#include "core/types.h"
+#include <cstdint>
#include <string>
#include <vector>
-#include <cstdint>
-#include "core/types.h"
-#include "core/const.h"
-
-namespace giada {
-namespace m {
+namespace giada
+{
+namespace m
+{
namespace patch
{
struct Version
int minor = G_VERSION_MINOR;
int patch = G_VERSION_PATCH;
- bool operator ==(const Version& o) const;
- bool operator < (const Version& o) const;
+ bool operator==(const Version& o) const;
+ bool operator<(const Version& o) const;
};
-
struct Column
{
ID id;
int width;
};
-
struct Channel
{
ID id;
uint32_t midiInReadActions;
uint32_t midiInPitch;
// midi channel
- bool midiOut;
- int midiOutChan;
+ bool midiOut;
+ int midiOutChan;
#ifdef WITH_VST
std::vector<ID> pluginIds;
#endif
};
-
struct Action
{
ID id;
ID nextId;
};
-
struct Wave
{
ID id;
std::string path;
};
-
#ifdef WITH_VST
struct Plugin
{
};
#endif
-
struct Patch
{
Version version;
std::vector<Action> actions;
std::vector<Wave> waves;
#ifdef WITH_VST
- std::vector<Plugin> plugins;
+ std::vector<Plugin> plugins;
#endif
};
-
/* -------------------------------------------------------------------------- */
-
extern Patch patch;
-
/* -------------------------------------------------------------------------- */
/* init
Writes patch to file. */
bool write(const std::string& file);
-}}} // giada::m::patch::
-
+} // namespace patch
+} // namespace m
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <cassert>
-#include <FL/Fl.H>
-#include "utils/log.h"
-#include "utils/time.h"
+#include "plugin.h"
#include "core/const.h"
#include "core/plugins/pluginManager.h"
-#include "plugin.h"
-
-
-using std::string;
-
+#include "utils/log.h"
+#include "utils/time.h"
+#include <FL/Fl.H>
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m
{
Plugin::Plugin(ID id, const std::string& UID)
-: id (id)
-, valid (false)
+: id(id)
+, valid(false)
, onEditorResize(nullptr)
-, m_plugin (nullptr)
-, m_UID (UID)
+, m_plugin(nullptr)
+, m_UID(UID)
+, m_hasEditor(false)
{
}
-
/* -------------------------------------------------------------------------- */
-
Plugin::Plugin(ID id, std::unique_ptr<juce::AudioPluginInstance> plugin, double samplerate,
- int buffersize)
-: id (id)
-, valid (true)
+ int buffersize)
+: id(id)
+, valid(true)
, onEditorResize(nullptr)
-, m_plugin (std::move(plugin))
-, m_bypass (false)
+, m_plugin(std::move(plugin))
+, m_playHead(std::make_unique<pluginHost::Info>())
+, m_bypass(false)
+, m_hasEditor(m_plugin->hasEditor())
{
/* (1) Initialize midiInParams vector, where midiInParams.size == number of
plugin parameters. All values are initially empty (0x0): they will be filled
juce::AudioProcessor::Bus* outBus = getMainBus(BusType::OUT);
juce::AudioProcessor::Bus* inBus = getMainBus(BusType::IN);
- if (outBus != nullptr) outBus->setNumberOfChannels(G_MAX_IO_CHANS);
- if (inBus != nullptr) inBus->setNumberOfChannels(G_MAX_IO_CHANS);
+ if (outBus != nullptr)
+ outBus->setNumberOfChannels(G_MAX_IO_CHANS);
+ if (inBus != nullptr)
+ inBus->setNumberOfChannels(G_MAX_IO_CHANS);
+
+ /* Set pointer to PlayHead, used to pass Giada information (bpm, time, ...)
+ to the plug-in. */
+
+ m_plugin->setPlayHead(m_playHead.get());
m_plugin->prepareToPlay(samplerate, buffersize);
- u::log::print("[Plugin] plugin initialized and ready. MIDI input params: %lu\n",
- midiInParams.size());
+ u::log::print("[Plugin] plugin initialized and ready. MIDI input params: %lu\n",
+ midiInParams.size());
}
-
/* -------------------------------------------------------------------------- */
-
Plugin::Plugin(const Plugin& o)
-: id (o.id)
-, midiInParams (o.midiInParams)
-, valid (o.valid)
+: id(o.id)
+, midiInParams(o.midiInParams)
+, valid(o.valid)
, onEditorResize(o.onEditorResize)
-, m_plugin (std::move(pluginManager::makePlugin(o)->m_plugin))
-, m_bypass (o.m_bypass.load())
+, m_plugin(std::move(pluginManager::makePlugin(o)->m_plugin))
+, m_bypass(o.m_bypass.load())
{
}
-
/* -------------------------------------------------------------------------- */
-
Plugin::~Plugin()
{
if (!valid)
m_plugin->releaseResources();
}
-
/* -------------------------------------------------------------------------- */
-
-void Plugin::componentMovedOrResized(juce::Component& c, bool moved, bool/* resized*/)
+void Plugin::componentMovedOrResized(juce::Component& c, bool moved, bool /* resized*/)
{
if (moved)
return;
onEditorResize(c.getWidth(), c.getHeight());
}
-
/* -------------------------------------------------------------------------- */
-
juce::AudioProcessor::Bus* Plugin::getMainBus(BusType b) const
{
const bool isInput = static_cast<bool>(b);
- for (int i=0; i<m_plugin->getBusCount(isInput); i++)
+ for (int i = 0; i < m_plugin->getBusCount(isInput); i++)
if (m_plugin->getBus(isInput, i)->isMain())
- return m_plugin->getBus(isInput, i);
+ return m_plugin->getBus(isInput, i);
return nullptr;
}
-
int Plugin::countMainOutChannels() const
{
juce::AudioProcessor::Bus* b = getMainBus(BusType::OUT);
return b->getNumberOfChannels();
}
-
/* -------------------------------------------------------------------------- */
-
juce::AudioProcessorEditor* Plugin::createEditor() const
{
juce::AudioProcessorEditor* e = m_plugin->createEditorIfNeeded();
return e;
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getUniqueId() const
+std::string Plugin::getUniqueId() const
{
if (!valid)
return m_UID;
return m_plugin->getPluginDescription().createIdentifierString().toStdString();
}
-
/* -------------------------------------------------------------------------- */
-
int Plugin::getNumParameters() const
{
return valid ? m_plugin->getParameters().size() : 0;
}
-
/* -------------------------------------------------------------------------- */
-
float Plugin::getParameter(int paramIndex) const
{
return m_plugin->getParameters()[paramIndex]->getValue();
}
-
/* -------------------------------------------------------------------------- */
-
void Plugin::setParameter(int paramIndex, float value) const
{
m_plugin->getParameters()[paramIndex]->setValue(value);
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getName() const
+std::string Plugin::getName() const
{
if (!valid)
return "** invalid **";
return m_plugin->getName().toStdString();
}
-
/* -------------------------------------------------------------------------- */
-
bool Plugin::isSuspended() const
{
if (!valid)
return m_plugin->isSuspended();
}
-
/* -------------------------------------------------------------------------- */
-
bool Plugin::acceptsMidi() const
{
if (!valid)
return m_plugin->acceptsMidi();
}
-
/* -------------------------------------------------------------------------- */
-
PluginState Plugin::getState() const
{
+ if (!valid)
+ return {};
juce::MemoryBlock data;
m_plugin->getStateInformation(data);
return PluginState(std::move(data));
}
-
/* -------------------------------------------------------------------------- */
-
bool Plugin::isBypassed() const { return m_bypass.load(); }
void Plugin::setBypass(bool b) { m_bypass.store(b); }
-
/* -------------------------------------------------------------------------- */
-
void Plugin::process(juce::AudioBuffer<float>& out, juce::MidiBuffer m)
{
/* If this is not an instrument (i.e. doesn't accept MIDI), copy the
by taking into account the bus layout - many plug-ins might have mono output
and we have a stereo buffer to fill. */
- for (int i=0, j=0; i<out.getNumChannels(); i++) {
+ for (int i = 0, j = 0; i < out.getNumChannels(); i++)
+ {
if (isInstrument)
out.addFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
else
}
}
-
/* -------------------------------------------------------------------------- */
-
void Plugin::setState(PluginState state)
{
m_plugin->setStateInformation(state.getData(), state.getSize());
}
-
/* -------------------------------------------------------------------------- */
-
int Plugin::getNumPrograms() const
{
if (!valid)
return m_plugin->getNumPrograms();
}
-
/* -------------------------------------------------------------------------- */
-
int Plugin::getCurrentProgram() const
{
if (!valid)
return m_plugin->getCurrentProgram();
}
-
/* -------------------------------------------------------------------------- */
-
void Plugin::setCurrentProgram(int index) const
{
if (valid)
m_plugin->setCurrentProgram(index);
}
-
/* -------------------------------------------------------------------------- */
-
bool Plugin::hasEditor() const
{
- if (!valid)
- return false;
- return m_plugin->hasEditor();
+ return m_hasEditor;
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getProgramName(int index) const
+std::string Plugin::getProgramName(int index) const
{
if (!valid)
return {};
return m_plugin->getProgramName(index).toStdString();
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getParameterName(int index) const
+std::string Plugin::getParameterName(int index) const
{
if (!valid)
return {};
return m_plugin->getParameters()[index]->getName(labelSize).toStdString();
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getParameterText(int index) const
+std::string Plugin::getParameterText(int index) const
{
return m_plugin->getParameters()[index]->getCurrentValueAsText().toStdString();
}
-
/* -------------------------------------------------------------------------- */
-
-string Plugin::getParameterLabel(int index) const
+std::string Plugin::getParameterLabel(int index) const
{
return m_plugin->getParameters()[index]->getLabel().toStdString();
}
-
-}} // giada::m::
-
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
#ifndef G_PLUGIN_H
#define G_PLUGIN_H
-
-#include <vector>
-#include "deps/juce-config.h"
+#include "core/const.h"
+#include "core/midiLearnParam.h"
#include "core/plugins/pluginHost.h"
#include "core/plugins/pluginState.h"
-#include "core/midiLearnParam.h"
-#include "core/const.h"
-
+#include "deps/juce-config.h"
+#include <vector>
-namespace giada {
-namespace m
+namespace giada::m
{
class Plugin : private juce::ComponentListener
{
public:
-
Plugin(ID id, const std::string& UID);
Plugin(ID id, std::unique_ptr<juce::AudioPluginInstance> p, double samplerate, int buffersize);
Plugin(const Plugin& o);
~Plugin();
+ /* TODO - mark is as non-copiable/movable*/
/* getUniqueId
Returns a string-based UID. */
- std::string getUniqueId() const;
- std::string getName() const;
- bool hasEditor() const;
- int getNumParameters() const;
- float getParameter(int index) const;
- std::string getParameterName(int index) const;
- std::string getParameterText(int index) const;
- std::string getParameterLabel(int index) const;
- bool isSuspended() const;
- bool isBypassed() const;
- int getNumPrograms() const;
- int getCurrentProgram() const;
- std::string getProgramName(int index) const;
- void setParameter(int index, float value) const;
- void setCurrentProgram(int index) const;
- bool acceptsMidi() const;
- PluginState getState() const;
+ std::string getUniqueId() const;
+ std::string getName() const;
+ bool hasEditor() const;
+ int getNumParameters() const;
+ float getParameter(int index) const;
+ std::string getParameterName(int index) const;
+ std::string getParameterText(int index) const;
+ std::string getParameterLabel(int index) const;
+ bool isSuspended() const;
+ bool isBypassed() const;
+ int getNumPrograms() const;
+ int getCurrentProgram() const;
+ std::string getProgramName(int index) const;
+ void setParameter(int index, float value) const;
+ void setCurrentProgram(int index) const;
+ bool acceptsMidi() const;
+ PluginState getState() const;
juce::AudioProcessorEditor* createEditor() const;
/* process
copy. */
void process(juce::AudioBuffer<float>& b, juce::MidiBuffer m);
-
+
void setState(PluginState p);
void setBypass(bool b);
external hardware. */
std::vector<MidiLearnParam> midiInParams;
-
+
/* valid
A missing plug-in is loaded anyway, yet marked as 'invalid'. */
std::function<void(int w, int h)> onEditorResize;
private:
-
#ifdef G_OS_WINDOWS
- /* Fuck... */
- #undef IN
- #undef OUT
+/* Fuck... */
+#undef IN
+#undef OUT
#endif
- enum class BusType
- {
- IN = true, OUT = false
+ enum class BusType
+ {
+ IN = true,
+ OUT = false
};
/* JUCE overrides. */
-
+
void componentMovedOrResized(juce::Component& c, bool moved, bool resized) override;
juce::AudioProcessor::Bus* getMainBus(BusType b) const;
int countMainOutChannels() const;
std::unique_ptr<juce::AudioPluginInstance> m_plugin;
+ std::unique_ptr<pluginHost::Info> m_playHead;
juce::AudioBuffer<float> m_buffer;
std::atomic<bool> m_bypass;
The original UID, used for missing plugins. */
std::string m_UID;
+
+ /* m_hasEditor
+ Cached boolean value that tells if the plug-in has editor. Some plug-ins
+ take ages to query it, better fetch the property during construction. */
+
+ bool m_hasEditor;
};
-}} // giada::m::
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-#include <cassert>
-#include "utils/log.h"
-#include "utils/vector.h"
-#include "core/model/model.h"
+#include "core/plugins/pluginHost.h"
#include "core/channels/channel.h"
+#include "core/clock.h"
#include "core/const.h"
+#include "core/model/model.h"
#include "core/plugins/plugin.h"
#include "core/plugins/pluginManager.h"
-#include "core/plugins/pluginHost.h"
-
+#include "utils/log.h"
+#include "utils/vector.h"
+#include <cassert>
-namespace giada {
-namespace m {
-namespace pluginHost
+namespace giada::m::pluginHost
{
namespace
{
-juce::MessageManager* messageManager_;
+std::vector<Plugin*> plugins_;
+juce::MessageManager* messageManager_;
juce::AudioBuffer<float> audioBuffer_;
-ID pluginId_;
-
+ID pluginId_;
/* -------------------------------------------------------------------------- */
-
void giadaToJuceTempBuf_(const AudioBuffer& outBuf)
{
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++)
+ for (int i = 0; i < outBuf.countFrames(); i++)
+ for (int j = 0; j < outBuf.countChannels(); j++)
audioBuffer_.setSample(j, i, outBuf[i][j]);
}
-
/* juceToGiadaOutBuf_
Converts buffer from Juce to Giada. A note for the future: if we overwrite (=)
(as we do now) it's SEND, if we add (+) it's INSERT. */
void juceToGiadaOutBuf_(AudioBuffer& outBuf)
{
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++)
+ for (int i = 0; i < outBuf.countFrames(); i++)
+ for (int j = 0; j < outBuf.countChannels(); j++)
outBuf[i][j] = audioBuffer_.getSample(j, i);
}
-
/* -------------------------------------------------------------------------- */
-
-void processPlugins_(const std::vector<ID>& pluginIds, juce::MidiBuffer& events)
+void processPlugins_(const std::vector<Plugin*>& plugins, juce::MidiBuffer& events)
{
- model::PluginsLock l(model::plugins);
-
- for (ID id : pluginIds) {
- Plugin& p = model::get(model::plugins, id);
- if (!p.valid || p.isSuspended() || p.isBypassed())
+ for (Plugin* p : plugins)
+ {
+ if (!p->valid || p->isSuspended() || p->isBypassed())
continue;
- p.process(audioBuffer_, events);
- events.clear();
+ p->process(audioBuffer_, events);
}
+ events.clear();
}
+} // namespace
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
-ID clonePlugin_(ID pluginId)
+bool Info::getCurrentPosition(CurrentPositionInfo& result)
{
- model::PluginsLock l(model::plugins);
+ result.bpm = clock::getBpm();
+ result.timeInSamples = clock::getCurrentFrame();
+ result.timeInSeconds = clock::getCurrentSecond();
+ result.isPlaying = clock::isRunning();
- const Plugin& original = model::get(model::plugins, pluginId);
- std::unique_ptr<Plugin> clone = pluginManager::makePlugin(original);
- ID newId = clone->id;
+ return true;
+}
- model::plugins.push(std::move(clone));
+/* -------------------------------------------------------------------------- */
- return newId;
+bool Info::canControlTransport()
+{
+ return false;
}
-} // {anonymous}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void close()
{
messageManager_->deleteInstance();
- model::plugins.clear();
+ model::clear<model::PluginPtrs>();
}
-
/* -------------------------------------------------------------------------- */
-
void init(int buffersize)
{
messageManager_ = juce::MessageManager::getInstance();
pluginId_ = 0;
}
-
/* -------------------------------------------------------------------------- */
-
-void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
- juce::MidiBuffer* events)
+void processStack(AudioBuffer& outBuf, const std::vector<Plugin*>& plugins,
+ juce::MidiBuffer* events)
{
assert(outBuf.countFrames() == audioBuffer_.getNumSamples());
sample channels. No need for MIDI events.
If events are not null: MIDI stack (MIDI channels). MIDI channels must not
process the current buffer: give them an empty and clean one. */
-
- if (events == nullptr) {
+
+ if (events == nullptr)
+ {
giadaToJuceTempBuf_(outBuf);
juce::MidiBuffer dummyEvents; // empty
- processPlugins_(pluginIds, dummyEvents);
+ processPlugins_(plugins, dummyEvents);
}
- else {
+ else
+ {
audioBuffer_.clear();
- processPlugins_(pluginIds, *events);
+ processPlugins_(plugins, *events);
}
juceToGiadaOutBuf_(outBuf);
}
-
/* -------------------------------------------------------------------------- */
-
void addPlugin(std::unique_ptr<Plugin> p, ID channelId)
{
- ID pluginId = p->id;
-
- model::plugins.push(std::move(p));
+ model::add(std::move(p));
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- c.pluginIds.push_back(pluginId);
- });
-}
+ const Plugin& pluginRef = model::back<Plugin>();
+ /* TODO - unfortunately JUCE wants mutable plugin objects due to the
+ presence of the non-const processBlock() method. Why not const_casting
+ only in the Plugin class? */
+ model::get().getChannel(channelId).plugins.push_back(const_cast<Plugin*>(&pluginRef));
+ model::swap(model::SwapType::HARD);
+}
/* -------------------------------------------------------------------------- */
-
-void swapPlugin(ID pluginId1, ID pluginId2, ID channelId)
+void swapPlugin(const m::Plugin& p1, const m::Plugin& p2, ID channelId)
{
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- auto a = u::vector::indexOf(c.pluginIds, pluginId1);
- auto b = u::vector::indexOf(c.pluginIds, pluginId2);
-
- std::swap(c.pluginIds.at(a), c.pluginIds.at(b));
- });
-}
+ std::vector<m::Plugin*>& pvec = model::get().getChannel(channelId).plugins;
+ std::size_t index1 = u::vector::indexOf(pvec, &p1);
+ std::size_t index2 = u::vector::indexOf(pvec, &p2);
+ std::swap(pvec.at(index1), pvec.at(index2));
+ model::swap(model::SwapType::HARD);
+}
/* -------------------------------------------------------------------------- */
-
-void freePlugin(ID pluginId, ID channelId)
+void freePlugin(const m::Plugin& plugin, ID channelId)
{
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- u::vector::remove(c.pluginIds, pluginId);
- });
-
- model::plugins.pop(model::getIndex(model::plugins, pluginId));
+ u::vector::remove(model::get().getChannel(channelId).plugins, &plugin);
+ model::swap(model::SwapType::HARD);
+ model::remove(plugin);
}
-
-void freePlugins(const std::vector<ID>& pluginIds)
+void freePlugins(const std::vector<Plugin*>& plugins)
{
- for (ID id : pluginIds)
- model::plugins.pop(model::getIndex(model::plugins, id));
+ // TODO - channels???
+ for (const Plugin* p : plugins)
+ model::remove(*p);
}
-
/* -------------------------------------------------------------------------- */
-
-std::vector<ID> clonePlugins(std::vector<ID> pluginIds)
+std::vector<Plugin*> clonePlugins(const std::vector<Plugin*>& plugins)
{
- std::vector<ID> out;
- for (ID id : pluginIds)
- out.push_back(clonePlugin_(id));
+ std::vector<Plugin*> out;
+ for (const Plugin* p : plugins)
+ {
+ model::add(pluginManager::makePlugin(*p));
+ out.push_back(&model::back<Plugin>());
+ }
return out;
}
-
/* -------------------------------------------------------------------------- */
-
void setPluginParameter(ID pluginId, int paramIndex, float value)
{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setParameter(paramIndex, value);
- });
+ model::find<Plugin>(pluginId)->setParameter(paramIndex, value);
}
-
/* -------------------------------------------------------------------------- */
-
void setPluginProgram(ID pluginId, int programIndex)
{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setCurrentProgram(programIndex);
- });
+ model::find<Plugin>(pluginId)->setCurrentProgram(programIndex);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleBypass(ID pluginId)
{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setBypass(!p.isBypassed());
- });
+ Plugin& plugin = *model::find<Plugin>(pluginId);
+ plugin.setBypass(!plugin.isBypassed());
}
-
/* -------------------------------------------------------------------------- */
-
void runDispatchLoop()
{
messageManager_->runDispatchLoopUntil(10);
}
-
-}}} // giada::m::pluginHost::
-
+} // namespace giada::m::pluginHost
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef G_PLUGIN_HOST_H
#define G_PLUGIN_HOST_H
-
-#include <functional>
-#include "deps/juce-config.h"
#include "core/types.h"
+#include "deps/juce-config.h"
+#include <functional>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Plugin;
class AudioBuffer;
-namespace pluginHost
+} // namespace giada::m
+namespace giada::m::pluginHost
+{
+struct Info : public juce::AudioPlayHead
{
-using Stack = std::vector<std::shared_ptr<Plugin>>;
+ bool getCurrentPosition(CurrentPositionInfo& result) override;
+ bool canControlTransport() override;
+};
+
+/* -------------------------------------------------------------------------- */
void init(int buffersize);
void close();
/* processStack
Applies the fx list to the buffer. */
-void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
- juce::MidiBuffer* events=nullptr);
+void processStack(AudioBuffer& outBuf, const std::vector<Plugin*>& plugins,
+ juce::MidiBuffer* events = nullptr);
/* swapPlugin
-Swaps plug-in with ID 1 with plug-in with ID 2 in Channel 'channelId'. */
+Swaps plug-in 1 with plug-in 2 in Channel 'channelId'. */
-void swapPlugin(ID pluginId1, ID pluginId2, ID channelId);
+void swapPlugin(const m::Plugin& p1, const m::Plugin& p2, ID channelId);
/* freePlugin.
Unloads plugin from channel 'channelId'. */
-void freePlugin(ID pluginId, ID channelId);
+void freePlugin(const m::Plugin& plugin, ID channelId);
/* freePlugins
Unloads multiple plugins. Useful when freeing or deleting a channel. */
-void freePlugins(const std::vector<ID>& pluginIds);
+void freePlugins(const std::vector<Plugin*>& plugins);
/* clonePlugins
-Clones all the plug-ins from 'pluginIds' vector coming from the old channel
-and returns new IDs. */
+Clones all the plug-ins in the 'plugins' vector. */
-std::vector<ID> clonePlugins(std::vector<ID> pluginIds);
+std::vector<Plugin*> clonePlugins(const std::vector<Plugin*>& plugins);
void setPluginParameter(ID pluginId, int paramIndex, float value);
-
-void setPluginProgram(ID pluginId, int programIndex);
-
+void setPluginProgram(ID pluginId, int programIndex);
void toggleBypass(ID pluginId);
/* runDispatchLoop
Wakes up plugins' GUI manager for N milliseconds. */
void runDispatchLoop();
-}}} // giada::m::pluginHost::
-
+} // namespace giada::m::pluginHost
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <cassert>
-#include "utils/log.h"
-#include "utils/fs.h"
-#include "utils/string.h"
+#include "pluginManager.h"
+#include "core/conf.h"
#include "core/const.h"
#include "core/idManager.h"
+#include "core/model/model.h"
#include "core/patch.h"
-#include "core/conf.h"
#include "core/plugins/plugin.h"
-#include "pluginManager.h"
-
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include <cassert>
-namespace giada {
-namespace m {
-namespace pluginManager
+namespace giada::m::pluginManager
{
namespace
{
bool missingPlugins_;
-
std::unique_ptr<Plugin> makeInvalidPlugin_(const std::string& pid, ID id)
{
missingPlugins_ = true;
unknownPluginList_.push_back(pid);
- return std::make_unique<Plugin>(pluginId_.get(id), pid); // Invalid plug-in
+ return std::make_unique<Plugin>(pluginId_.generate(id), pid); // Invalid plug-in
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init(int samplerate, int buffersize)
{
pluginId_ = IdManager();
samplerate_ = samplerate;
- buffersize_ = buffersize;
+ buffersize_ = buffersize;
missingPlugins_ = false;
formatManager_.addDefaultFormats();
sortPlugins(static_cast<pluginManager::SortMethod>(conf::conf.pluginSortMethod));
}
-
/* -------------------------------------------------------------------------- */
-
int scanDirs(const std::string& dirs, const std::function<void(float)>& cb)
{
u::log::print("[pluginManager::scanDir] requested directories: '%s'\n", dirs);
u::log::print("[pluginManager::scanDir] current plugins: %d\n", knownPluginList_.getNumTypes());
- knownPluginList_.clear(); // clear up previous plugins
+ knownPluginList_.clear(); // clear up previous plugins
std::vector<std::string> dirVec = u::string::split(dirs, ";");
for (const std::string& dir : dirVec)
searchPath.add(juce::File(dir));
- for (int i = 0; i < formatManager_.getNumFormats(); i++) {
+ for (int i = 0; i < formatManager_.getNumFormats(); i++)
+ {
- juce::PluginDirectoryScanner scanner(knownPluginList_, *formatManager_.getFormat(i), searchPath,
- /*recursive=*/true, juce::File());
+ juce::PluginDirectoryScanner scanner(knownPluginList_, *formatManager_.getFormat(i), searchPath,
+ /*recursive=*/true, juce::File());
juce::String name;
- while (scanner.scanNextFile(false, name)) {
+ while (scanner.scanNextFile(false, name))
+ {
u::log::print("[pluginManager::scanDir] scanning '%s'\n", name.toRawUTF8());
cb(scanner.getProgress());
}
return knownPluginList_.getNumTypes();
}
-
/* -------------------------------------------------------------------------- */
-
bool saveList(const std::string& filepath)
{
bool out = knownPluginList_.createXml()->writeTo(juce::File(filepath));
return out;
}
-
/* -------------------------------------------------------------------------- */
-
bool loadList(const std::string& filepath)
{
std::unique_ptr<juce::XmlElement> elem(juce::XmlDocument::parse(juce::File(filepath)));
return true;
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Plugin> makePlugin(const std::string& pid, ID id)
{
/* Plug-in ID generator is updated anyway, as we store Plugin objects also
if they are in an invalid state. */
-
+
pluginId_.set(id);
const std::unique_ptr<juce::PluginDescription> pd = knownPluginList_.getTypeForIdentifierString(pid);
- if (pd == nullptr) {
+ if (pd == nullptr)
+ {
u::log::print("[pluginManager::makePlugin] no plugin found with pid=%s!\n", pid);
return makeInvalidPlugin_(pid, id);
}
- juce::String error;
+ juce::String error;
std::unique_ptr<juce::AudioPluginInstance> pi = formatManager_.createPluginInstance(*pd, samplerate_, buffersize_, error);
- if (pi == nullptr) {
+ if (pi == nullptr)
+ {
u::log::print("[pluginManager::makePlugin] unable to create instance with pid=%s! Error: %s\n",
- pid, error.toStdString());
+ pid, error.toStdString());
return makeInvalidPlugin_(pid, id);
}
u::log::print("[pluginManager::makePlugin] plugin instance with pid=%s created\n", pid);
- return std::make_unique<Plugin>(pluginId_.get(id), std::move(pi), samplerate_, buffersize_);
+ return std::make_unique<Plugin>(pluginId_.generate(id), std::move(pi), samplerate_, buffersize_);
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Plugin> makePlugin(int index)
{
juce::PluginDescription pd = knownPluginList_.getTypes()[index];
-
+
if (pd.uid == 0) // Invalid
return {};
-
+
u::log::print("[pluginManager::makePlugin] plugin found, uid=%s, name=%s...\n",
- pd.createIdentifierString().toRawUTF8(), pd.name.toRawUTF8());
-
- return makePlugin(pd.createIdentifierString().toStdString());
+ pd.createIdentifierString().toRawUTF8(), pd.name.toRawUTF8());
+ return makePlugin(pd.createIdentifierString().toStdString());
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Plugin> makePlugin(const Plugin& src)
{
std::unique_ptr<Plugin> p = makePlugin(src.getUniqueId());
-
- for (int i=0; i<src.getNumParameters(); i++)
- p->setParameter(i, src.getParameter(i));
+
+ for (int i = 0; i < src.getNumParameters(); i++)
+ p->setParameter(i, src.getParameter(i));
return p;
}
-
/* -------------------------------------------------------------------------- */
-
const patch::Plugin serializePlugin(const Plugin& p)
{
patch::Plugin pp;
return pp;
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Plugin> deserializePlugin(const patch::Plugin& p, patch::Version version)
{
std::unique_ptr<Plugin> plugin = makePlugin(p.path, p.id);
if (!plugin->valid)
return plugin; // Return invalid version
-
+
/* Fill plug-in parameters. */
plugin->setBypass(p.bypass);
-
+
if (version < patch::Version{0, 17, 0}) // TODO - to be removed in 0.18.0
- for (unsigned j=0; j<p.params.size(); j++)
+ for (unsigned j = 0; j < p.params.size(); j++)
plugin->setParameter(j, p.params.at(j));
else
plugin->setState(PluginState(p.state));
/* Fill plug-in MidiIn parameters. Don't fill Plugin::midiInParam if
Patch::midiInParams are zero: it would wipe out the current default 0x0
values. */
-
- if (!p.midiInParams.empty()) {
+
+ if (!p.midiInParams.empty())
+ {
plugin->midiInParams.clear();
std::size_t paramIndex = 0;
for (uint32_t midiInParam : p.midiInParams)
return plugin;
}
-
/* -------------------------------------------------------------------------- */
+std::vector<Plugin*> hydratePlugins(std::vector<ID> pluginIds)
+{
+ std::vector<Plugin*> out;
+ for (ID id : pluginIds)
+ {
+ Plugin* plugin = model::find<Plugin>(id);
+ if (plugin != nullptr)
+ out.push_back(plugin);
+ }
+ return out;
+}
+
+/* -------------------------------------------------------------------------- */
int countAvailablePlugins()
{
return knownPluginList_.getNumTypes();
}
-
/* -------------------------------------------------------------------------- */
-
int countUnknownPlugins()
{
return unknownPluginList_.size();
}
-
/* -------------------------------------------------------------------------- */
-
PluginInfo getAvailablePluginInfo(int i)
{
juce::PluginDescription pd = knownPluginList_.getTypes()[i];
- PluginInfo pi;
+ PluginInfo pi;
pi.uid = pd.fileOrIdentifier.toStdString();
pi.name = pd.descriptiveName.toStdString();
pi.category = pd.category.toStdString();
return pi;
}
-
/* -------------------------------------------------------------------------- */
-
bool hasMissingPlugins()
{
return missingPlugins_;
}
-
/* -------------------------------------------------------------------------- */
-
std::string getUnknownPluginInfo(int i)
{
return unknownPluginList_.at(i);
}
-
/* -------------------------------------------------------------------------- */
-
bool doesPluginExist(const std::string& pid)
{
return formatManager_.doesPluginStillExist(*knownPluginList_.getTypeForFile(pid));
}
-
/* -------------------------------------------------------------------------- */
-
void sortPlugins(SortMethod method)
{
- switch (method) {
- case SortMethod::NAME:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true);
- break;
- case SortMethod::CATEGORY:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByCategory, true);
- break;
- case SortMethod::MANUFACTURER:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true);
- break;
- case SortMethod::FORMAT:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByFormat, true);
- break;
+ switch (method)
+ {
+ case SortMethod::NAME:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true);
+ break;
+ case SortMethod::CATEGORY:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByCategory, true);
+ break;
+ case SortMethod::MANUFACTURER:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true);
+ break;
+ case SortMethod::FORMAT:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByFormat, true);
+ break;
}
}
-}}} // giada::m::pluginManager::
-
+} // namespace giada::m::pluginManager
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
#ifndef G_PLUGIN_MANAGER_H
#define G_PLUGIN_MANAGER_H
-
#include "deps/juce-config.h"
#include "plugin.h"
-
-namespace giada {
-namespace m
-{
-namespace patch
+namespace giada::m::patch
{
struct Plugin;
struct Version;
-}
-namespace pluginManager
+} // namespace giada::m::patch
+namespace giada::m::pluginManager
{
enum class SortMethod : int
{
- NAME = 0, CATEGORY, MANUFACTURER, FORMAT
+ NAME = 0,
+ CATEGORY,
+ MANUFACTURER,
+ FORMAT
};
struct PluginInfo
std::string category;
std::string manufacturerName;
std::string format;
- bool isInstrument;
+ bool isInstrument;
};
void init(int samplerate, int buffersize);
int countUnknownPlugins();
-std::unique_ptr<Plugin> makePlugin(const std::string& pid, ID id=0);
+std::unique_ptr<Plugin> makePlugin(const std::string& pid, ID id = 0);
std::unique_ptr<Plugin> makePlugin(int index);
std::unique_ptr<Plugin> makePlugin(const Plugin& other);
const patch::Plugin serializePlugin(const Plugin& p);
std::unique_ptr<Plugin> deserializePlugin(const patch::Plugin& p, patch::Version version);
+std::vector<Plugin*> hydratePlugins(std::vector<ID> pluginIds);
/* getAvailablePluginInfo
Returns the available plugin information (name, type, ...) given a plug-in
void sortPlugins(SortMethod sortMethod);
-}}} // giada::m::pluginManager::
-
+} // namespace giada::m::pluginManager
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#include <cassert>
#include "pluginState.h"
+#include "core/const.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
PluginState::PluginState(juce::MemoryBlock&& data)
: m_data(std::move(data))
/* -------------------------------------------------------------------------- */
-
PluginState::PluginState(const std::string& base64)
{
bool res = m_data.fromBase64Encoding(base64);
- assert(res);
+ if (!res)
+ G_DEBUG("Error while loading plug-in state!");
}
-
/* -------------------------------------------------------------------------- */
-
std::string PluginState::asBase64() const
{
return m_data.toBase64Encoding().toStdString();
}
-
/* -------------------------------------------------------------------------- */
-const void* PluginState::getData() const
+const void* PluginState::getData() const
{
return m_data.getData();
}
-
size_t PluginState::getSize() const
{
return m_data.getSize();
}
-}}
+} // namespace giada::m
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
#ifndef G_PLUGIN_STATE_H
#define G_PLUGIN_STATE_H
-
-#include <string>
#include "deps/juce-config.h"
+#include <string>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class PluginState
{
public:
+ PluginState() = default; // Invalid state
+ PluginState(juce::MemoryBlock&& data);
+ PluginState(const std::string& base64);
- PluginState(juce::MemoryBlock&& data);
- PluginState(const std::string& base64);
-
- std::string asBase64() const;
- const void* getData() const;
- size_t getSize() const;
+ std::string asBase64() const;
+ const void* getData() const;
+ size_t getSize() const;
private:
-
- juce::MemoryBlock m_data;
+ juce::MemoryBlock m_data;
};
-}}
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/clock.h"
#include "quantizer.h"
+#include "core/clock.h"
+#include <cassert>
-
-namespace giada {
-namespace m
+namespace giada::m
{
void Quantizer::trigger(int id)
{
- assert(id >= 0);
- assert(id < (int) m_callbacks.size());
+ assert(m_callbacks.count(id) > 0); // Make sure id exists
m_performId = id;
}
-
/* -------------------------------------------------------------------------- */
-
void Quantizer::schedule(int id, std::function<void(Frame delta)> f)
{
- assert(id >= 0);
- assert(id < (int) m_callbacks.size());
-
m_callbacks[id] = f;
}
-
/* -------------------------------------------------------------------------- */
-
void Quantizer::advance(Range<Frame> block, Frame quantizerStep)
{
/* Nothing to do if there's no action to perform. */
if (m_performId == -1)
return;
- assert(m_callbacks[m_performId] != nullptr);
+ assert(m_callbacks.count(m_performId) > 0);
- for (Frame global = block.getBegin(), local = 0; global < block.getEnd(); global++, local++) {
+ for (Frame global = block.getBegin(), local = 0; global < block.getEnd(); global++, local++)
+ {
- if (global % quantizerStep != 0) // Skip if it's not on a quantization unit.
+ if (global % quantizerStep != 0) // Skip if it's not on a quantization unit.
continue;
- m_callbacks[m_performId](local);
+ m_callbacks.at(m_performId)(local);
m_performId = -1;
return;
}
}
-
/* -------------------------------------------------------------------------- */
-
void Quantizer::clear()
{
m_performId = -1;
}
-
/* -------------------------------------------------------------------------- */
-
-bool Quantizer::isTriggered() const
+bool Quantizer::hasBeenTriggered() const
{
return m_performId != -1;
}
-}} // giada::m::
\ No newline at end of file
+} // namespace giada::m
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_QUANTIZER_H
#define G_QUANTIZER_H
-
-#include <array>
-#include <functional>
#include "core/const.h"
#include "core/range.h"
#include "core/types.h"
+#include <functional>
+#include <map>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Quantizer
{
public:
-
/* schedule
Schedules a function in slot 'id' to be called at the right time. The
function has a 'delta' parameter for the buffer offset. */
void clear();
- /* isTriggered
+ /* hasBeenTriggered
True if a quantizer function has been triggered(). */
-
- bool isTriggered() const;
-private:
+ bool hasBeenTriggered() const;
- std::array<std::function<void(Frame)>, G_MAX_QUANTIZER_SIZE> m_callbacks;
- int m_performId = -1;
+ private:
+ std::map<int, std::function<void(Frame)>> m_callbacks;
+ int m_performId = -1;
};
-}} // giada::m::
-
+} // namespace giada::m
#endif
*
* -------------------------------------------------------------------------- */
-
#ifndef G_QUEUE_H
#define G_QUEUE_H
-
#include <array>
#include <atomic>
-
-namespace giada {
+namespace giada
+{
namespace m
{
/* Queue
Single producer, single consumer lock-free queue. */
-template<typename T, std::size_t size>
+template <typename T, std::size_t size>
class Queue
{
public:
-
- Queue() : m_head(0), m_tail(0)
- {
- }
-
-
- Queue(const Queue&) = delete;
-
-
- bool pop(T& item)
- {
- std::size_t curr = m_head.load();
- if (curr == m_tail.load()) // Queue empty, nothing to do
- return false;
-
- item = m_data[curr];
- m_head.store(increment(curr));
- return true;
- }
-
-
- bool push(const T& item)
- {
- std::size_t curr = m_tail.load();
- std::size_t next = increment(curr);
-
- if (next == m_head.load()) // Queue full, nothing to do
- return false;
-
- m_data[curr] = item;
- m_tail.store(next);
- return true;
- }
-
-private:
-
- std::size_t increment(std::size_t i) const
- {
- return (i + 1) % size;
- }
-
-
- std::array<T, size> m_data;
- std::atomic<std::size_t> m_head;
- std::atomic<std::size_t> m_tail;
+ Queue()
+ : m_head(0)
+ , m_tail(0)
+ {
+ }
+
+ Queue(const Queue&) = delete;
+
+ bool pop(T& item)
+ {
+ std::size_t curr = m_head.load();
+ if (curr == m_tail.load()) // Queue empty, nothing to do
+ return false;
+
+ item = m_data[curr];
+ m_head.store(increment(curr));
+ return true;
+ }
+
+ bool push(const T& item)
+ {
+ std::size_t curr = m_tail.load();
+ std::size_t next = increment(curr);
+
+ if (next == m_head.load()) // Queue full, nothing to do
+ return false;
+
+ m_data[curr] = item;
+ m_tail.store(next);
+ return true;
+ }
+
+ private:
+ std::size_t increment(std::size_t i) const
+ {
+ return (i + 1) % size;
+ }
+
+ std::array<T, size> m_data;
+ std::atomic<std::size_t> m_head;
+ std::atomic<std::size_t> m_tail;
};
-}} // giada::m::
-
+} // namespace m
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_RANGE_H
#define G_RANGE_H
-
#include <cassert>
-
namespace giada
{
-template<typename T>
+template <typename T>
class Range
{
public:
+ Range()
+ : m_a(0)
+ , m_b(0)
+ {
+ }
+ Range(T a, T b)
+ : m_a(a)
+ , m_b(b)
+ {
+ assert(a < b);
+ }
- Range() : m_a(0), m_b(0) {}
- Range(T a, T b) : m_a(a), m_b(b) { assert(a < b); }
-
- T getBegin() const { return m_a; }
- T getEnd() const { return m_b; }
+ T getBegin() const { return m_a; }
+ T getEnd() const { return m_b; }
T getLength() const { return m_b - m_a; }
-private:
+ bool contains(T t) const
+ {
+ return t >= m_a && t < m_b;
+ }
+ private:
T m_a;
T m_b;
};
-} // giada::
-
+} // namespace giada
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_RCU_LIST_H
-#define G_RCU_LIST_H
-
-
-#include <array>
-#include <cassert>
-#include <thread>
-#include <atomic>
-#include <iterator>
-
-
-namespace giada {
-namespace m
-{
-/* RCUList
-Single producer, multiple consumer (i.e. one writer, many readers) RCU-based
-list. */
-
-template<typename T>
-class RCUList
-{
-public:
-
- /* Lock
- Scoped lock structure. Not copyable, not moveable, not copy-constructible.
- Same as std::scoped_lock:
- https://en.cppreference.com/w/cpp/thread/scoped_lock .*/
-
- struct Lock
- {
- Lock(RCUList<T>& r) : rcu(r) { rcu.lock(); }
- Lock(const Lock&) = delete;
- Lock& operator=(const Lock&) = delete;
- ~Lock() { rcu.unlock(); }
-
- RCUList<T>& rcu;
- };
-
- /* Node
- Element of the linked list. */
-
- struct Node
- {
- std::unique_ptr<T> data;
- std::atomic<Node*> next;
-
- Node(std::unique_ptr<T> data, Node* next=nullptr)
- : data(std::move(data)),
- next(next)
- {}
- };
-
- /* Iterator (const)
- This is based on simple, non-atomic pointers: you must always lock the RCU
- list before looping over it! */
-
- class Iterator : public std::iterator<std::forward_iterator_tag, Node*>
- {
- public:
-
- Iterator(Node* n) : m_curr(n) {}
-
- bool operator!= (const Iterator& o) const
- {
- return m_curr != o.m_curr;
- }
-
- bool operator== (const Iterator& o) const
- {
- return m_curr == o.m_curr;
- }
-
- const T* operator* () const
- {
- return m_curr->data.get();
- }
-
- // TODO - this non-const will go away with the non-virtual Channel
- // refactoring.
- T* operator* ()
- {
- return m_curr->data.get();
- }
-
- const Iterator& operator++ () // Prefix operator (++x)
- {
- if (m_curr != nullptr)
- m_curr = m_curr->next;
- return *this;
- }
-
- private:
-
- const Node* m_curr;
- };
-
- /* RCUList
- Singly linked list protected by a Read-Copy-Update (RCU) mechanism. */
-
- RCUList()
- : changed (false),
- m_grace (0),
- m_size (0),
- m_writing(false),
- m_head (nullptr),
- m_tail (nullptr)
- {
- m_readers[0].store(0);
- m_readers[1].store(0);
- }
-
- RCUList(std::unique_ptr<T> data) : RCUList()
- {
- push(std::move(data));
- }
-
- RCUList(const RCUList&) = delete;
- RCUList(RCUList&&) = delete;
-
- ~RCUList()
- {
- clear();
- }
-
- Iterator begin()
- {
- assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
- return Iterator(m_head.load());
- }
-
- Iterator end()
- {
- assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
- return Iterator(nullptr);
- }
-
- /* unlock
- Increases current readers count. Always call lock()/unlock() when reading
- data from the list. Or use the scoped version Lock above. */
-
- void lock()
- {
- t_grace = m_grace.load();
- m_readers[t_grace]++;
- }
-
- /* unlock
- Releases current readers count. */
-
- void unlock()
- {
- m_readers[t_grace]--;
- assert(m_readers[t_grace] >= 0 && "Negative reader");
- }
-
- /* get
- Returns a reference to the data held by node 'i'. */
-
- T* get(std::size_t i=0) const
- {
- assert(i < size() && "Index overflow");
- assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
- return getNode(i)->data.get();
- }
-
- /* Subscript operator []
- Same as above for the [] syntax. */
-
- T* operator[] (std::size_t i) const
- {
- return get(i);
- }
-
- /* back
- Return data held by the last node. */
-
- T* back() const
- {
- assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
- return m_tail.load()->data.get();
- }
-
- /* clone
- Returns a new copy of the data held by node 'i'. */
-
- std::unique_ptr<T> clone(std::size_t i=0) const
- {
- /* Make sure no one is writing (swapping, popping, pushing). */
- assert(m_writing.load() == false);
- return std::make_unique<T>(*getNode(i)->data.get());
- }
-
- /* swap
- Exchanges data contained in node 'i' with new data 'data'. New data must
- always come from a call to clone(). There is a natural protection against
- multiple calls to swap() made by the same thread: the caller is blocked by
- the spinlock below: no progress is made until m_readers[oldgrace] > 0. */
-
- void swap(std::unique_ptr<T> data, std::size_t i=0)
- {
- /* Never start two overlapping writing sessions. */
-
- if (m_writing.load() == true)
- return;
-
- /* Begin of writing session. */
-
- m_writing.store(true);
-
- /* Flip the current grace bit. Now we have entered a new grace period
- with a different number from the previous one. */
-
- std::int8_t oldgrace = m_grace.fetch_xor(1);
-
- /* Prepare useful node pointers: current, next and previous. Fetching
- from the current list with getNode() is safe here: we are just reading. */
-
- Node* curr = getNode(i);
- Node* prev = curr == m_head.load() ? nullptr : getNode(i - 1);
- Node* next = curr == m_tail.load() ? nullptr : getNode(i + 1);
-
- /* Prepare a new node holding the new data in input. */
-
- Node* n = new Node(std::move(data), next);
-
- /* Make the previous node point to the new one just created. New
- readers will read the new one from now on. The only write operation
- performed here is the atomic store. */
-
- if (prev != nullptr)
- prev->next.store(n);
- else
- m_head.store(n);
-
- if (next == nullptr)
- m_tail.store(n);
-
- /* Wait until no readers from the previous grace period are reading the
- list. Avoid brutal spinlock with a tiny sleep. */
-
- while (m_readers[oldgrace] > 0)
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
-
- /* Delete old node. Node destructor makes sure data is deleted. */
-
- delete curr;
-
- /* End of writing session. */
-
- m_writing.store(false);
- changed.store(true);
- }
-
- /* push
- Adds a new element to the list containing 'data'. */
-
- void push(std::unique_ptr<T> data)
- {
- /* Never start two overlapping writing sessions. */
-
- if (m_writing.load() == true)
- return;
-
- /* Begin of writing session. */
-
- m_writing.store(true);
-
- /* Create new node. */
-
- Node* n = new Node(std::move(data));
-
- /* Update the current tail->next pointer to this node, if a tail exists.
- I.e., grab the current last node and append it the new one. */
-
- if (m_tail.load() != nullptr)
- m_tail.load()->next.store(n);
-
- /* Update tail pointer to point to the new node. */
-
- m_tail.store(n);
-
- /* Head is null when the list is empty. If so, set head to this new
- node too. A list with only one node has both head and tail pointing
- to the same node. */
-
- if (m_head.load() == nullptr)
- m_head.store(n);
-
- /* Upgrade static size. Last thing to do, so that other threads won't
- read a false size. */
-
- m_size++;
-
- /* End of writing session. Data has changed, set the flag. */
-
- m_writing.store(false);
- changed.store(true);
- }
-
- /* pop
- Removes the i-th element. There is a natural protection against multiple
- calls to pop() made by the same thread: the caller is blocked by the
- spinlock below: no progress is made while m_readers[oldgrace] > 0. */
-
- void pop(std::size_t i)
- {
- /* Never start two overlapping writing sessions. */
-
- if (m_writing.load() == true)
- return;
-
- /* Begin of writing session. */
-
- m_writing.store(true);
-
- /* Flip the current grace bit. Now we have entered a new grace period
- with a different number from the previous one. */
-
- std::int8_t oldgrace = m_grace.fetch_xor(1);
-
- /* Prepare useful node pointers: current, next and previous. Fetching
- from the current list with getNode() is safe here: we are just reading. */
-
- Node* curr = getNode(i);
- Node* prev = curr == m_head.load() ? nullptr : getNode(i - 1);
- Node* next = curr == m_tail.load() ? nullptr : getNode(i + 1);
-
- /* Disconnect the previous node from the current one to be deleted:
- prev->curr->next becomes prev->next, with (curr) pending in the void
- for the remaining readers. Special care is needed if we are removing an
- edge node. */
-
- if (prev != nullptr)
- prev->next.store(next);
- else
- m_head.store(next);
-
- if (next == nullptr)
- m_tail.store(prev);
-
- /* Size can be updated at this point. */
-
- m_size--;
-
- /* Wait until no readers from the previous grace period are reading the
- list. Avoid brutal spinlock with a tiny sleep. */
-
- while (m_readers[oldgrace] > 0)
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
-
- /* Delete old node. Node destructor makes sure data is deleted. */
-
- delete curr;
-
- /* End of writing session. Data has changed, set the flag. */
-
- m_writing.store(false);
- changed.store(true);
- }
-
- /* clear
- Removes all nodes. */
-
- void clear()
- {
- /* Never start two overlapping writing sessions. */
-
- if (m_writing.load() == true)
- return;
-
- /* Begin of writing session. */
-
- m_writing.store(true);
-
- /* Flip the current grace bit. Now we have entered a new grace period
- with a different number from the previous one. */
-
- std::int8_t oldgrace = m_grace.fetch_xor(1);
-
- /* Store the first node locally. We will need it later on. */
-
- Node* current = m_head.load();
-
- /* Block any other reader by setting the size to 0 in advance and
- cleaning up head/tail pointers. */
-
- m_size.store(0);
- m_head.store(nullptr);
- m_tail.store(nullptr);
-
- while (current != nullptr) {
-
- /* Wait until no readers from the previous grace period are reading the
- list. Avoid brutal spinlock with a tiny sleep. */
-
- while (m_readers[oldgrace] > 0)
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
-
- /* Delete old node. Node destructor makes sure data is deleted. */
-
- Node* next = current->next;
- delete current;
- current = next;
- }
-
- /* End of writing session. Data has changed, set the flag. */
-
- m_writing.store(false);
- changed.store(true);
- }
-
- /* size
- Returns the number of nodes in the list. */
-
- std::size_t size() const
- {
- return m_size.load();
- }
-
- /* changed
- Tells whether the list has been altered with a swap, a push or a pop. */
-
- std::atomic<bool> changed;
-
- /* value_type
- A variable that holds the type of data contained in the list. Used for
- metaprogramming stuff. */
-
- using value_type = T;
-
-private:
-
- Node* getNode(std::size_t i) const
- {
- std::size_t p = 0;
- Node* curr = m_head.load();
-
- while (curr != nullptr && p < i) {
- p++;
- curr = curr->next.load();
- }
- assert(curr != nullptr);
- return curr;
- }
-
- std::array<std::atomic<int>, 2> m_readers;
- std::atomic<std::int8_t> m_grace;
- std::atomic<std::size_t> m_size;
- std::atomic<bool> m_writing;
-
- /* m_head
- Pointer to the first node. Used when reading and writing: always update
- this one first, as it is read by reader threads. */
-
- std::atomic<Node*> m_head;
-
- /* m_tail
- Pointer to the last node. Used only when reading: unlike m_head, updating it
- the right time is not that critical. */
-
- std::atomic<Node*> m_tail;
-
- /* t_grace
- Current grace flag. Each thread has its own copy of it (thread_local). */
-
- thread_local static int t_grace;
-};
-
-
-template<typename T>
-thread_local int RCUList<T>::t_grace = 0;
-}} // giada::m::
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "gui/dispatcher.h"
-#include "core/model/model.h"
-#include "core/types.h"
+#include "core/recManager.h"
#include "core/clock.h"
-#include "core/kernelAudio.h"
#include "core/conf.h"
+#include "core/kernelAudio.h"
+#include "core/midiDispatcher.h"
#include "core/mixer.h"
-#include "core/sequencer.h"
#include "core/mixerHandler.h"
-#include "core/midiDispatcher.h"
+#include "core/model/model.h"
#include "core/recorder.h"
#include "core/recorderHandler.h"
-#include "core/recManager.h"
-
+#include "core/sequencer.h"
+#include "core/types.h"
+#include "gui/dispatcher.h"
-namespace giada {
-namespace m {
-namespace recManager
+namespace giada::m::recManager
{
namespace
{
-void setRecordingAction_(bool v)
+bool isKernelReady_()
{
- model::onSwap(model::recorder, [&](model::Recorder& r)
- {
- r.isRecordingAction = v;
- });
+ return kernelAudio::isReady();
}
+bool canRec_()
+{
+ return isKernelReady_() && kernelAudio::isInputEnabled();
+}
-void setRecordingInput_(bool v)
+/* -------------------------------------------------------------------------- */
+
+void setRecordingAction_(bool v)
{
- model::onSwap(model::recorder, [&](model::Recorder& r)
- {
- r.isRecordingInput = v;
- });
+ model::get().recorder.isRecordingAction = v;
+ model::swap(model::SwapType::NONE);
}
+void setRecordingInput_(bool v)
+{
+ model::get().recorder.isRecordingInput = v;
+ model::swap(model::SwapType::NONE);
+}
/* -------------------------------------------------------------------------- */
-
bool startActionRec_()
{
- if (!kernelAudio::isReady())
- return false;
clock::setStatus(ClockStatus::RUNNING);
sequencer::start();
- m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
+ conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
return true;
}
-
/* -------------------------------------------------------------------------- */
-
-bool startInputRec_()
+void startInputRec_()
{
- if (!kernelAudio::isReady() || !mh::hasInputRecordableChannels())
- return false;
- mixer::startInputRec();
+ /* Start recording from the current frame, not the beginning. */
+ mixer::startInputRec(clock::getCurrentFrame());
sequencer::start();
- m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
- return true;
+ conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
bool isRecording()
-{
+{
return isRecordingAction() || isRecordingInput();
}
-
bool isRecordingAction()
-{
- model::RecorderLock lock(model::recorder);
- return model::recorder.get()->isRecordingAction;
+{
+ return model::get().recorder.isRecordingAction;
}
-
bool isRecordingInput()
-{
- model::RecorderLock lock(model::recorder);
- return model::recorder.get()->isRecordingInput;
+{
+ return model::get().recorder.isRecordingInput;
}
-
/* -------------------------------------------------------------------------- */
-
void startActionRec(RecTriggerMode mode)
{
- if (mode == RecTriggerMode::NORMAL) {
- if (startActionRec_())
- setRecordingAction_(true);
+ if (!isKernelReady_())
+ return;
+
+ if (mode == RecTriggerMode::NORMAL)
+ {
+ startActionRec_();
+ setRecordingAction_(true);
}
- else { // RecTriggerMode::SIGNAL
+ else
+ { // RecTriggerMode::SIGNAL
clock::setStatus(ClockStatus::WAITING);
clock::rewind();
- m::midiDispatcher::setSignalCallback(startActionRec_);
+ midiDispatcher::setSignalCallback(startActionRec_);
v::dispatcher::setSignalCallback(startActionRec_);
setRecordingAction_(true);
}
}
-
/* -------------------------------------------------------------------------- */
-
void stopActionRec()
{
setRecordingAction_(false);
/* If you stop the Action Recorder in SIGNAL mode before any actual
recording: just clean up everything and return. */
- if (clock::getStatus() == ClockStatus::WAITING) {
+ if (clock::getStatus() == ClockStatus::WAITING)
+ {
clock::setStatus(ClockStatus::STOPPED);
midiDispatcher::setSignalCallback(nullptr);
v::dispatcher::setSignalCallback(nullptr);
actions. Start reading right away, without checking whether
conf::treatRecsAsLoops is enabled or not. Same thing for MIDI channels. */
- for (ID id : channels) {
- model::onGet(model::channels, id, [](Channel& c)
- {
- c.state->readActions.store(true);
- if (c.getType() == ChannelType::MIDI)
- c.state->playStatus.store(ChannelStatus::PLAY);
- });
+ for (ID id : channels)
+ {
+ channel::Data& ch = model::get().getChannel(id);
+ ch.state->readActions.store(true);
+ if (ch.type == ChannelType::MIDI)
+ ch.state->playStatus.store(ChannelStatus::PLAY);
}
+ model::swap(model::SwapType::HARD);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleActionRec(RecTriggerMode m)
{
isRecordingAction() ? stopActionRec() : startActionRec(m);
}
-
/* -------------------------------------------------------------------------- */
-
-bool startInputRec(RecTriggerMode mode)
+bool startInputRec(RecTriggerMode triggerMode, InputRecMode inputMode)
{
- if (mode == RecTriggerMode::NORMAL) {
-G_DEBUG("Start input rec, NORMAL mode");
- if (!startInputRec_())
- return false;
+ if (!canRec_() || !mh::hasInputRecordableChannels())
+ return false;
+
+ if (triggerMode == RecTriggerMode::SIGNAL || inputMode == InputRecMode::FREE)
+ clock::rewind();
+
+ if (inputMode == InputRecMode::FREE)
+ mixer::setEndOfRecCallback([inputMode] { stopInputRec(inputMode); });
+
+ if (triggerMode == RecTriggerMode::NORMAL)
+ {
+ startInputRec_();
setRecordingInput_(true);
- return true;
+ G_DEBUG("Start input rec, NORMAL mode");
}
- else {
-G_DEBUG("Start input rec, SIGNAL mode");
- if (!mh::hasInputRecordableChannels())
- return false;
+ else
+ {
clock::setStatus(ClockStatus::WAITING);
- clock::rewind();
- mixer::setSignalCallback(startInputRec_);
- setRecordingInput_(true);
- return true;
+ mixer::setSignalCallback([] {
+ startInputRec_();
+ setRecordingInput_(true);
+ });
+ G_DEBUG("Start input rec, SIGNAL mode");
}
-}
+ return true;
+}
/* -------------------------------------------------------------------------- */
-
-void stopInputRec()
+void stopInputRec(InputRecMode recMode)
{
setRecordingInput_(false);
- mixer::stopInputRec();
-
+ Frame recordedFrames = mixer::stopInputRec();
+
+ /* When recording in RIGID mode, the amount of recorded frames is always
+ equal to the current loop length. */
+
+ if (recMode == InputRecMode::RIGID)
+ recordedFrames = clock::getFramesInLoop();
+
+ G_DEBUG("Stop input rec, recordedFrames=" << recordedFrames);
+
/* If you stop the Input Recorder in SIGNAL mode before any actual
recording: just clean up everything and return. */
- if (clock::getStatus() == ClockStatus::WAITING) {
+ if (clock::getStatus() == ClockStatus::WAITING)
+ {
clock::setStatus(ClockStatus::STOPPED);
mixer::setSignalCallback(nullptr);
+ return;
}
- else
- mh::finalizeInputRec();
-}
+ /* Finalize recordings. InputRecMode::FREE requires some adjustments. */
-/* -------------------------------------------------------------------------- */
+ mh::finalizeInputRec(recordedFrames);
+
+ if (recMode == InputRecMode::FREE)
+ {
+ clock::rewind();
+ clock::setBpm(clock::calcBpmFromRec(recordedFrames));
+ mixer::setEndOfRecCallback(nullptr);
+ refreshInputRecMode(); // Back to RIGID mode if necessary
+ }
+}
+/* -------------------------------------------------------------------------- */
-bool toggleInputRec(RecTriggerMode m)
+bool toggleInputRec(RecTriggerMode m, InputRecMode i)
{
- if (isRecordingInput()) {
- stopInputRec();
+ if (isRecordingInput())
+ {
+ stopInputRec(i);
return true;
}
- return startInputRec(m);
+ return startInputRec(m, i);
+}
+
+/* -------------------------------------------------------------------------- */
+
+bool canEnableRecOnSignal() { return !clock::isRunning(); }
+bool canEnableFreeInputRec() { return !mh::hasAudioData(); }
+
+void refreshInputRecMode()
+{
+ if (!canEnableFreeInputRec())
+ conf::conf.inputRecMode = InputRecMode::RIGID;
}
-}}} // giada::m::recManager
+} // namespace giada::m::recManager
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_REC_MANAGER_H
#define G_REC_MANAGER_H
-
#include "core/types.h"
-
-namespace giada {
-namespace m {
-namespace recManager
+namespace giada::m::recManager
{
bool isRecording();
bool isRecordingAction();
bool isRecordingInput();
-void startActionRec(RecTriggerMode m);
+void startActionRec(RecTriggerMode);
void stopActionRec();
-void toggleActionRec(RecTriggerMode m);
+void toggleActionRec(RecTriggerMode);
+
+bool startInputRec(RecTriggerMode, InputRecMode);
+void stopInputRec(InputRecMode);
+bool toggleInputRec(RecTriggerMode, InputRecMode);
+
+/* canEnableRecOnSignal
+True if rec-on-signal can be enabled: can't set it while sequencer is running,
+in order to prevent mistakes while live recording. */
+
+bool canEnableRecOnSignal();
+
+/* canEnableFreeInputRec
+True if free loop-length can be enabled: Can't set it if there's already a
+filled Sample Channel in the current project. */
+
+bool canEnableFreeInputRec();
+
+/* refreshInputRecMode
+Makes sure the input rec mode stays the right one when a new Sample Channel is
+filled with data. See canEnableFreeInputRec() rationale. */
-bool startInputRec(RecTriggerMode m);
-void stopInputRec();
-bool toggleInputRec(RecTriggerMode m);
-}}} // giada::m::recManager
+void refreshInputRecMode();
+} // namespace giada::m::recManager
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <memory>
-#include <algorithm>
-#include <cassert>
-#include "utils/log.h"
-#include "core/model/model.h"
+#include "core/recorder.h"
#include "core/action.h"
#include "core/idManager.h"
-#include "core/recorder.h"
-
+#include "core/model/model.h"
+#include "utils/log.h"
+#include <algorithm>
+#include <cassert>
+#include <memory>
-namespace giada {
-namespace m {
-namespace recorder
+namespace giada::m::recorder
{
namespace
{
IdManager actionId_;
-
/* -------------------------------------------------------------------------- */
-
Action* findAction_(ActionMap& src, ID id)
{
for (auto& [frame, actions] : src)
if (a.id == id)
return &a;
assert(false);
- return nullptr;
+ return nullptr;
}
+/* -------------------------------------------------------------------------- */
+
+/* updateMapPointers_
+Updates all prev/next actions pointers into the action map. This is required
+after an action has been recorded, since pushing back new actions in a Action
+vector makes it reallocating the existing ones. */
+
+void updateMapPointers_(ActionMap& src)
+{
+ for (auto& kv : src)
+ {
+ for (Action& action : kv.second)
+ {
+ if (action.nextId != 0)
+ action.next = findAction_(src, action.nextId);
+ if (action.prevId != 0)
+ action.prev = findAction_(src, action.prevId);
+ }
+ }
+}
/* -------------------------------------------------------------------------- */
it->second.size() == 0 ? it = map.erase(it) : ++it;
}
-
/* -------------------------------------------------------------------------- */
-
void removeIf_(std::function<bool(const Action&)> f)
{
- model::onSwap(model::actions, [&](model::Actions& a)
- {
- for (auto& [frame, actions] : a.map)
- actions.erase(std::remove_if(actions.begin(), actions.end(), f), actions.end());
- optimize_(a.map);
- updateMapPointers(a.map);
- });
+ model::DataLock lock;
+
+ ActionMap& map = model::getAll<model::Actions>();
+ for (auto& [frame, actions] : map)
+ actions.erase(std::remove_if(actions.begin(), actions.end(), f), actions.end());
+ optimize_(map);
+ updateMapPointers_(map);
}
/* -------------------------------------------------------------------------- */
-
bool exists_(ID channelId, Frame frame, const MidiEvent& event, const ActionMap& target)
{
for (const auto& [_, actions] : target)
- for (const Action& a : actions)
+ for (const Action& a : actions)
if (a.channelId == channelId && a.frame == frame && a.event.getRaw() == event.getRaw())
return true;
- return false;
+ return false;
}
-
bool exists_(ID channelId, Frame frame, const MidiEvent& event)
{
- model::ActionsLock lock(model::actions);
- return exists_(channelId, frame, event, model::actions.get()->map);
+ return exists_(channelId, frame, event, model::getAll<model::Actions>());
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init()
{
actionId_ = IdManager();
clearAll();
}
-
/* -------------------------------------------------------------------------- */
-
void clearAll()
{
- model::onSwap(model::actions, [&](model::Actions& a)
- {
- a.map.clear();
- });
+ model::DataLock lock;
+ model::getAll<model::Actions>().clear();
}
-
/* -------------------------------------------------------------------------- */
-
void clearChannel(ID channelId)
{
removeIf_([=](const Action& a) { return a.channelId == channelId; });
}
-
/* -------------------------------------------------------------------------- */
-
void clearActions(ID channelId, int type)
{
- removeIf_([=](const Action& a)
- {
+ removeIf_([=](const Action& a) {
return a.channelId == channelId && a.event.getStatus() == type;
});
}
-
/* -------------------------------------------------------------------------- */
-
void deleteAction(ID id)
{
removeIf_([=](const Action& a) { return a.id == id; });
}
-
void deleteAction(ID currId, ID nextId)
{
removeIf_([=](const Action& a) { return a.id == currId || a.id == nextId; });
}
-
/* -------------------------------------------------------------------------- */
-
void updateKeyFrames(std::function<Frame(Frame old)> f)
{
- std::unique_ptr<model::Actions> ma = model::actions.clone();
-
- /* Remove all existing actions: let's start from scratch.
- TODO - so why cloning it?! */
+ recorder::ActionMap temp;
- ma->map.clear();
-
- /* Copy all existing actions in local data by cloning them, with just a
+ /* Copy all existing actions in local map by cloning them, with just a
difference: they have a new frame value. */
+ for (const auto& [oldFrame, actions] : model::getAll<model::Actions>())
{
- model::ActionsLock lock(model::actions);
-
- for (const auto& [oldFrame, actions] : model::actions.get()->map) {
- Frame newFrame = f(oldFrame);
- for (const Action& a : actions) {
- Action copy = a;
- copy.frame = newFrame;
- ma->map[newFrame].push_back(copy);
- }
-G_DEBUG(oldFrame << " -> " << newFrame);
+ Frame newFrame = f(oldFrame);
+ for (const Action& a : actions)
+ {
+ Action copy = a;
+ copy.frame = newFrame;
+ temp[newFrame].push_back(copy);
}
+ G_DEBUG(oldFrame << " -> " << newFrame);
}
- updateMapPointers(ma->map);
+ updateMapPointers_(temp);
- model::actions.swap(std::move(ma));
+ model::DataLock lock;
+ model::getAll<model::Actions>() = std::move(temp);
}
-
/* -------------------------------------------------------------------------- */
-
void updateEvent(ID id, MidiEvent e)
{
- model::onSwap(model::actions, [&](model::Actions& a)
- {
- findAction_(a.map, id)->event = e;
- });
+ model::DataLock lock;
+ findAction_(model::getAll<model::Actions>(), id)->event = e;
}
-
/* -------------------------------------------------------------------------- */
-
void updateSiblings(ID id, ID prevId, ID nextId)
{
- model::onSwap(model::actions, [&](model::Actions& a)
+ model::DataLock lock;
+
+ Action* pcurr = findAction_(model::getAll<model::Actions>(), id);
+ Action* pprev = findAction_(model::getAll<model::Actions>(), prevId);
+ Action* pnext = findAction_(model::getAll<model::Actions>(), nextId);
+
+ pcurr->prev = pprev;
+ pcurr->prevId = pprev->id;
+ pcurr->next = pnext;
+ pcurr->nextId = pnext->id;
+
+ if (pprev != nullptr)
{
- Action* pcurr = findAction_(a.map, id);
- Action* pprev = findAction_(a.map, prevId);
- Action* pnext = findAction_(a.map, nextId);
-
- pcurr->prev = pprev;
- pcurr->prevId = pprev->id;
- pcurr->next = pnext;
- pcurr->nextId = pnext->id;
-
- if (pprev != nullptr) {
- pprev->next = pcurr;
- pprev->nextId = pcurr->id;
- }
- if (pnext != nullptr) {
- pnext->prev = pcurr;
- pnext->prevId = pcurr->id;
- }
- });
+ pprev->next = pcurr;
+ pprev->nextId = pcurr->id;
+ }
+ if (pnext != nullptr)
+ {
+ pnext->prev = pcurr;
+ pnext->prevId = pcurr->id;
+ }
}
-
/* -------------------------------------------------------------------------- */
-
bool hasActions(ID channelId, int type)
{
- model::ActionsLock lock(model::actions);
-
- for (const auto& [frame, actions] : model::actions.get()->map)
+ for (const auto& [frame, actions] : model::getAll<model::Actions>())
for (const Action& a : actions)
if (a.channelId == channelId && (type == 0 || type == a.event.getStatus()))
return true;
return false;
}
-
/* -------------------------------------------------------------------------- */
-
Action makeAction(ID id, ID channelId, Frame frame, MidiEvent e)
{
- Action out {actionId_.get(id), channelId, frame, e, -1, -1};
+ Action out{actionId_.generate(id), channelId, frame, e, -1, -1};
actionId_.set(id);
return out;
}
-
Action makeAction(const patch::Action& a)
{
actionId_.set(a.id);
- return Action {a.id, a.channelId, a.frame, a.event, -1, -1, a.prevId,
- a.nextId};
+ return Action{a.id, a.channelId, a.frame, a.event, -1, -1, a.prevId,
+ a.nextId};
}
-
/* -------------------------------------------------------------------------- */
-
Action rec(ID channelId, Frame frame, MidiEvent event)
{
/* Skip duplicates. */
return {};
Action a = makeAction(0, channelId, frame, event);
-
+
/* If key frame doesn't exist yet, the [] operator in std::map is smart
enough to insert a new item first. No plug-in data for now. */
- model::onSwap(model::actions, [&](model::Actions& mas)
- {
- mas.map[frame].push_back(a);
- updateMapPointers(mas.map);
- });
+ model::DataLock lock;
+
+ model::getAll<model::Actions>()[frame].push_back(a);
+ updateMapPointers_(model::getAll<model::Actions>());
return a;
}
-
/* -------------------------------------------------------------------------- */
-
void rec(std::vector<Action>& actions)
{
if (actions.size() == 0)
return;
- model::onSwap(model::actions, [&](model::Actions& mas)
- {
- for (const Action& a : actions)
- if (!exists_(a.channelId, a.frame, a.event, mas.map))
- mas.map[a.frame].push_back(a);
- updateMapPointers(mas.map);
- });
-}
+ model::DataLock lock;
+ ActionMap& map = model::getAll<model::Actions>();
-/* -------------------------------------------------------------------------- */
+ for (const Action& a : actions)
+ if (!exists_(a.channelId, a.frame, a.event, map))
+ map[a.frame].push_back(a);
+ updateMapPointers_(map);
+}
+/* -------------------------------------------------------------------------- */
void rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2)
{
- model::onSwap(model::actions, [&](model::Actions& mas)
- {
- mas.map[f1].push_back(makeAction(0, channelId, f1, e1));
- mas.map[f2].push_back(makeAction(0, channelId, f2, e2));
+ model::DataLock lock;
- Action* a1 = findAction_(mas.map, mas.map[f1].back().id);
- Action* a2 = findAction_(mas.map, mas.map[f2].back().id);
- a1->nextId = a2->id;
- a2->prevId = a1->id;
+ ActionMap& map = model::getAll<model::Actions>();
- updateMapPointers(mas.map);
- });
-}
+ map[f1].push_back(makeAction(0, channelId, f1, e1));
+ map[f2].push_back(makeAction(0, channelId, f2, e2));
+ Action* a1 = findAction_(map, map[f1].back().id);
+ Action* a2 = findAction_(map, map[f2].back().id);
+ a1->nextId = a2->id;
+ a2->prevId = a1->id;
-/* -------------------------------------------------------------------------- */
+ updateMapPointers_(map);
+}
+/* -------------------------------------------------------------------------- */
const std::vector<Action>* getActionsOnFrame(Frame frame)
{
- model::ActionsLock lock(model::actions);
-
- if (model::actions.get()->map.count(frame) == 0)
+ if (model::getAll<model::Actions>().count(frame) == 0)
return nullptr;
- return &model::actions.get()->map.at(frame);
+ return &model::getAll<model::Actions>().at(frame);
}
-
/* -------------------------------------------------------------------------- */
-
Action getClosestAction(ID channelId, Frame f, int type)
{
Action out = {};
- forEachAction([&](const Action& a)
- {
+ forEachAction([&](const Action& a) {
if (a.event.getStatus() != type || a.channelId != channelId)
return;
if (!out.isValid() || (a.frame <= f && a.frame > out.frame))
return out;
}
-
/* -------------------------------------------------------------------------- */
-
std::vector<Action> getActionsOnChannel(ID channelId)
{
std::vector<Action> out;
- forEachAction([&](const Action& a)
- {
+ forEachAction([&](const Action& a) {
if (a.channelId == channelId)
out.push_back(a);
});
return out;
}
-
/* -------------------------------------------------------------------------- */
-
-void updateMapPointers(ActionMap& src)
-{
- for (auto& kv : src) {
- for (Action& action : kv.second) {
- if (action.nextId != 0)
- action.next = findAction_(src, action.nextId);
- if (action.prevId != 0)
- action.prev = findAction_(src, action.prevId);
- }
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void forEachAction(std::function<void(const Action&)> f)
{
- model::ActionsLock lock(model::actions);
-
- for (auto& [_, actions] : model::actions.get()->map)
+ for (auto& [_, actions] : model::getAll<model::Actions>())
for (const Action& action : actions)
f(action);
}
-
/* -------------------------------------------------------------------------- */
-
ID getNewActionId()
{
- return actionId_.get();
+ return actionId_.generate();
}
-}}} // giada::m::recorder::
+} // namespace giada::m::recorder
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_RECORDER_H
#define G_RECORDER_H
-
-#include <map>
-#include <vector>
-#include <functional>
-#include <memory>
-#include "core/types.h"
#include "core/action.h"
-#include "core/patch.h"
#include "core/midiEvent.h"
+#include "core/patch.h"
+#include "core/types.h"
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
-
-namespace giada {
-namespace m
-{
-namespace recorder
+namespace giada::m::recorder
{
using ActionMap = std::map<Frame, std::vector<Action>>;
/* hasActions
Checks if the channel has at least one action recorded. */
-bool hasActions(ID channelId, int type=0);
+bool hasActions(ID channelId, int type = 0);
/* makeAction
Makes a new action given some data. */
Action getClosestAction(ID channelId, Frame f, int type);
-/* updateMapPointers
-Updates all prev/next actions pointers into the action map. This is required
-after an action has been recorded, since pushing back new actions in a Action
-vector makes it reallocating the existing ones. Also needed in model::Data copy
-constructor. */
-
-void updateMapPointers(ActionMap& src);
-
/* getNewActionId
Returns a new action ID, internally generated. */
ID getNewActionId();
-
-}}} // giada::m::recorder::
-
+} // namespace giada::m::recorder
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <unordered_map>
-#include <algorithm>
-#include <cmath>
-#include <cassert>
-#include "utils/log.h"
-#include "utils/ver.h"
-#include "model/model.h"
-#include "recorder.h"
+#include "recorderHandler.h"
#include "action.h"
#include "clock.h"
#include "const.h"
+#include "model/model.h"
#include "patch.h"
-#include "recorderHandler.h"
-
+#include "recorder.h"
+#include "utils/log.h"
+#include "utils/ver.h"
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <unordered_map>
-namespace giada {
-namespace m {
-namespace recorderHandler
+namespace giada::m::recorderHandler
{
namespace
{
constexpr int MAX_LIVE_RECS_CHUNK = 128;
-std::vector<Action> recs_;
-
+std::vector<Action> recs_;
/* -------------------------------------------------------------------------- */
-
const Action* getActionPtrById_(int id, const recorder::ActionMap& source)
{
for (const auto& [_, actions] : source)
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
/* areComposite_
Composite: NOTE_ON + NOTE_OFF on the same note. */
bool areComposite_(const Action& a1, const Action& a2)
{
- return a1.event.getStatus() == MidiEvent::NOTE_ON &&
+ return a1.event.getStatus() == MidiEvent::NOTE_ON &&
a2.event.getStatus() == MidiEvent::NOTE_OFF &&
- a1.event.getNote() == a2.event.getNote() &&
+ a1.event.getNote() == a2.event.getNote() &&
a1.channelId == a2.channelId;
}
-
/* -------------------------------------------------------------------------- */
-
/* consolidate_
Given an action 'a1' tries to find the matching NOTE_OFF. This algorithm must
start searching from the element next to 'a1': since live actions are recorded
void consolidate_(const Action& a1, std::size_t i)
{
- for (auto it = recs_.begin() + i; it != recs_.end(); ++it) {
+ for (auto it = recs_.begin() + i; it != recs_.end(); ++it)
+ {
const Action& a2 = *it;
}
}
-
/* -------------------------------------------------------------------------- */
-
void consolidate_()
{
for (auto it = recs_.begin(); it != recs_.end(); ++it)
- consolidate_(*it, it - recs_.begin()); // Pass current index
+ consolidate_(*it, it - recs_.begin()); // Pass current index
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init()
{
recs_.reserve(MAX_LIVE_RECS_CHUNK);
}
-
/* -------------------------------------------------------------------------- */
-
bool isBoundaryEnvelopeAction(const Action& a)
{
assert(a.prev != nullptr);
return a.prev->frame > a.frame || a.next->frame < a.frame;
}
-
/* -------------------------------------------------------------------------- */
-
-void updateBpm(float oldval, float newval, int oldquanto)
+void updateBpm(float ratio, int quantizerStep)
{
- recorder::updateKeyFrames([=](Frame old)
- {
+ if (ratio == 1.0f)
+ return;
+
+ recorder::updateKeyFrames([=](Frame old) {
/* The division here cannot be precise. A new frame can be 44099 and the
quantizer set to 44100. That would mean two recs completely useless. So we
- compute a reject value ('scarto'): if it's lower than 6 frames the new frame
+ compute a reject value ('delta'): if it's lower than 6 frames the new frame
is collapsed with a quantized frame. FIXME - maybe 6 frames are too low. */
- Frame frame = static_cast<Frame>((old / newval) * oldval);
- if (frame != 0) {
- Frame delta = oldquanto % frame;
+ Frame frame = static_cast<Frame>(old * ratio);
+ if (frame != 0)
+ {
+ Frame delta = quantizerStep % frame;
if (delta > 0 && delta <= 6)
frame = frame + delta;
}
});
}
-
/* -------------------------------------------------------------------------- */
-
void updateSamplerate(int systemRate, int patchRate)
{
if (systemRate == patchRate)
return;
- float ratio = systemRate / (float) patchRate;
+ float ratio = systemRate / (float)patchRate;
recorder::updateKeyFrames([=](Frame old) { return floorf(old * ratio); });
}
-
/* -------------------------------------------------------------------------- */
-
bool cloneActions(ID channelId, ID newChannelId)
{
- bool cloned = false;
- std::vector<Action> actions;
+ bool cloned = false;
+ std::vector<Action> actions;
std::unordered_map<ID, ID> map; // Action ID mapper, old -> new
- recorder::forEachAction([&](const Action& a)
- {
+ recorder::forEachAction([&](const Action& a) {
if (a.channelId != channelId)
return;
-
+
ID newActionId = recorder::getNewActionId();
map.insert({a.id, newActionId});
/* Update nextId and prevId relationships given the new action ID. */
- for (Action& a : actions) {
- if (a.prevId != 0) a.prevId = map.at(a.prevId);
- if (a.nextId != 0) a.nextId = map.at(a.nextId);
+ for (Action& a : actions)
+ {
+ if (a.prevId != 0)
+ a.prevId = map.at(a.prevId);
+ if (a.nextId != 0)
+ a.nextId = map.at(a.nextId);
}
recorder::rec(actions);
return cloned;
}
-
/* -------------------------------------------------------------------------- */
-
void liveRec(ID channelId, MidiEvent e, Frame globalFrame)
{
assert(e.isNoteOnOff()); // Can't record any other kind of events for now
/* TODO - this might allocate on the MIDI thread */
if (recs_.size() >= recs_.capacity())
recs_.reserve(recs_.size() + MAX_LIVE_RECS_CHUNK);
-
+
recs_.push_back(recorder::makeAction(recorder::getNewActionId(), channelId, globalFrame, e));
}
-
/* -------------------------------------------------------------------------- */
-
std::unordered_set<ID> consolidate()
{
consolidate_();
return out;
}
-
/* -------------------------------------------------------------------------- */
-
void clearAllActions()
{
- /* TODO - disgusting */
- for (std::size_t i = 0; i < model::channels.size(); i++) {
- model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c)
- {
- c.state->hasActions = false;
- });
- }
+ for (channel::Data& ch : model::get().channels)
+ ch.hasActions = false;
+
+ model::swap(model::SwapType::HARD);
+
recorder::clearAll();
}
-
/* -------------------------------------------------------------------------- */
-
recorder::ActionMap deserializeActions(const std::vector<patch::Action>& pactions)
{
recorder::ActionMap out;
/* Second pass: fill in previous and next actions, if any. Is this the
fastest/smartest way to do it? Maybe not. Optimizations are welcome. */
- for (const patch::Action& paction : pactions) {
- if (paction.nextId == 0 && paction.prevId == 0)
+ for (const patch::Action& paction : pactions)
+ {
+ if (paction.nextId == 0 && paction.prevId == 0)
continue;
Action* curr = const_cast<Action*>(getActionPtrById_(paction.id, out));
assert(curr != nullptr);
- if (paction.nextId != 0) {
+ if (paction.nextId != 0)
+ {
curr->next = getActionPtrById_(paction.nextId, out);
assert(curr->next != nullptr);
}
- if (paction.prevId != 0) {
+ if (paction.prevId != 0)
+ {
curr->prev = getActionPtrById_(paction.prevId, out);
assert(curr->prev != nullptr);
}
return out;
}
-
/* -------------------------------------------------------------------------- */
-
std::vector<patch::Action> serializeActions(const recorder::ActionMap& actions)
{
std::vector<patch::Action> out;
- for (const auto& kv : actions) {
- for (const Action& a : kv.second) {
+ for (const auto& kv : actions)
+ {
+ for (const Action& a : kv.second)
+ {
out.push_back({
- a.id,
- a.channelId,
- a.frame,
- a.event.getRaw(),
- a.prevId,
- a.nextId,
+ a.id,
+ a.channelId,
+ a.frame,
+ a.event.getRaw(),
+ a.prevId,
+ a.nextId,
});
}
}
- return out;
+ return out;
}
-
-}}} // giada::m::recorderHandler::
-
-
+} // namespace giada::m::recorderHandler
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_RECORDER_HANDLER_H
#define G_RECORDER_HANDLER_H
-
+#include "core/midiEvent.h"
+#include "core/recorder.h"
+#include "core/types.h"
#include <unordered_set>
-#include "midiEvent.h"
-
-namespace giada {
-namespace m
-{
-namespace patch
+namespace giada::m::patch
{
struct Action;
}
+namespace giada::m
+{
struct Action;
-namespace recorderHandler
+}
+namespace giada::m::recorderHandler
{
void init();
/* updateBpm
Changes actions position by calculating the new bpm value. */
-void updateBpm(float oldval, float newval, int oldquanto);
+void updateBpm(float ratio, int quantizerStep);
/* updateSamplerate
Changes actions position by taking in account the new samplerate. If
/* (de)serializeActions
Creates new Actions given the patch raw data and vice versa. */
-recorder::ActionMap deserializeActions(const std::vector<patch::Action>& as);
+recorder::ActionMap deserializeActions(const std::vector<patch::Action>& as);
std::vector<patch::Action> serializeActions(const recorder::ActionMap& as);
-}}} // giada::m::recorderHandler::
-
+} // namespace giada::m::recorderHandler
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "core/resampler.h"
+#include <algorithm>
+#include <cassert>
+#include <new>
+#include <utility>
+
+#include <cstdio>
+
+namespace giada::m
+{
+Resampler::Resampler()
+: m_state(nullptr)
+, m_input(nullptr)
+, m_inputPos(0)
+, m_inputLength(0)
+, m_channels(0)
+, m_usedFrames(0)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
+Resampler::Resampler(Quality quality, int channels)
+: Resampler()
+{
+ alloc(quality, channels);
+}
+
+/* -------------------------------------------------------------------------- */
+
+Resampler::Resampler(const Resampler& o)
+: Resampler()
+{
+ *this = o;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* This is a fake move constructor that makes a copy instead. The SRC_STATE
+object has a callback that, if moved, would still point to the original object.
+TODO: maybe delete the move constructor? */
+
+Resampler::Resampler(Resampler&& o)
+: Resampler()
+{
+ *this = o;
+}
+
+/* -------------------------------------------------------------------------- */
+
+Resampler& Resampler::operator=(const Resampler& o)
+{
+ if (this == &o)
+ return *this;
+ alloc(o.m_quality, o.m_channels);
+ return *this;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* This is a fake move operator: see notes above. */
+
+Resampler& Resampler::operator=(Resampler&& o)
+{
+ if (this == &o)
+ return *this;
+ alloc(o.m_quality, o.m_channels);
+ return *this;
+}
+
+/* -------------------------------------------------------------------------- */
+
+Resampler::~Resampler()
+{
+ src_delete(m_state);
+}
+
+/* -------------------------------------------------------------------------- */
+
+long Resampler::callback(void* self, float** audio)
+{
+ return static_cast<Resampler*>(self)->callback(audio);
+}
+
+/* -------------------------------------------------------------------------- */
+
+long Resampler::callback(float** audio)
+{
+ assert(audio != nullptr);
+
+ /* Move pointer properly, taking into account read data and number of
+ channels in input data. */
+
+ *audio = m_input + (m_inputPos * m_channels);
+
+ /* Returns how many frames have been read in this callback shot. */
+
+ long frames;
+
+ /* Read in CHUNK_LEN parts, checking if there is enough data left. */
+
+ if (m_inputPos + CHUNK_LEN < m_inputLength)
+ frames = CHUNK_LEN;
+ else
+ frames = m_inputLength - m_inputPos;
+
+ m_usedFrames += frames;
+ m_inputPos += frames;
+
+ return frames;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Resampler::alloc(Quality quality, int channels)
+{
+ if (m_state != nullptr)
+ src_delete(m_state);
+ m_state = src_callback_new(callback, static_cast<int>(quality), channels, nullptr, this);
+ m_quality = quality;
+ m_channels = channels;
+ if (m_state == nullptr)
+ throw std::bad_alloc();
+ src_reset(m_state);
+}
+
+/* -------------------------------------------------------------------------- */
+
+Resampler::Result Resampler::process(float* input, long inputPos, long inputLength,
+ float* output, long outputLength, float ratio)
+{
+ assert(m_state != nullptr); // Must be initialized first!
+
+ m_input = input;
+ m_inputPos = inputPos;
+ m_inputLength = inputLength;
+ m_usedFrames = 0;
+
+ long generated = src_callback_read(m_state, 1 / ratio, outputLength, output);
+
+ return {m_usedFrames, generated};
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Resampler::last()
+{
+ src_reset(m_state);
+}
+} // namespace giada::m
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_RESAMPLER_H
+#define G_RESAMPLER_H
+
+#include <cstddef>
+#include <samplerate.h>
+
+namespace giada::m
+{
+class Resampler final
+{
+public:
+ enum class Quality
+ {
+ SINC_BEST = 0,
+ SINC_MEDIUM = 1,
+ SINC_FASTEST = 2,
+ ZERO_ORDER_HOLD = 3,
+ LINEAR = 4
+ };
+
+ /* Result
+ A Result object is returned by the process() function below, containing the
+ number of frames used from input and generated to output. */
+
+ struct Result
+ {
+ long used, generated;
+ };
+
+ Resampler(); // Invalid
+ Resampler(Quality quality, int channels);
+ Resampler(const Resampler& o);
+ Resampler(Resampler&&);
+ Resampler& operator=(const Resampler&);
+ Resampler& operator=(Resampler&&);
+ ~Resampler();
+
+ /* process
+ Resamples a certain amount of frames from 'input' starting at 'inputPos' and
+ puts the result into 'output'. */
+
+ Result process(float* input, long inputPos, long inputLength, float* output,
+ long outputLength, float ratio);
+
+ /* last
+ Call this when you are about to process the last chunk of data. */
+
+ void last();
+
+private:
+ static long callback(void* self, float** audio);
+ long callback(float** audio);
+
+ void alloc(Quality quality, int channels);
+
+ /* CHUNK_LEN
+ How many chunks of data to read from input in the callback. */
+
+ static constexpr int CHUNK_LEN = 256;
+
+ SRC_STATE* m_state;
+ Quality m_quality;
+ float* m_input; // Pointer to input data
+ long m_inputPos; // Where to read from input
+ long m_inputLength; // Total number of frames in input data
+ int m_channels; // Number of channels
+ long m_usedFrames; // How many frames have been read from input with a process() call
+};
+} // namespace giada::m
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_RING_BUFFER_H
#define G_RING_BUFFER_H
-
#include <array>
-
namespace giada
{
/* RingBuffer
using iterator = typename std::array<T, S>::iterator;
using const_iterator = typename std::array<T, S>::const_iterator;
- iterator begin() { return m_data.begin(); }
- iterator end() { return m_data.begin() + m_end; }
- const_iterator begin() const { return m_data.begin(); }
- const_iterator end() const { return m_data.begin() + m_end; }
+ iterator begin() { return m_data.begin(); }
+ iterator end() { return m_data.begin() + m_end; }
+ const_iterator begin() const { return m_data.begin(); }
+ const_iterator end() const { return m_data.begin() + m_end; }
const_iterator cbegin() const { return m_data.begin(); }
- const_iterator cend() const { return m_data.begin() + m_end; }
+ const_iterator cend() const { return m_data.begin() + m_end; }
void clear()
{
void push_back(T t)
{
m_data[m_index] = t;
- m_index = (m_index + 1) % m_data.size(); // Wraps around at m_data.size()
- m_end = std::max(m_index, m_end); // Points to the greater index
+ m_index = (m_index + 1) % m_data.size(); // Wraps around at m_data.size()
+ m_end = std::max(m_index, m_end); // Points to the greater index
}
- constexpr std::size_t size() const noexcept
+ std::size_t size() const noexcept
{
- return m_data.size();
+ return m_end;
}
-private:
-
+ private:
std::array<T, S> m_data;
std::size_t m_index = 0;
std::size_t m_end = 0;
};
-} // giada::
-
+} // namespace giada
#endif
-
-
-
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/model/model.h"
+#include "sequencer.h"
+#include "core/clock.h"
+#include "core/conf.h"
#include "core/const.h"
+#include "core/kernelAudio.h"
+#include "core/metronome.h"
#include "core/mixer.h"
+#include "core/model/model.h"
#include "core/quantizer.h"
-#include "core/clock.h"
-#include "core/conf.h"
#include "core/recManager.h"
-#include "core/kernelAudio.h"
-#include "sequencer.h"
-
-namespace giada {
-namespace m {
-namespace sequencer
+namespace giada::m::sequencer
{
namespace
{
constexpr int Q_ACTION_REWIND = 0;
+/* eventBuffer_
+Buffer of events found in each block sent to channels for event parsing. This is
+filled during react(). */
-/* -------------------------------------------------------------------------- */
-
-
-struct Metronome
-{
- static constexpr Frame CLICK_SIZE = 38;
-
- float beat[CLICK_SIZE] = {
- 0.059033f, 0.117240f, 0.173807f, 0.227943f, 0.278890f, 0.325936f,
- 0.368423f, 0.405755f, 0.437413f, 0.462951f, 0.482013f, 0.494333f,
- 0.499738f, 0.498153f, 0.489598f, 0.474195f, 0.452159f, 0.423798f,
- 0.389509f, 0.349771f, 0.289883f, 0.230617f, 0.173194f, 0.118739f,
- 0.068260f, 0.022631f, -0.017423f, -0.051339f, -0.078721f, -0.099345f,
- -0.113163f, -0.120295f, -0.121028f, -0.115804f, -0.105209f, -0.089954f,
- -0.070862f, -0.048844f
- };
-
- float bar[CLICK_SIZE] = {
- 0.175860f, 0.341914f, 0.488904f, 0.608633f, 0.694426f, 0.741500f,
- 0.747229f, 0.711293f, 0.635697f, 0.524656f, 0.384362f, 0.222636f,
- 0.048496f, -0.128348f, -0.298035f, -0.451105f, -0.579021f, -0.674653f,
- -0.732667f, -0.749830f, -0.688924f, -0.594091f, -0.474481f, -0.340160f,
- -0.201360f, -0.067752f, 0.052194f, 0.151746f, 0.226280f, 0.273493f,
- 0.293425f, 0.288307f, 0.262252f, 0.220811f, 0.170435f, 0.117887f,
- 0.069639f, 0.031320f
- };
-
- Frame tracker = 0;
- bool running = false;
- bool playBar = false;
- bool playBeat = false;
-
- void render(AudioBuffer& outBuf, bool& process, float* data, Frame f)
- {
- process = true;
- for (int i=0; i<outBuf.countChannels(); i++)
- outBuf[f][i] += data[tracker];
- if (++tracker > Metronome::CLICK_SIZE) {
- process = false;
- tracker = 0;
- }
- }
-} metronome_;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void renderMetronome_(AudioBuffer& outBuf, Frame f)
-{
- if (!metronome_.running)
- return;
-
- if (clock::isOnBar() || metronome_.playBar)
- metronome_.render(outBuf, metronome_.playBar, metronome_.bar, f);
- else
- if (clock::isOnBeat() || metronome_.playBeat)
- metronome_.render(outBuf, metronome_.playBeat, metronome_.beat, f);
-}
+EventBuffer eventBuffer_;
+Metronome metronome_;
/* -------------------------------------------------------------------------- */
-
void rewindQ_(Frame delta)
{
clock::rewind();
- mixer::pumpEvent({ mixer::EventType::SEQUENCER_REWIND, delta, {} });
+ eventBuffer_.push_back({EventType::REWIND, 0, delta});
}
-
+} // namespace
/* -------------------------------------------------------------------------- */
-
-
-void start_()
-{
-#ifdef WITH_AUDIO_JACK
- if (kernelAudio::getAPI() == G_SYS_API_JACK)
- kernelAudio::jackStart();
- else
-#endif
- start();
-}
-
-
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void stop_()
-{
-#ifdef WITH_AUDIO_JACK
- if (kernelAudio::getAPI() == G_SYS_API_JACK)
- kernelAudio::jackStop();
- else
-#endif
- stop();
-}
-
+Quantizer quantizer;
/* -------------------------------------------------------------------------- */
-
-void rewind_()
+void init()
{
-#ifdef WITH_AUDIO_JACK
- if (kernelAudio::getAPI() == G_SYS_API_JACK)
- kernelAudio::jackSetPosition(0);
- else
-#endif
- rewind();
+ quantizer.schedule(Q_ACTION_REWIND, rewindQ_);
+ clock::rewind();
}
-
-/* -------------------------------------------------------------------------- */
-
-
-Quantizer quantizer_;
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void init()
+void react(const eventDispatcher::EventBuffer& events)
{
- quantizer_.schedule(Q_ACTION_REWIND, rewindQ_);
- clock::rewind();
+ for (const eventDispatcher::Event& e : events)
+ {
+ if (e.type == eventDispatcher::EventType::SEQUENCER_START)
+ {
+ start();
+ break;
+ }
+ if (e.type == eventDispatcher::EventType::SEQUENCER_STOP)
+ {
+ stop();
+ break;
+ }
+ if (e.type == eventDispatcher::EventType::SEQUENCER_REWIND)
+ {
+ rewind();
+ break;
+ }
+ }
}
-
/* -------------------------------------------------------------------------- */
-
-void run(Frame bufferSize)
+const EventBuffer& advance(Frame bufferSize)
{
- Frame start = clock::getCurrentFrame();
- Frame end = start + bufferSize;
- Frame total = clock::getFramesInLoop();
- Frame bar = clock::getFramesInBar();
+ eventBuffer_.clear();
- for (Frame i = start, local = 0; i < end; i++, local++) {
+ const Frame start = clock::getCurrentFrame();
+ const Frame end = start + bufferSize;
+ const Frame framesInLoop = clock::getFramesInLoop();
+ const Frame framesInBar = clock::getFramesInBar();
+ const Frame framesInBeat = clock::getFramesInBeat();
+
+ for (Frame i = start, local = 0; i < end; i++, local++)
+ {
- Frame global = i % total; // wraps around 'total'
+ Frame global = i % framesInLoop; // wraps around 'framesInLoop'
if (global == 0)
- mixer::pumpEvent({ mixer::EventType::SEQUENCER_FIRST_BEAT, local, { 0, 0, global, {} } });
- else
- if (global % bar == 0)
- mixer::pumpEvent({ mixer::EventType::SEQUENCER_BAR, local, { 0, 0, global, {} } });
+ {
+ eventBuffer_.push_back({EventType::FIRST_BEAT, global, local});
+ metronome_.trigger(Metronome::Click::BEAT, local);
+ }
+ else if (global % framesInBar == 0)
+ {
+ eventBuffer_.push_back({EventType::BAR, global, local});
+ metronome_.trigger(Metronome::Click::BAR, local);
+ }
+ else if (global % framesInBeat == 0)
+ {
+ metronome_.trigger(Metronome::Click::BEAT, local);
+ }
const std::vector<Action>* as = recorder::getActionsOnFrame(global);
if (as != nullptr)
- for (const Action& a : *as)
- mixer::pumpEvent({ mixer::EventType::ACTION, local, a });
+ eventBuffer_.push_back({EventType::ACTIONS, global, local, as});
}
- quantizer_.advance(Range<Frame>(start, end), clock::getQuantizerStep());
-}
-
+ /* Advance clock and quantizer after the event parsing. */
+ clock::advance(bufferSize);
+ quantizer.advance(Range<Frame>(start, end), clock::getQuantizerStep());
-/* -------------------------------------------------------------------------- */
-
-
-void parse(const mixer::EventBuffer& events)
-{
- for (const mixer::Event& e : events) {
- if (e.type == mixer::EventType::SEQUENCER_START) {
- start_(); break;
- }
- if (e.type == mixer::EventType::SEQUENCER_STOP) {
- stop_(); break;
- }
- if (e.type == mixer::EventType::SEQUENCER_REWIND_REQ) {
- rewind_(); break;
- }
- }
+ return eventBuffer_;
}
-
/* -------------------------------------------------------------------------- */
-
-void advance(AudioBuffer& outBuf)
+void render(AudioBuffer& outBuf)
{
- for (Frame i = 0; i < outBuf.countFrames(); i++) {
- clock::sendMIDIsync();
- clock::incrCurrentFrame();
- renderMetronome_(outBuf, i);
- }
+ if (metronome_.running)
+ metronome_.render(outBuf);
}
-
/* -------------------------------------------------------------------------- */
-
-void start()
+void rawStart()
{
- switch (clock::getStatus()) {
- case ClockStatus::STOPPED:
- clock::setStatus(ClockStatus::RUNNING);
- break;
- case ClockStatus::WAITING:
- clock::setStatus(ClockStatus::RUNNING);
- recManager::stopActionRec();
- break;
- default:
- break;
+ switch (clock::getStatus())
+ {
+ case ClockStatus::STOPPED:
+ clock::setStatus(ClockStatus::RUNNING);
+ break;
+ case ClockStatus::WAITING:
+ clock::setStatus(ClockStatus::RUNNING);
+ recManager::stopActionRec();
+ break;
+ default:
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
-void stop()
+void rawStop()
{
clock::setStatus(ClockStatus::STOPPED);
if (recManager::isRecordingAction())
recManager::stopActionRec();
- else
- if (recManager::isRecordingInput())
- recManager::stopInputRec();
+ else if (recManager::isRecordingInput())
+ recManager::stopInputRec(conf::conf.inputRecMode);
}
-
/* -------------------------------------------------------------------------- */
-
-void rewind()
+void rawRewind()
{
if (clock::canQuantize())
- quantizer_.trigger(Q_ACTION_REWIND);
+ quantizer.trigger(Q_ACTION_REWIND);
else
rewindQ_(/*delta=*/0);
}
+/* -------------------------------------------------------------------------- */
+
+void start()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackStart();
+ else
+#endif
+ rawStart();
+}
/* -------------------------------------------------------------------------- */
+void stop()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackStop();
+ else
+#endif
+ rawStop();
+}
-bool isMetronomeOn() { return metronome_.running; }
-void toggleMetronome() { metronome_.running = !metronome_.running; }
-void setMetronome(bool v) { metronome_.running = v; }
-}}} // giada::m::sequencer::
+/* -------------------------------------------------------------------------- */
+void rewind()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackSetPosition(0);
+ else
+#endif
+ rawRewind();
+}
+
+/* -------------------------------------------------------------------------- */
+bool isMetronomeOn() { return metronome_.running; }
+void toggleMetronome() { metronome_.running = !metronome_.running; }
+void setMetronome(bool v) { metronome_.running = v; }
+} // namespace giada::m::sequencer
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_SEQUENCER_H
#define G_SEQUENCER_H
+#include "core/eventDispatcher.h"
+#include "core/quantizer.h"
+#include <vector>
-#include "core/mixer.h"
-
-
-namespace giada {
-namespace m
+namespace giada::m
{
class AudioBuffer;
-namespace sequencer
+}
+namespace giada::m::sequencer
{
+enum class EventType
+{
+ NONE,
+ FIRST_BEAT,
+ BAR,
+ REWIND,
+ ACTIONS
+};
+
+struct Event
+{
+ EventType type = EventType::NONE;
+ Frame global = 0;
+ Frame delta = 0;
+ const std::vector<Action>* actions = nullptr;
+};
+
+using EventBuffer = RingBuffer<Event, G_MAX_SEQUENCER_EVENTS>;
+
+/* quantizer
+Used by the sequencer itself and each sample channel. */
+
+extern Quantizer quantizer;
+
void init();
-/* parse
+/* react
+Reacts to live events coming from the EventDispatcher (human events). */
+
+void react(const eventDispatcher::EventBuffer& e);
+
+/* advance
Parses sequencer events that might occur in a block and advances the internal
-quantizer. */
+quantizer. Returns a reference to the internal EventBuffer filled with events
+(if any). Call this on each new audio block. */
+
+const EventBuffer& advance(Frame bufferSize);
-void run(Frame bufferSize);
-void parse(const mixer::EventBuffer& events);
-void advance(AudioBuffer& outBuf);
+/* render
+Renders audio coming out from the sequencer: that is, the metronome! */
+
+void render(AudioBuffer& outBuf);
+
+/* raw[*]
+Raw functions to start, stop and rewind the sequencer. These functions must be
+called only by clock:: when the JACK signal is received. Other modules should
+use the non-raw versions below. */
+
+void rawStart();
+void rawStop();
+void rawRewind();
void start();
void stop();
bool isMetronomeOn();
void toggleMetronome();
void setMetronome(bool v);
-}}} // giada::m::sequencer::
-
+} // namespace giada::m::sequencer
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_SWAPPER_H
+#define G_SWAPPER_H
+
+#include <atomic>
+#include <functional>
+
+namespace giada
+{
+/* Swapper
+A template class that performs atomic double buffering on type T. */
+
+template <typename T>
+class Swapper
+{
+public:
+ class RtLock
+ {
+ friend Swapper;
+
+ public:
+ RtLock(Swapper& s)
+ : m_swapper(s)
+ {
+ m_swapper.rt_lock();
+ }
+
+ ~RtLock()
+ {
+ m_swapper.rt_unlock();
+ }
+
+ const T& get() const
+ {
+ return m_swapper.rt_get();
+ }
+
+ private:
+ Swapper& m_swapper;
+ };
+
+ Swapper()
+ {
+ static_assert(std::is_assignable_v<T, T>);
+ }
+
+ /* get
+ Returns local data for non-realtime thread. */
+
+ T& get()
+ {
+ return m_data[(m_bits.load() & BIT_INDEX) ^ 1];
+ }
+
+ void swap()
+ {
+ int bits = m_bits.load();
+
+ /* Wait for the audio thread to finish, i.e. until the BUSY bit becomes
+ zero. Only then, swap indexes. This will let the audio thread to pick
+ the updated data on its next cycle. */
+ int desired;
+ do
+ {
+ bits = bits & ~BIT_BUSY; // Expected: current value without busy bit set
+ desired = (bits ^ BIT_INDEX) & BIT_INDEX; // Desired: flipped (xor) index
+ } while (!m_bits.compare_exchange_weak(bits, desired));
+
+ bits = desired;
+
+ /* After the swap above, m_data[(bits & BIT_INDEX) ^ 1] has become the
+ non-realtime slot and it points to the data previously read by the
+ realtime thread. That data is old, so update it: overwrite it with the
+ realtime data in the realtime slot (m_data[bits & BIT_INDEX]) that is
+ currently being read by the realtime thread. */
+ m_data[(bits & BIT_INDEX) ^ 1] = m_data[bits & BIT_INDEX];
+ }
+
+ bool isLocked()
+ {
+ return m_bits.load() & BIT_BUSY;
+ }
+
+private:
+ static constexpr int BIT_INDEX = (1 << 0); // 0001
+ static constexpr int BIT_BUSY = (1 << 1); // 0010
+
+ /* [realtime] lock. */
+
+ void rt_lock()
+ {
+ /* Set the busy bit and also get the current index. */
+ m_index = m_bits.fetch_or(BIT_BUSY) & BIT_INDEX;
+ }
+
+ /* [realtime] unlock. */
+
+ void rt_unlock()
+ {
+ m_bits.store(m_index & BIT_INDEX);
+ }
+
+ /* [realtime] Get data currently being ready by the rt thread. */
+
+ const T& rt_get() const
+ {
+ return m_data[m_bits.load() & BIT_INDEX];
+ }
+
+ std::array<T, 2> m_data;
+ std::atomic<int> m_bits{0};
+ int m_index{0};
+};
+} // namespace giada
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_TYPES_H
#define G_TYPES_H
-
namespace giada
{
using ID = int;
using Pixel = int;
using Frame = int;
-enum class Thread { MAIN, MIDI, AUDIO };
+enum class Thread
+{
+ MAIN,
+ MIDI,
+ AUDIO,
+ EVENTS
+};
/* Windows fix */
#ifdef _WIN32
#undef VOID
#endif
-enum class ClockStatus { STOPPED, WAITING, RUNNING, ON_BEAT, ON_BAR, ON_FIRST_BEAT, VOID };
+enum class ClockStatus
+{
+ STOPPED,
+ WAITING,
+ RUNNING,
+ ON_BEAT,
+ ON_BAR,
+ ON_FIRST_BEAT,
+ VOID
+};
-enum class ChannelType : int { SAMPLE = 1, MIDI, MASTER, PREVIEW };
+enum class ChannelType : int
+{
+ SAMPLE = 1,
+ MIDI,
+ MASTER,
+ PREVIEW
+};
enum class ChannelStatus : int
{
- ENDING = 1, WAIT, PLAY, OFF, EMPTY, MISSING, WRONG
+ ENDING = 1,
+ WAIT,
+ PLAY,
+ OFF,
+ EMPTY,
+ MISSING,
+ WRONG
};
enum class SamplePlayerMode : int
{
- LOOP_BASIC = 1, LOOP_ONCE, LOOP_REPEAT, LOOP_ONCE_BAR,
- SINGLE_BASIC, SINGLE_PRESS, SINGLE_RETRIG, SINGLE_ENDLESS
+ LOOP_BASIC = 1,
+ LOOP_ONCE,
+ LOOP_REPEAT,
+ LOOP_ONCE_BAR,
+ SINGLE_BASIC,
+ SINGLE_PRESS,
+ SINGLE_RETRIG,
+ SINGLE_ENDLESS
};
-enum class RecTriggerMode : int { NORMAL = 0, SIGNAL };
+enum class RecTriggerMode : int
+{
+ NORMAL = 0,
+ SIGNAL
+};
-enum class EventType : int { AUTO = 0, MANUAL };
-}
+enum class InputRecMode : int
+{
+ RIGID = 0,
+ FREE
+};
+enum class EventType : int
+{
+ AUTO = 0,
+ MANUAL
+};
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <cstring> // memcpy
+#include "wave.h"
+#include "const.h"
#include "utils/fs.h"
#include "utils/log.h"
#include "utils/string.h"
-#include "const.h"
-#include "wave.h"
-
+#include <cassert>
-namespace giada {
-namespace m
+namespace giada::m
{
Wave::Wave(ID id)
-: id (id),
- m_rate (0),
- m_bits (0),
- m_logical(false),
- m_edited (false)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float* Wave::operator [](int offset) const
+: id(id)
+, m_rate(0)
+, m_bits(0)
+, m_logical(false)
+, m_edited(false)
{
- return buffer[offset];
}
-
/* -------------------------------------------------------------------------- */
-
Wave::Wave(const Wave& other)
-: id (other.id),
- m_rate (other.m_rate),
- m_bits (other.m_bits),
- m_logical (false),
- m_edited (false),
- m_path (other.m_path)
+: id(other.id)
+, m_buffer(other.getBuffer())
+, m_rate(other.m_rate)
+, m_bits(other.m_bits)
+, m_logical(false)
+, m_edited(false)
+, m_path(other.m_path)
{
- buffer.alloc(other.getSize(), other.getChannels());
- buffer.copyData(other.getFrame(0), other.getSize());
}
-
/* -------------------------------------------------------------------------- */
-
-void Wave::alloc(int size, int channels, int rate, int bits, const std::string& path)
+void Wave::alloc(Frame size, int channels, int rate, int bits, const std::string& path)
{
- buffer.alloc(size, channels);
+ m_buffer.alloc(size, channels);
m_rate = rate;
m_bits = bits;
m_path = path;
}
-
/* -------------------------------------------------------------------------- */
-
std::string Wave::getBasename(bool ext) const
{
return ext ? u::fs::basename(m_path) : u::fs::stripExt(u::fs::basename(m_path));
}
-
/* -------------------------------------------------------------------------- */
-
-int Wave::getRate() const { return m_rate; }
-int Wave::getChannels() const { return buffer.countChannels(); }
+int Wave::getRate() const { return m_rate; }
std::string Wave::getPath() const { return m_path; }
-int Wave::getSize() const { return buffer.countFrames(); }
-int Wave::getBits() const { return m_bits; }
-bool Wave::isLogical() const { return m_logical; }
-bool Wave::isEdited() const { return m_edited; }
-
+int Wave::getBits() const { return m_bits; }
+bool Wave::isLogical() const { return m_logical; }
+bool Wave::isEdited() const { return m_edited; }
/* -------------------------------------------------------------------------- */
+AudioBuffer& Wave::getBuffer() { return m_buffer; }
+const AudioBuffer& Wave::getBuffer() const { return m_buffer; }
+
+/* -------------------------------------------------------------------------- */
int Wave::getDuration() const
{
- return buffer.countFrames() / m_rate;
+ return m_buffer.countFrames() / m_rate;
}
-
/* -------------------------------------------------------------------------- */
-
std::string Wave::getExtension() const
{
return u::fs::getExt(m_path);
}
-
/* -------------------------------------------------------------------------- */
-
-float* Wave::getFrame(int f) const
-{
- return buffer[f];
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Wave::setRate(int v) { m_rate = v; }
+void Wave::setRate(int v) { m_rate = v; }
void Wave::setLogical(bool l) { m_logical = l; }
-void Wave::setEdited(bool e) { m_edited = e; }
-
+void Wave::setEdited(bool e) { m_edited = e; }
/* -------------------------------------------------------------------------- */
-
-void Wave::setPath(const std::string& p, int wid)
-{
+void Wave::setPath(const std::string& p, int wid)
+{
if (wid == -1)
- m_path = p;
- else
+ m_path = p;
+ else
m_path = u::fs::stripExt(p) + "-" + std::to_string(wid) + u::fs::getExt(p);
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void Wave::copyData(const float* data, int frames, int channels, int offset)
-{
- buffer.copyData(data, frames, channels, offset);
-}
-
-
-void Wave::copyData(const AudioBuffer& b) { buffer.copyData(b); }
-void Wave::addData(const AudioBuffer& b) { buffer.addData(b); }
-
-
/* -------------------------------------------------------------------------- */
-
-void Wave::moveData(AudioBuffer& b)
+void Wave::replaceData(AudioBuffer&& b)
{
- buffer.moveData(b);
+ m_buffer = std::move(b);
}
-}} // giada::m::
+} // namespace giada::m
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_WAVE_H
#define G_WAVE_H
-
-#include <string>
#include "core/audioBuffer.h"
#include "core/types.h"
+#include <string>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave
{
public:
-
Wave(ID id);
- Wave(const Wave& other);
+ Wave(const Wave& o);
+ Wave(Wave&& o) = default;
- float* operator [](int offset) const;
+ Wave& operator=(Wave&& o) = default;
- /* getFrame
- Works like operator []. See AudioBuffer for reference. */
-
- float* getFrame(int f) const;
-
- std::string getBasename(bool ext=false) const;
+ std::string getBasename(bool ext = false) const;
std::string getExtension() const;
- int getRate() const;
- int getChannels() const;
- std::string getPath() const;
- int getBits() const;
- int getSize() const; // in frames
- int getDuration() const;
- bool isLogical() const;
- bool isEdited() const;
+ int getRate() const;
+ std::string getPath() const;
+ int getBits() const;
+ int getDuration() const;
+ bool isLogical() const;
+ bool isEdited() const;
+
+ /* getBuffer
+ Returns a (non-)const reference to the underlying audio buffer. */
+
+ AudioBuffer& getBuffer();
+ const AudioBuffer& getBuffer() const;
/* setPath
Sets new path 'p'. If 'id' != -1 inserts a numeric id next to the file
extension, e.g. : /path/to/sample-[id].wav */
- void setPath(const std::string& p, int id=-1);
+ void setPath(const std::string& p, int id = -1);
void setRate(int v);
void setLogical(bool l);
void setEdited(bool e);
- /* moveData
- Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */
-
- void moveData(AudioBuffer& b);
-
- /* copyData
- Copies 'frames' frames from the new 'data' into m_data, starting from frame
- 'offset'. */
-
- void copyData(const float* data, int frames, int channels, int offset=0);
- void copyData(const AudioBuffer& b);
+ /* replaceData
+ Replaces internal audio buffer with 'b' by moving it. */
- /* addData
- Merges audio data from buffer 'b' onto this one. */
+ void replaceData(AudioBuffer&& b);
- void addData(const AudioBuffer& b);
-
- void alloc(int size, int channels, int rate, int bits, const std::string& path);
+ void alloc(Frame size, int channels, int rate, int bits, const std::string& path);
ID id;
private:
-
- AudioBuffer buffer;
- int m_rate;
- int m_bits;
- bool m_logical; // memory only (a take)
- bool m_edited; // edited via editor
- std::string m_path; // E.g. /path/to/my/sample.wav
+ AudioBuffer m_buffer;
+ int m_rate;
+ int m_bits;
+ bool m_logical; // memory only (a take)
+ bool m_edited; // edited via editor
+ std::string m_path; // E.g. /path/to/my/sample.wav
};
-}} // giada::m::
-
+} // namespace giada::m
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <cassert>
-#include <algorithm>
-#include "core/model/model.h"
-#include "utils/log.h"
+#include "waveFx.h"
#include "const.h"
+#include "utils/log.h"
#include "wave.h"
-#include "waveFx.h"
-
+#include <algorithm>
+#include <cassert>
+#include <cmath>
-namespace giada {
-namespace m {
-namespace wfx
+namespace giada::m::wfx
{
namespace
{
void fadeFrame_(Wave& w, int i, float val)
{
- for (int j=0; j<w.getChannels(); j++)
- w[i][j] *= val;
+ for (int j = 0; j < w.getBuffer().countChannels(); j++)
+ w.getBuffer()[i][j] *= val;
}
-
/* -------------------------------------------------------------------------- */
-
float getPeak_(const Wave& w, int a, int b)
{
float peak = 0.0f;
float abs = 0.0f;
- for (int i=a; i<b; i++) {
- for (int j=0; j<w.getChannels(); j++) // Find highest value in any channel
- abs = fabs(w[i][j]);
+ for (int i = a; i < b; i++)
+ {
+ for (int j = 0; j < w.getBuffer().countChannels(); j++) // Find highest value in any channel
+ abs = fabs(w.getBuffer()[i][j]);
if (abs > peak)
peak = abs;
}
return peak;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
constexpr int SMOOTH_SIZE = 32;
-
-void normalize(ID waveId, int a, int b)
+void normalize(Wave& w, int a, int b)
{
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- float peak = getPeak_(w, a, b);
- if (peak == 0.0f || peak > 1.0f)
- return;
+ float peak = getPeak_(w, a, b);
+ if (peak == 0.0f || peak > 1.0f)
+ return;
- for (int i=a; i<b; i++) {
- for (int j=0; j<w.getChannels(); j++)
- w[i][j] = w[i][j] * (1.0f / peak);
- }
- w.setEdited(true);
- });
+ for (int i = a; i < b; i++)
+ {
+ for (int j = 0; j < w.getBuffer().countChannels(); j++)
+ w.getBuffer()[i][j] = w.getBuffer()[i][j] * (1.0f / peak);
+ }
+ w.setEdited(true);
}
-
/* -------------------------------------------------------------------------- */
-
int monoToStereo(Wave& w)
{
- if (w.getChannels() >= G_MAX_IO_CHANS)
+ if (w.getBuffer().countChannels() >= G_MAX_IO_CHANS)
return G_RES_OK;
AudioBuffer newData;
- newData.alloc(w.getSize(), G_MAX_IO_CHANS);
+ newData.alloc(w.getBuffer().countFrames(), G_MAX_IO_CHANS);
- for (int i=0; i<newData.countFrames(); i++)
- for (int j=0; j<newData.countChannels(); j++)
- newData[i][j] = w[i][0];
+ for (int i = 0; i < newData.countFrames(); i++)
+ for (int j = 0; j < newData.countChannels(); j++)
+ newData[i][j] = w.getBuffer()[i][0];
- w.moveData(newData);
+ w.replaceData(std::move(newData));
return G_RES_OK;
}
-
/* -------------------------------------------------------------------------- */
-
-void silence(ID waveId, int a, int b)
+void silence(Wave& w, int a, int b)
{
u::log::print("[wfx::silence] silencing from %d to %d\n", a, b);
-
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- for (int i=a; i<b; i++)
- for (int j=0; j<w.getChannels(); j++)
- w[i][j] = 0.0f;
- w.setEdited(true);
- });
-}
+ for (int i = a; i < b; i++)
+ for (int j = 0; j < w.getBuffer().countChannels(); j++)
+ w.getBuffer()[i][j] = 0.0f;
+ w.setEdited(true);
+}
/* -------------------------------------------------------------------------- */
-
-void cut(ID waveId, int a, int b)
+void cut(Wave& w, int a, int b)
{
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- if (a < 0) a = 0;
- if (b > w.getSize()) b = w.getSize();
+ if (a < 0)
+ a = 0;
+ if (b > w.getBuffer().countFrames())
+ b = w.getBuffer().countFrames();
- /* Create a new temp wave and copy there the original one, skipping the a-b
- range. */
+ /* Create a new temp wave and copy there the original one, skipping the a-b
+ range. */
- int newSize = w.getSize() - (b - a);
+ int newSize = w.getBuffer().countFrames() - (b - a);
- AudioBuffer newData;
- newData.alloc(newSize, w.getChannels());
+ AudioBuffer newData;
+ newData.alloc(newSize, w.getBuffer().countChannels());
- u::log::print("[wfx::cut] cutting from %d to %d\n", a, b);
+ u::log::print("[wfx::cut] cutting from %d to %d\n", a, b);
- for (int i=0, k=0; i<w.getSize(); i++) {
- if (i < a || i >= b) {
- for (int j=0; j<w.getChannels(); j++)
- newData[k][j] = w[i][j];
- k++;
- }
+ for (int i = 0, k = 0; i < w.getBuffer().countFrames(); i++)
+ {
+ if (i < a || i >= b)
+ {
+ for (int j = 0; j < w.getBuffer().countChannels(); j++)
+ newData[k][j] = w.getBuffer()[i][j];
+ k++;
}
+ }
- w.moveData(newData);
- w.setEdited(true);
- });
+ w.replaceData(std::move(newData));
+ w.setEdited(true);
}
-
/* -------------------------------------------------------------------------- */
-
-void trim(ID waveId, int a, int b)
+void trim(Wave& w, Frame a, Frame b)
{
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- if (a < 0) a = 0;
- if (b > w.getSize()) b = w.getSize();
+ if (a < 0)
+ a = 0;
+ if (b > w.getBuffer().countFrames())
+ b = w.getBuffer().countFrames();
- int newSize = b - a;
+ Frame newSize = b - a;
- AudioBuffer newData;
- newData.alloc(newSize, w.getChannels());
+ AudioBuffer newData;
+ newData.alloc(newSize, w.getBuffer().countChannels());
- u::log::print("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
+ u::log::print("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b - a);
- for (int i=0; i<newData.countFrames(); i++)
- for (int j=0; j<newData.countChannels(); j++)
- newData[i][j] = w[i+a][j];
+ for (int i = 0; i < newData.countFrames(); i++)
+ for (int j = 0; j < newData.countChannels(); j++)
+ newData[i][j] = w.getBuffer()[i + a][j];
- w.moveData(newData);
- w.setEdited(true);
- });
+ w.replaceData(std::move(newData));
+ w.setEdited(true);
}
-
/* -------------------------------------------------------------------------- */
-
-void paste(const Wave& src, ID waveId, int a)
+void paste(const Wave& src, Wave& des, Frame a)
{
- model::onSwap(m::model::waves, waveId, [&](Wave& des)
- {
- assert(src.getChannels() == des.getChannels());
+ assert(src.getBuffer().countChannels() == des.getBuffer().countChannels());
- AudioBuffer newData;
- newData.alloc(src.getSize() + des.getSize(), des.getChannels());
+ AudioBuffer newData;
+ newData.alloc(src.getBuffer().countFrames() + des.getBuffer().countFrames(), des.getBuffer().countChannels());
- /* |---original data---|///paste data///|---original data---|
- des[0, a) src[0, src.size) des[a, des.size) */
+ /* |---original data---|///paste data///|---original data---|
+ des[0, a) src[0, src.size) des[a, des.size) */
- newData.copyData(des[0], a, 0);
- newData.copyData(src[0], src.getSize(), a);
- newData.copyData(des[a], des.getSize() - a, src.getSize() + a);
+ newData.set(des.getBuffer(), a, 0);
+ newData.set(src.getBuffer(), src.getBuffer().countFrames(), a);
+ newData.set(des.getBuffer(), des.getBuffer().countFrames() - a, src.getBuffer().countFrames() + a);
- des.moveData(newData);
- des.setEdited(true);
- });
+ des.replaceData(std::move(newData));
+ des.setEdited(true);
}
-
/* -------------------------------------------------------------------------- */
-
-void fade(ID waveId, int a, int b, Fade type)
+void fade(Wave& w, int a, int b, Fade type)
{
- u::log::print("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
+ u::log::print("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b - a);
float m = 0.0f;
- float d = 1.0f / (float) (b - a);
+ float d = 1.0f / (float)(b - a);
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- if (type == Fade::IN)
- for (int i=a; i<=b; i++, m+=d)
- fadeFrame_(w, i, m);
- else
- for (int i=b; i>=a; i--, m+=d)
- fadeFrame_(w, i, m);
-
- w.setEdited(true);
- });
-}
+ if (type == Fade::IN)
+ for (int i = a; i <= b; i++, m += d)
+ fadeFrame_(w, i, m);
+ else
+ for (int i = b; i >= a; i--, m += d)
+ fadeFrame_(w, i, m);
+ w.setEdited(true);
+}
/* -------------------------------------------------------------------------- */
-
-void smooth(ID waveId, int a, int b)
+void smooth(Wave& w, int a, int b)
{
/* Do nothing if fade edges (both of SMOOTH_SIZE samples) are > than selected
portion of wave. SMOOTH_SIZE*2 to count both edges. */
- if (SMOOTH_SIZE*2 > (b-a)) {
+ if (SMOOTH_SIZE * 2 > (b - a))
+ {
u::log::print("[wfx::smooth] selection is too small, nothing to do\n");
return;
}
- fade(waveId, a, a+SMOOTH_SIZE, Fade::IN);
- fade(waveId, b-SMOOTH_SIZE, b, Fade::OUT);
+ fade(w, a, a + SMOOTH_SIZE, Fade::IN);
+ fade(w, b - SMOOTH_SIZE, b, Fade::OUT);
}
-
/* -------------------------------------------------------------------------- */
-
-void shift(ID waveId, int offset)
+void shift(Wave& w, Frame offset)
{
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- if (offset < 0)
- offset = (w.getSize() + w.getChannels()) + offset;
+ if (offset < 0)
+ offset = (w.getBuffer().countFrames() + w.getBuffer().countChannels()) + offset;
- float* begin = w.getFrame(0);
- float* end = w.getFrame(0) + (w.getSize() * w.getChannels());
+ float* begin = w.getBuffer()[0];
+ float* end = w.getBuffer()[0] + (w.getBuffer().countFrames() * w.getBuffer().countChannels());
- std::rotate(begin, end - (offset * w.getChannels()), end);
- w.setEdited(true);
- });
+ std::rotate(begin, end - (offset * w.getBuffer().countChannels()), end);
+ w.setEdited(true);
}
-
/* -------------------------------------------------------------------------- */
-
-void reverse(ID waveId, int a, int b)
+void reverse(Wave& w, Frame a, Frame b)
{
/* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */
- model::onSwap(m::model::waves, waveId, [&](Wave& w)
- {
- float* begin = w.getFrame(0) + (a * w.getChannels());
- float* end = w.getFrame(0) + (b * w.getChannels());
+ float* begin = w.getBuffer()[0] + (a * w.getBuffer().countChannels());
+ float* end = w.getBuffer()[0] + (b * w.getBuffer().countChannels());
- std::reverse(begin, end);
+ std::reverse(begin, end);
- w.setEdited(true);
- });
+ w.setEdited(true);
}
-
-}}} // giada::m::wfx::
+} // namespace giada::m::wfx
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_WAVE_FX_H
#define G_WAVE_FX_H
-
#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave;
-namespace wfx
+}
+namespace giada::m::wfx
{
/* Windows fix */
#ifdef _WIN32
#undef IN
#undef OUT
#endif
-enum class Fade { IN, OUT };
+enum class Fade
+{
+ IN,
+ OUT
+};
/* monoToStereo
-Converts a 1-channel Wave to a 2-channels wave. It works on a free Wave object,
-not yet added to the RCUList. */
+Converts a 1-channel Wave to a 2-channels wave. */
int monoToStereo(Wave& w);
/* normalize
Normalizes the wave in range a-b by altering values in memory. */
-void normalize(ID waveId, int a, int b);
+void normalize(Wave& w, int a, int b);
-void silence(ID waveId, int a, int b);
-void cut(ID waveId, int a, int b);
-void trim(ID waveId, int a, int b);
+void silence(Wave& w, int a, int b);
+void cut(Wave& w, int a, int b);
+void trim(Wave& w, int a, int b);
/* paste
-Pastes Wave 'src' into Wave at 'waveIndex', starting from frame 'a'. */
+Pastes Wave 'src' into Wave 'dest', starting from frame 'a'. */
-void paste(const Wave& src, ID waveId, int a);
+void paste(const Wave& src, Wave& dest, Frame a);
/* fade
Fades in or fades out selection. Can be Fade::IN or Fade::OUT. */
-void fade(ID waveId, int a, int b, Fade type);
+void fade(Wave& w, int a, int b, Fade type);
/* smooth
Smooth edges of selection. */
-void smooth(ID waveId, int a, int b);
+void smooth(Wave& w, int a, int b);
/* reverse
Flips Wave's data. */
-void reverse(ID waveId, int a, int b);
-
-void shift(ID waveId, int offset);
-}}} // giada::m::wfx::
+void reverse(Wave& v, Frame a, Frame b);
+void shift(Wave& w, Frame offset);
+} // namespace giada::m::wfx
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <sndfile.h>
-#include <samplerate.h>
-#include "utils/log.h"
-#include "utils/fs.h"
+#include "waveManager.h"
#include "const.h"
#include "idManager.h"
-#include "wave.h"
+#include "model/model.h"
#include "patch.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "wave.h"
#include "waveFx.h"
-#include "waveManager.h"
-
+#include <cmath>
+#include <samplerate.h>
+#include <sndfile.h>
-namespace giada {
-namespace m {
-namespace waveManager
+namespace giada::m::waveManager
{
namespace
{
IdManager waveId_;
-
/* -------------------------------------------------------------------------- */
-
int getBits_(const SF_INFO& header)
{
- if (header.format & SF_FORMAT_PCM_S8)
+ if (header.format & SF_FORMAT_PCM_S8)
return 8;
else if (header.format & SF_FORMAT_PCM_16)
return 16;
return 64;
return 0;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void init()
{
waveId_ = IdManager();
}
-
/* -------------------------------------------------------------------------- */
-
Result createFromFile(const std::string& path, ID id, int samplerate, int quality)
{
- if (path == "" || u::fs::isDir(path)) {
+ if (path == "" || u::fs::isDir(path))
+ {
u::log::print("[waveManager::create] malformed path (was '%s')\n", path);
- return { G_RES_ERR_NO_DATA };
+ return {G_RES_ERR_NO_DATA};
}
if (path.size() > FILENAME_MAX)
- return { G_RES_ERR_PATH_TOO_LONG };
+ return {G_RES_ERR_PATH_TOO_LONG};
- SF_INFO header;
+ SF_INFO header;
SNDFILE* fileIn = sf_open(path.c_str(), SFM_READ, &header);
- if (fileIn == nullptr) {
+ if (fileIn == nullptr)
+ {
u::log::print("[waveManager::create] unable to read %s. %s\n", path, sf_strerror(fileIn));
- return { G_RES_ERR_IO };
+ return {G_RES_ERR_IO};
}
- if (header.channels > G_MAX_IO_CHANS) {
+ if (header.channels > G_MAX_IO_CHANS)
+ {
u::log::print("[waveManager::create] unsupported multi-channel sample\n");
- return { G_RES_ERR_WRONG_DATA };
+ return {G_RES_ERR_WRONG_DATA};
}
waveId_.set(id);
- std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get(id));
+ std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.generate(id));
wave->alloc(header.frames, header.channels, header.samplerate, getBits_(header), path);
- if (sf_readf_float(fileIn, wave->getFrame(0), header.frames) != header.frames)
+ if (sf_readf_float(fileIn, wave->getBuffer()[0], header.frames) != header.frames)
u::log::print("[waveManager::create] warning: incomplete read!\n");
sf_close(fileIn);
if (header.channels == 1 && !wfx::monoToStereo(*wave))
- return { G_RES_ERR_PROCESSING };
-
- if (wave->getRate() != samplerate) {
+ return {G_RES_ERR_PROCESSING};
+
+ if (wave->getRate() != samplerate)
+ {
u::log::print("[waveManager::create] input rate (%d) != required rate (%d), conversion needed\n",
- wave->getRate(), samplerate);
+ wave->getRate(), samplerate);
if (resample(*wave.get(), quality, samplerate) != G_RES_OK)
- return { G_RES_ERR_PROCESSING };
+ return {G_RES_ERR_PROCESSING};
}
- u::log::print("[waveManager::create] new Wave created, %d frames\n", wave->getSize());
+ u::log::print("[waveManager::create] new Wave created, %d frames\n", wave->getBuffer().countFrames());
- return { G_RES_OK, std::move(wave) };
+ return {G_RES_OK, std::move(wave)};
}
/* -------------------------------------------------------------------------- */
-
-std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
- const std::string& name)
+std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
+ const std::string& name)
{
- std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get());
+ std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.generate());
wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name);
wave->setLogical(true);
- u::log::print("[waveManager::createEmpty] new empty Wave created, %d frames\n",
- wave->getSize());
+ u::log::print("[waveManager::createEmpty] new empty Wave created, %d frames\n",
+ wave->getBuffer().countFrames());
return wave;
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b)
{
- int channels = src.getChannels();
+ int channels = src.getBuffer().countChannels();
int frames = b - a;
- std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get());
+ std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.generate());
wave->alloc(frames, channels, src.getRate(), src.getBits(), src.getPath());
- wave->copyData(src.getFrame(a), frames, channels);
+ wave->getBuffer().set(src.getBuffer(), frames);
wave->setLogical(true);
u::log::print("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
return wave;
}
-
/* -------------------------------------------------------------------------- */
-
std::unique_ptr<Wave> deserializeWave(const patch::Wave& w, int samplerate, int quality)
{
return createFromFile(w.path, w.id, samplerate, quality).wave;
}
-
const patch::Wave serializeWave(const Wave& w)
{
- return { w.id, u::fs::basename(w.getPath()) };
+ return {w.id, u::fs::basename(w.getPath())};
}
-
/* -------------------------------------------------------------------------- */
+Wave* hydrateWave(ID waveId)
+{
+ return model::find<Wave>(waveId);
+}
+
+/* -------------------------------------------------------------------------- */
int resample(Wave& w, int quality, int samplerate)
{
- float ratio = samplerate / (float) w.getRate();
- int newSizeFrames = static_cast<int>(ceil(w.getSize() * ratio));
+ float ratio = samplerate / (float)w.getRate();
+ int newSizeFrames = static_cast<int>(ceil(w.getBuffer().countFrames() * ratio));
AudioBuffer newData;
- newData.alloc(newSizeFrames, w.getChannels());
+ newData.alloc(newSizeFrames, w.getBuffer().countChannels());
SRC_DATA src_data;
- src_data.data_in = w.getFrame(0);
- src_data.input_frames = w.getSize();
+ src_data.data_in = w.getBuffer()[0];
+ src_data.input_frames = w.getBuffer().countFrames();
src_data.data_out = newData[0];
src_data.output_frames = newSizeFrames;
src_data.src_ratio = ratio;
u::log::print("[waveManager::resample] resampling: new size=%d frames\n", newSizeFrames);
- int ret = src_simple(&src_data, quality, w.getChannels());
- if (ret != 0) {
+ int ret = src_simple(&src_data, quality, w.getBuffer().countChannels());
+ if (ret != 0)
+ {
u::log::print("[waveManager::resample] resampling error: %s\n", src_strerror(ret));
return G_RES_ERR_PROCESSING;
}
- w.moveData(newData);
+ w.replaceData(std::move(newData));
w.setRate(samplerate);
return G_RES_OK;
}
-
/* -------------------------------------------------------------------------- */
-
int save(const Wave& w, const std::string& path)
{
SF_INFO header;
header.samplerate = w.getRate();
- header.channels = w.getChannels();
+ header.channels = w.getBuffer().countChannels();
header.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
SNDFILE* file = sf_open(path.c_str(), SFM_WRITE, &header);
- if (file == nullptr) {
+ if (file == nullptr)
+ {
u::log::print("[waveManager::save] unable to open %s for exporting: %s\n",
- path, sf_strerror(file));
+ path, sf_strerror(file));
return G_RES_ERR_IO;
}
- if (sf_writef_float(file, w.getFrame(0), w.getSize()) != w.getSize())
+ if (sf_writef_float(file, w.getBuffer()[0], w.getBuffer().countFrames()) != w.getBuffer().countFrames())
u::log::print("[waveManager::save] warning: incomplete write!\n");
sf_close(file);
return G_RES_OK;
}
-}}} // giada::m::waveManager
+} // namespace giada::m::waveManager
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_WAVE_MANAGER_H
#define G_WAVE_MANAGER_H
-
-#include <string>
-#include <memory>
#include "core/types.h"
+#include <memory>
+#include <string>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave;
-namespace patch
+}
+namespace giada::m::patch
{
struct Wave;
}
-namespace waveManager
+namespace giada::m::waveManager
{
struct Result
{
- int status;
- std::unique_ptr<Wave> wave = nullptr;
+ int status;
+ std::unique_ptr<Wave> wave = nullptr;
};
/* init
Initializes internal data. */
-
+
void init();
/* create
/* createEmpty
Creates a new silent Wave object. */
-std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
+std::unique_ptr<Wave> createEmpty(int frames, int channels, int samplerate,
const std::string& name);
/* createFromWave
std::unique_ptr<Wave> deserializeWave(const patch::Wave& w, int samplerate, int quality);
const patch::Wave serializeWave(const Wave& w);
+Wave* hydrateWave(ID waveId);
/* resample
Change sample rate of 'w' to the desider value. The 'quality' parameter sets the
algorithm to use for the conversion. */
-int resample(Wave& w, int quality, int samplerate);
+int resample(Wave& w, int quality, int samplerate);
/* save
Writes Wave data to file 'path'. Only 'wav' format is supported for now. */
int save(const Wave& w, const std::string& path);
-
-}}} // giada::m::waveManager
-
+} // namespace giada::m::waveManager
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_WEAK_ATOMIC_H
+#define G_WEAK_ATOMIC_H
+
+#include <atomic>
+
+namespace giada
+{
+template <typename T>
+class WeakAtomic
+{
+public:
+ WeakAtomic() = default;
+
+ WeakAtomic(T t)
+ : m_value(t)
+ {
+ }
+
+ WeakAtomic(const WeakAtomic& o)
+ : m_value(o.m_value.load(std::memory_order_relaxed))
+ {
+ }
+
+ WeakAtomic(WeakAtomic&& o)
+ : m_value(o.m_value.load(std::memory_order_relaxed), std::memory_order_relaxed)
+ {
+ }
+
+ WeakAtomic& operator=(const WeakAtomic& o)
+ {
+ if (this == &o)
+ return *this;
+ m_value.store(o.m_value.load(std::memory_order_relaxed), std::memory_order_relaxed);
+ return *this;
+ }
+
+ WeakAtomic& operator=(WeakAtomic&& o) = delete;
+
+ T load() const
+ {
+ return m_value.load(std::memory_order_relaxed);
+ }
+
+ void store(T t)
+ {
+ return m_value.store(t, std::memory_order_relaxed);
+ }
+
+ private:
+ std::atomic<T> m_value;
+};
+} // namespace giada
+
+#endif
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "worker.h"
+#include "utils/time.h"
+
+namespace giada
+{
+Worker::Worker()
+: m_running(false)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
+Worker::~Worker()
+{
+ stop();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Worker::start(std::function<void()> f, int sleep)
+{
+ m_running.store(true);
+ m_thread = std::thread([this, f, sleep]() {
+ while (m_running.load() == true)
+ {
+ f();
+ u::time::sleep(sleep);
+ }
+ });
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Worker::stop()
+{
+ m_running.store(false);
+ if (m_thread.joinable())
+ m_thread.join();
+}
+} // namespace giada
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_WORKER_H
+#define G_WORKER_H
+
+#include <atomic>
+#include <functional>
+#include <thread>
+
+namespace giada
+{
+class Worker
+{
+public:
+ Worker();
+ ~Worker();
+
+ void start(std::function<void()> f, int sleep);
+ void stop();
+
+ private:
+ std::thread m_thread;
+ std::atomic<bool> m_running;
+};
+} // namespace giada
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/model/model.h"
+#include "actionEditor.h"
+#include "core/action.h"
#include "core/clock.h"
#include "core/const.h"
-#include "core/recorderHandler.h"
+#include "core/model/model.h"
#include "core/recorder.h"
-#include "core/action.h"
+#include "core/recorderHandler.h"
#include "glue/events.h"
#include "glue/recorder.h"
-#include "actionEditor.h"
-
+#include <cassert>
-namespace giada {
-namespace c {
-namespace actionEditor
+namespace giada::c::actionEditor
{
namespace
{
Frame fixVerticalEnvActions_(Frame f, const m::Action& a1, const m::Action& a2)
{
- if (a1.frame == f) f += 1;
- else if (a2.frame == f) f -= 1;
+ if (a1.frame == f)
+ f += 1;
+ else if (a2.frame == f)
+ f -= 1;
if (a1.frame == f || a2.frame == f)
return -1;
return f;
}
-
/* -------------------------------------------------------------------------- */
/* recordFirstEnvelopeAction_
namespace mr = m::recorder;
// TODO - use MidiEvent(float)
- m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, G_MAX_VELOCITY);
- m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
- const m::Action a1 = mr::rec(channelId, 0, e1);
- const m::Action a2 = mr::rec(channelId, frame, e2);
+ m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, G_MAX_VELOCITY);
+ m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
+ const m::Action a1 = mr::rec(channelId, 0, e1);
+ const m::Action a2 = mr::rec(channelId, frame, e2);
const m::Action a3 = mr::rec(channelId, m::clock::getFramesInLoop() - 1, e1);
mr::updateSiblings(a1.id, /*prev=*/a3.id, /*next=*/a2.id); // Circular loop (begin)
mr::updateSiblings(a3.id, /*prev=*/a2.id, /*next=*/a1.id); // Circular loop (end)
}
-
/* -------------------------------------------------------------------------- */
-
/* recordNonFirstEnvelopeAction_
Find action right before frame 'frame' and inject a new action in there.
Vertical envelope points are forbidden. */
return;
// TODO - use MidiEvent(float)
- m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
+ m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
const m::Action a2 = mr::rec(channelId, frame, e2);
mr::updateSiblings(a2.id, a1.id, a3.id);
}
-
/* -------------------------------------------------------------------------- */
-
bool isSinglePressMode_(ID channelId)
{
- bool b;
- m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
- {
- b = c.samplePlayer->state->mode == SamplePlayerMode::SINGLE_PRESS;
- });
- return b;
+ /* TODO - use m::model getChannel utils (to be added) */
+ return m::model::get().getChannel(channelId).samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-SampleData::SampleData(const m::SamplePlayer& s)
-: channelMode(s.state->mode.load())
-, isLoopMode (s.state->isAnyLoopMode())
+SampleData::SampleData(const m::samplePlayer::Data& s)
+: channelMode(s.mode)
+, isLoopMode(s.isAnyLoopMode())
{
}
-
/* -------------------------------------------------------------------------- */
-
-Data::Data(const m::Channel& c)
-: channelId (c.id)
-, channelName(c.state->name)
-, actions (m::recorder::getActionsOnChannel(c.id))
+Data::Data(const m::channel::Data& c)
+: channelId(c.id)
+, channelName(c.name)
+, actions(m::recorder::getActionsOnChannel(c.id))
{
- if (c.getType() == ChannelType::SAMPLE)
- sample = std::make_optional<SampleData>(*c.samplePlayer);
+ if (c.type == ChannelType::SAMPLE)
+ sample = std::make_optional<SampleData>(c.samplePlayer.value());
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Data getData(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
- return Data(mm::get(mm::channels, channelId));
+ return Data(m::model::get().getChannel(channelId));
}
-
/* -------------------------------------------------------------------------- */
-
void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2)
{
namespace mr = m::recorder;
/* Avoid frame overflow. */
Frame overflow = f2 - (m::clock::getFramesInLoop());
- if (overflow > 0) {
+ if (overflow > 0)
+ {
f2 -= overflow;
f1 -= overflow;
}
- m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity);
+ m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity);
m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity);
mr::rec(channelId, f1, f2, e1, e2);
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void deleteMidiAction(ID channelId, const m::Action& a)
{
namespace mr = m::recorder;
/* Send a note-off first in case we are deleting it in a middle of a
key_on/key_off sequence. Check if 'next' exist first: could be orphaned. */
-
- if (a.next != nullptr) {
+
+ if (a.next != nullptr)
+ {
events::sendMidiToChannel(channelId, a.next->event, Thread::MAIN);
mr::deleteAction(a.id, a.next->id);
}
/* -------------------------------------------------------------------------- */
-
-void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity,
- Frame f1, Frame f2)
+void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity,
+ Frame f1, Frame f2)
{
namespace mr = m::recorder;
recordMidiAction(channelId, note, velocity, f1, f2);
}
-
/* -------------------------------------------------------------------------- */
-
void recordSampleAction(ID channelId, int type, Frame f1, Frame f2)
{
namespace mr = m::recorder;
- if (isSinglePressMode_(channelId)) {
+ if (isSinglePressMode_(channelId))
+ {
if (f2 == 0)
f2 = f1 + G_DEFAULT_ACTION_SIZE;
m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, 0, 0);
m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, 0, 0);
mr::rec(channelId, f1, f2, e1, e2);
}
- else {
+ else
+ {
m::MidiEvent e1 = m::MidiEvent(type, 0, 0);
mr::rec(channelId, f1, e1);
}
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
-void updateSampleAction(ID channelId, const m::Action& a, int type,
- Frame f1, Frame f2)
+void updateSampleAction(ID channelId, const m::Action& a, int type,
+ Frame f1, Frame f2)
{
- namespace mr = m::recorder;
+ namespace mr = m::recorder;
if (isSinglePressMode_(channelId))
mr::deleteAction(a.id, a.next->id);
recordSampleAction(channelId, type, f1, f2);
}
-
/* -------------------------------------------------------------------------- */
-
void deleteSampleAction(ID channelId, const m::Action& a)
{
namespace mr = m::recorder;
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void recordEnvelopeAction(ID channelId, Frame f, int value)
{
- namespace mr = m::recorder;
- namespace cr = c::recorder;
+ namespace mr = m::recorder;
+ namespace cr = c::recorder;
assert(value >= 0 && value <= G_MAX_VELOCITY);
/* First action ever? Add actions at boundaries. Else, find action right
before frame 'f' and inject a new action in there. Vertical envelope points
are forbidden for now. */
-
if (!mr::hasActions(channelId, m::MidiEvent::ENVELOPE))
recordFirstEnvelopeAction_(channelId, f, value);
- else
+ else
recordNonFirstEnvelopeAction_(channelId, f, value);
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void deleteEnvelopeAction(ID channelId, const m::Action& a)
{
namespace mr = m::recorder;
to restore _i and _d members in channel. */
/* TODO - move this to c::*/
/* TODO - FIX*/
- if (mrh::isBoundaryEnvelopeAction(a)) {
- if (a.isVolumeEnvelope()) {
+ if (mrh::isBoundaryEnvelopeAction(a))
+ {
+ if (a.isVolumeEnvelope())
+ {
/*
m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
{
}
mr::clearActions(channelId, a.event.getStatus());
}
- else {
+ else
+ {
assert(a.prev != nullptr);
assert(a.next != nullptr);
-
+
const m::Action a1 = *a.prev;
const m::Action a1prev = *a1.prev;
- const m::Action a3 = *a.next;
- const m::Action a3next = *a3.next;
+ const m::Action a3 = *a.next;
+ const m::Action a3next = *a3.next;
/* Original status: a1--->a--->a3
Modified status: a1-------->a3
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value)
{
namespace mr = m::recorder;
if (mrh::isBoundaryEnvelopeAction(a))
mr::updateEvent(a.id, m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value));
- else {
+ else
+ {
deleteEnvelopeAction(channelId, a);
recordEnvelopeAction(channelId, f, value);
}
}
-
/* -------------------------------------------------------------------------- */
-
std::vector<m::Action> getActions(ID channelId)
{
return m::recorder::getActionsOnChannel(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void updateVelocity(const m::Action& a, int value)
{
namespace mr = m::recorder;
-
+
m::MidiEvent event(a.event);
event.setVelocity(value);
mr::updateEvent(a.id, event);
}
-}}} // giada::c::actionEditor::
+} // namespace giada::c::actionEditor
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_ACTION_EDITOR_H
#define G_GLUE_ACTION_EDITOR_H
-
+#include "core/types.h"
#include <optional>
-#include <vector>
#include <string>
-#include "core/types.h"
-
+#include <vector>
-namespace giada {
-namespace m
+namespace giada::m
{
struct Action;
-class Channel;
-class SamplePlayer;
}
-namespace c {
-namespace actionEditor
+namespace giada::m::channel
+{
+struct Data;
+}
+namespace giada::m::samplePlayer
+{
+struct Data;
+}
+namespace giada::c::actionEditor
{
struct SampleData
{
- SampleData(const m::SamplePlayer&);
+ SampleData(const m::samplePlayer::Data&);
- SamplePlayerMode channelMode;
+ SamplePlayerMode channelMode;
bool isLoopMode;
};
struct Data
{
- Data() = default;
- Data(const m::Channel&);
+ Data() = default;
+ Data(const m::channel::Data&);
- ID channelId;
- std::string channelName;
+ ID channelId;
+ std::string channelName;
std::vector<m::Action> actions;
- std::optional<SampleData> sample;
+ std::optional<SampleData> sample;
};
Data getData(ID channelId);
/* MIDI actions. */
-void recordMidiAction(ID channelId, int note, int velocity, Frame f1,
- Frame f2=0);
+void recordMidiAction(ID channelId, int note, int velocity, Frame f1,
+ Frame f2 = 0);
void deleteMidiAction(ID channelId, const m::Action& a);
-void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity,
- Frame f1, Frame f2);
+void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity,
+ Frame f1, Frame f2);
void updateVelocity(const m::Action& a, int value);
/* Sample Actions. */
-void recordSampleAction(ID channelId, int type, Frame f1, Frame f2=0);
+void recordSampleAction(ID channelId, int type, Frame f1, Frame f2 = 0);
void deleteSampleAction(ID channelId, const m::Action& a);
-void updateSampleAction(ID channelId, const m::Action& a, int type,
- Frame f1, Frame f2=0);
+void updateSampleAction(ID channelId, const m::Action& a, int type,
+ Frame f1, Frame f2 = 0);
/* Envelope actions (only volume for now). */
void recordEnvelopeAction(ID channelId, Frame f, int value);
void deleteEnvelopeAction(ID channelId, const m::Action& a);
void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value);
-}}} // giada::c::actionEditor::
-
+} // namespace giada::c::actionEditor
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <functional>
-#include <cmath>
-#include <cassert>
-#include <FL/Fl.H>
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "channel.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/kernelAudio.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/plugins/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/recManager.h"
+#include "core/recorder.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/sampleEditor.h"
#include "gui/dialogs/warnings.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/dial.h"
-#include "gui/elems/sampleEditor/waveTools.h"
-#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/mainWindow/keyboard/channelButton.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
#include "gui/elems/sampleEditor/boostTool.h"
#include "gui/elems/sampleEditor/panTool.h"
#include "gui/elems/sampleEditor/pitchTool.h"
#include "gui/elems/sampleEditor/rangeTool.h"
+#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/sampleEditor/waveTools.h"
#include "gui/elems/sampleEditor/waveform.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
-#include "gui/elems/mainWindow/keyboard/channelButton.h"
-#include "utils/gui.h"
+#include "main.h"
#include "utils/fs.h"
+#include "utils/gui.h"
#include "utils/log.h"
-#include "core/model/model.h"
-#include "core/kernelAudio.h"
-#include "core/mixerHandler.h"
-#include "core/mixer.h"
-#include "core/clock.h"
-#include "core/plugins/pluginHost.h"
-#include "core/conf.h"
-#include "core/wave.h"
-#include "core/recorder.h"
-#include "core/recManager.h"
-#include "core/plugins/plugin.h"
-#include "core/waveManager.h"
-#include "main.h"
-#include "channel.h"
-
+#include <FL/Fl.H>
+#include <cassert>
+#include <cmath>
+#include <functional>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
-namespace channel
+namespace giada::c::channel
{
namespace
{
void printLoadError_(int res)
{
- if (res == G_RES_ERR_WRONG_DATA)
+ if (res == G_RES_ERR_WRONG_DATA)
v::gdAlert("Multichannel samples not supported.");
else if (res == G_RES_ERR_IO)
v::gdAlert("Unable to read this sample.");
else if (res == G_RES_ERR_NO_DATA)
v::gdAlert("No file specified.");
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-SampleData::SampleData(const m::SamplePlayer& s, const m::AudioReceiver& a)
-: waveId (s.getWaveId())
-, mode (s.state->mode.load())
-, isLoop (s.state->isAnyLoopMode())
-, pitch (s.state->pitch.load())
-, m_samplePlayer (&s)
-, m_audioReceiver(&a)
+// TODO - just pass const channel::Data&
+SampleData::SampleData(const m::channel::Data& ch)
+: waveId(ch.samplePlayer->getWaveId())
+, mode(ch.samplePlayer->mode)
+, isLoop(ch.samplePlayer->isAnyLoopMode())
+, pitch(ch.samplePlayer->pitch)
+, m_channel(&ch)
{
}
-
-Frame SampleData::a_getTracker() const { return a_get(m_samplePlayer->state->tracker); }
-Frame SampleData::a_getBegin() const { return a_get(m_samplePlayer->state->begin); }
-Frame SampleData::a_getEnd() const { return a_get(m_samplePlayer->state->end); }
-bool SampleData::a_getInputMonitor() const { return a_get(m_audioReceiver->state->inputMonitor); }
-bool SampleData::a_getOverdubProtection() const { return a_get(m_audioReceiver->state->overdubProtection); }
-
+Frame SampleData::getTracker() const { return m_channel->state->tracker.load(); }
+/* TODO - useless methods, turn them into member vars */
+Frame SampleData::getBegin() const { return m_channel->samplePlayer->begin; }
+Frame SampleData::getEnd() const { return m_channel->samplePlayer->end; }
+bool SampleData::getInputMonitor() const { return m_channel->audioReceiver->inputMonitor; }
+bool SampleData::getOverdubProtection() const { return m_channel->audioReceiver->overdubProtection; }
/* -------------------------------------------------------------------------- */
-
-MidiData::MidiData(const m::MidiSender& m)
-: m_midiSender(&m)
+MidiData::MidiData(const m::channel::Data& m)
+: m_channel(&m)
{
}
-bool MidiData::a_isOutputEnabled() const { return a_get(m_midiSender->state->enabled); }
-int MidiData::a_getFilter() const { return a_get(m_midiSender->state->filter); }
-
+/* TODO - useless methods, turn them into member vars */
+bool MidiData::isOutputEnabled() const { return m_channel->midiSender->enabled; }
+int MidiData::getFilter() const { return m_channel->midiSender->filter; }
/* -------------------------------------------------------------------------- */
-
-Data::Data(const m::Channel& c)
-: id (c.id)
-, columnId (c.getColumnId())
+Data::Data(const m::channel::Data& c)
+: id(c.id)
+, columnId(c.columnId)
#ifdef WITH_VST
-, pluginIds (c.pluginIds)
+, plugins(c.plugins)
#endif
-, type (c.getType())
-, height (c.state->height)
-, name (c.state->name)
-, volume (c.state->volume.load())
-, pan (c.state->pan.load())
-, key (c.state->key.load())
-, hasActions (c.state->hasActions)
-, m_channel (c)
+, type(c.type)
+, height(c.height)
+, name(c.name)
+, volume(c.volume)
+, pan(c.pan)
+, key(c.key)
+, hasActions(c.hasActions)
+, m_channel(c)
{
- if (c.getType() == ChannelType::SAMPLE)
- sample = std::make_optional<SampleData>(*c.samplePlayer, *c.audioReceiver);
- else
- if (c.getType() == ChannelType::MIDI)
- midi = std::make_optional<MidiData>(*c.midiSender);
+ if (c.type == ChannelType::SAMPLE)
+ sample = std::make_optional<SampleData>(c);
+ else if (c.type == ChannelType::MIDI)
+ midi = std::make_optional<MidiData>(c);
}
-
-bool Data::a_getSolo() const { return a_get(m_channel.state->solo); }
-bool Data::a_getMute() const { return a_get(m_channel.state->mute); }
-ChannelStatus Data::a_getPlayStatus() const { return a_get(m_channel.state->playStatus); }
-ChannelStatus Data::a_getRecStatus() const { return a_get(m_channel.state->recStatus); }
-bool Data::a_getReadActions() const { return a_get(m_channel.state->readActions); }
-bool Data::a_isArmed() const { return a_get(m_channel.state->armed); }
-bool Data::a_isRecordingInput() const { return m::recManager::isRecordingInput(); }
-bool Data::a_isRecordingAction() const { return m::recManager::isRecordingAction(); }
-
+ChannelStatus Data::getPlayStatus() const { return m_channel.state->playStatus.load(); }
+ChannelStatus Data::getRecStatus() const { return m_channel.state->recStatus.load(); }
+bool Data::getReadActions() const { return m_channel.state->readActions.load(); }
+bool Data::isRecordingInput() const { return m::recManager::isRecordingInput(); }
+bool Data::isRecordingAction() const { return m::recManager::isRecordingAction(); }
+/* TODO - useless methods, turn them into member vars */
+bool Data::getSolo() const { return m_channel.solo; }
+bool Data::getMute() const { return m_channel.mute; }
+bool Data::isArmed() const { return m_channel.armed; }
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Data getData(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
- return Data(mm::get(mm::channels, channelId));
+ return Data(m::model::get().getChannel(channelId));
}
-
std::vector<Data> getChannels()
{
- namespace mm = m::model;
- mm::ChannelsLock cl(mm::channels);
-
std::vector<Data> out;
- for (const m::Channel* ch : mm::channels)
- if (!ch->isInternal())
- out.push_back(Data(*ch));
-
+ for (const m::channel::Data& ch : m::model::get().channels)
+ if (!ch.isInternal())
+ out.push_back(Data(ch));
return out;
}
-
/* -------------------------------------------------------------------------- */
-
int loadChannel(ID channelId, const std::string& fname)
{
/* Save the patch and take the last browser's dir in order to re-use it the
int res = m::mh::loadChannel(channelId, fname);
if (res != G_RES_OK)
printLoadError_(res);
-
+
return res;
}
-
/* -------------------------------------------------------------------------- */
-
void addChannel(ID columnId, ChannelType type)
{
m::mh::addChannel(type, columnId);
}
-
/* -------------------------------------------------------------------------- */
-
void addAndLoadChannel(ID columnId, const std::string& fpath)
{
int res = m::mh::addAndLoadChannel(columnId, fpath);
printLoadError_(res);
}
-
void addAndLoadChannels(ID columnId, const std::vector<std::string>& fpaths)
{
if (fpaths.size() == 1)
v::gdAlert("Some files weren't loaded sucessfully.");
}
-
/* -------------------------------------------------------------------------- */
-
void deleteChannel(ID channelId)
{
if (!v::gdConfirmWin("Warning", "Delete channel: are you sure?"))
m::mh::deleteChannel(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void freeChannel(ID channelId)
{
if (!v::gdConfirmWin("Warning", "Free channel: are you sure?"))
m::mh::freeChannel(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void setInputMonitor(ID channelId, bool value)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.audioReceiver->state->inputMonitor.store(value);
- });
+ m::model::get().getChannel(channelId).audioReceiver->inputMonitor = value;
+ m::model::swap(m::model::SwapType::SOFT);
}
-
/* -------------------------------------------------------------------------- */
-
void setOverdubProtection(ID channelId, bool value)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.audioReceiver->state->overdubProtection.store(value);
- if (value == true && c.state->armed.load() == true)
- c.state->armed.store(false);
- });
+ m::channel::Data& ch = m::model::get().getChannel(channelId);
+ ch.audioReceiver->overdubProtection = value;
+ if (value == true && ch.armed)
+ ch.armed = false;
+ m::model::swap(m::model::SwapType::SOFT);
}
-
/* -------------------------------------------------------------------------- */
-
void cloneChannel(ID channelId)
{
m::mh::cloneChannel(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
-void setSamplePlayerMode(ID channelId, SamplePlayerMode m)
+void setSamplePlayerMode(ID channelId, SamplePlayerMode mode)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.samplePlayer->state->mode.store(m);
- });
-
- /* TODO - brutal rebuild! Just rebuild the specific channel instead */
- G_MainWin->keyboard->rebuild();
-
+ m::model::get().getChannel(channelId).samplePlayer->mode = mode;
+ m::model::swap(m::model::SwapType::HARD); // TODO - SOFT should be enough, fix geChannel refresh method
u::gui::refreshActionEditor();
}
-
/* -------------------------------------------------------------------------- */
-
void setHeight(ID channelId, Pixel p)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.state->height = p;
- });
+ m::model::get().getChannel(channelId).height = p;
+ m::model::swap(m::model::SwapType::SOFT);
}
-
/* -------------------------------------------------------------------------- */
-
void setName(ID channelId, const std::string& name)
{
m::mh::renameChannel(channelId, name);
}
-}}} // giada::c::channel::
+} // namespace giada::c::channel
- /* -----------------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_CHANNEL_H
#define G_GLUE_CHANNEL_H
-
-#include <optional>
+#include "core/model/model.h"
+#include "core/types.h"
#include <atomic>
+#include <optional>
#include <string>
#include <vector>
-#include "core/model/model.h"
-#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m
{
-class Channel;
-class SamplePlayer;
+class Plugin;
}
-namespace c {
-namespace channel
+namespace giada::c::channel
{
struct SampleData
{
SampleData() = delete;
- SampleData(const m::SamplePlayer&, const m::AudioReceiver&);
+ SampleData(const m::channel::Data&);
- Frame a_getTracker() const;
- Frame a_getBegin() const;
- Frame a_getEnd() const;
- bool a_getInputMonitor() const;
- bool a_getOverdubProtection() const;
+ Frame getTracker() const;
+ Frame getBegin() const;
+ Frame getEnd() const;
+ bool getInputMonitor() const;
+ bool getOverdubProtection() const;
ID waveId;
SamplePlayerMode mode;
bool isLoop;
float pitch;
-private:
-
- const m::SamplePlayer* m_samplePlayer;
- const m::AudioReceiver* m_audioReceiver;
+ private:
+ const m::channel::Data* m_channel;
};
struct MidiData
{
MidiData() = delete;
- MidiData(const m::MidiSender&);
+ MidiData(const m::channel::Data&);
- bool a_isOutputEnabled() const;
- int a_getFilter() const;
+ bool isOutputEnabled() const;
+ int getFilter() const;
-private:
-
- const m::MidiSender* m_midiSender;
+ private:
+ const m::channel::Data* m_channel;
};
struct Data
{
- Data(const m::Channel&);
-
- bool a_getMute() const;
- bool a_getSolo() const;
- ChannelStatus a_getPlayStatus() const;
- ChannelStatus a_getRecStatus() const;
- bool a_getReadActions() const;
- bool a_isArmed() const;
- bool a_isRecordingInput() const;
- bool a_isRecordingAction() const;
-
- ID id;
- ID columnId;
+ Data(const m::channel::Data&);
+
+ bool getMute() const;
+ bool getSolo() const;
+ ChannelStatus getPlayStatus() const;
+ ChannelStatus getRecStatus() const;
+ bool getReadActions() const;
+ bool isArmed() const;
+ bool isRecordingInput() const;
+ bool isRecordingAction() const;
+
+ ID id;
+ ID columnId;
#ifdef WITH_VST
- std::vector<ID> pluginIds;
+ std::vector<m::Plugin*> plugins;
#endif
- ChannelType type;
- Pixel height;
- std::string name;
- float volume;
- float pan;
- int key;
- bool hasActions;
+ ChannelType type;
+ Pixel height;
+ std::string name;
+ float volume;
+ float pan;
+ int key;
+ bool hasActions;
std::optional<SampleData> sample;
std::optional<MidiData> midi;
-private:
-
- const m::Channel& m_channel;
+ private:
+ const m::channel::Data& m_channel;
};
/* getChannels
std::vector<Data> getChannels();
-/* a_get
-Returns an atomic property from a Channel, by locking it first. */
-
-template <typename T>
-T a_get(const std::atomic<T>& a)
-{
- m::model::ChannelsLock l(m::model::channels);
- return a.load();
-}
-
/* addChannel
Adds an empty new channel to the stack. */
/* addAndLoadChannel
Adds a new Sample Channel and fills it with a wave right away. */
-void addAndLoadChannel(ID columnId, const std::string& fpath);
+void addAndLoadChannel(ID columnId, const std::string& fpath);
/* addAndLoadChannels
As above, with multiple audio file paths in input. */
void setHeight(ID channelId, Pixel p);
void setSamplePlayerMode(ID channelId, SamplePlayerMode m);
-}}} // giada::c::channel::
+} // namespace giada::c::channel
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "config.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/kernelAudio.h"
+#include "deps/rtaudio/RtAudio.h"
+
+namespace giada::c::config
+{
+namespace
+{
+AudioDeviceData getAudioDeviceData_(DeviceType type, size_t index, int channelsCount, int channelsStart)
+{
+ for (const m::kernelAudio::Device& device : m::kernelAudio::getDevices())
+ if (device.index == index)
+ return AudioDeviceData(type, device, channelsCount, channelsStart);
+ return AudioDeviceData();
+}
+} // namespace
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+AudioDeviceData::AudioDeviceData(DeviceType type, const m::kernelAudio::Device& device,
+ int channelsCount, int channelsStart)
+: type(type)
+, index(device.index)
+, name(device.name)
+, channelsMax(type == DeviceType::OUTPUT ? device.maxOutputChannels : device.maxInputChannels)
+, sampleRates(device.sampleRates)
+, channelsCount(channelsCount)
+, channelsStart(channelsStart)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+void AudioData::setOutputDevice(int index)
+{
+ for (AudioDeviceData& d : outputDevices)
+ {
+ if (index != d.index)
+ continue;
+ outputDevice = d;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void AudioData::setInputDevice(int index)
+{
+ for (AudioDeviceData& d : inputDevices)
+ {
+ if (index == d.index)
+ {
+ inputDevice = d;
+ return;
+ }
+ }
+ inputDevice = {};
+}
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+AudioData getAudioData()
+{
+ AudioData audioData;
+
+ audioData.apis[G_SYS_API_NONE] = "(none)";
+
+#if defined(G_OS_LINUX)
+
+ if (m::kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+ audioData.apis[G_SYS_API_ALSA] = "ALSA";
+ if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+ audioData.apis[G_SYS_API_JACK] = "Jack";
+ if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+ audioData.apis[G_SYS_API_PULSE] = "PulseAudio";
+
+#elif defined(G_OS_FREEBSD)
+
+ if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+ audioData.apis[G_SYS_API_JACK] = "Jack";
+ if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+ audioData.apis[G_SYS_API_PULSE] = "PulseAudio";
+
+#elif defined(G_OS_WINDOWS)
+
+ if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
+ audioData.apis[G_SYS_API_DS] = "DirectSound";
+ if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+ audioData.apis[G_SYS_API_ASIO] = "ASIO";
+ if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
+ audioData.apis[G_SYS_API_WASAPI] = "WASAPI";
+
+#elif defined(G_OS_MAC)
+
+ if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+ audioData.apis[G_SYS_API_CORE] = "CoreAudio";
+
+#endif
+
+ std::vector<m::kernelAudio::Device> devices = m::kernelAudio::getDevices();
+
+ for (const m::kernelAudio::Device& device : devices)
+ {
+ if (device.maxOutputChannels > 0)
+ audioData.outputDevices.push_back(AudioDeviceData(DeviceType::OUTPUT, device, G_MAX_IO_CHANS, 0));
+ if (device.maxInputChannels > 0)
+ audioData.inputDevices.push_back(AudioDeviceData(DeviceType::INPUT, device, 1, 0));
+ }
+
+ audioData.api = m::conf::conf.soundSystem;
+ audioData.bufferSize = m::conf::conf.buffersize;
+ audioData.sampleRate = m::conf::conf.samplerate;
+ audioData.limitOutput = m::conf::conf.limitOutput;
+ audioData.recTriggerLevel = m::conf::conf.recTriggerLevel;
+ audioData.resampleQuality = m::conf::conf.rsmpQuality;
+ audioData.outputDevice = getAudioDeviceData_(DeviceType::OUTPUT,
+ m::conf::conf.soundDeviceOut, m::conf::conf.channelsOutCount,
+ m::conf::conf.channelsOutStart);
+ audioData.inputDevice = getAudioDeviceData_(DeviceType::INPUT,
+ m::conf::conf.soundDeviceIn, m::conf::conf.channelsInCount,
+ m::conf::conf.channelsInStart);
+
+ return audioData;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void save(const AudioData& data)
+{
+ m::conf::conf.soundSystem = data.api;
+ m::conf::conf.soundDeviceOut = data.outputDevice.index;
+ m::conf::conf.soundDeviceIn = data.inputDevice.index;
+ m::conf::conf.channelsOutCount = data.outputDevice.channelsCount;
+ m::conf::conf.channelsOutStart = data.outputDevice.channelsStart;
+ m::conf::conf.channelsInCount = data.inputDevice.channelsCount;
+ m::conf::conf.channelsInStart = data.inputDevice.channelsStart;
+ m::conf::conf.limitOutput = data.limitOutput;
+ m::conf::conf.rsmpQuality = data.resampleQuality;
+ m::conf::conf.buffersize = data.bufferSize;
+ m::conf::conf.recTriggerLevel = data.recTriggerLevel;
+ m::conf::conf.samplerate = data.sampleRate;
+}
+} // namespace giada::c::config
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_GLUE_CONFIG_H
+#define G_GLUE_CONFIG_H
+
+#include "core/types.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace giada::m::kernelAudio
+{
+struct Device;
+}
+namespace giada::c::config
+{
+enum class DeviceType
+{
+ INPUT,
+ OUTPUT
+};
+
+struct AudioDeviceData
+{
+ AudioDeviceData() = default;
+ AudioDeviceData(DeviceType t, const m::kernelAudio::Device&, int channelsCount, int channelsStart);
+
+ DeviceType type = DeviceType::OUTPUT;
+ int index = -1;
+ std::string name = "";
+ int channelsMax = 0;
+ std::vector<int> sampleRates = {};
+
+ /* Selectable values. */
+
+ int channelsCount = 0;
+ int channelsStart = 0;
+};
+
+struct AudioData
+{
+ void setOutputDevice(int index);
+ void setInputDevice(int index);
+
+ std::map<int, std::string> apis;
+ std::vector<AudioDeviceData> outputDevices;
+ std::vector<AudioDeviceData> inputDevices;
+
+ /* Selectable values. */
+
+ int api;
+ AudioDeviceData outputDevice;
+ AudioDeviceData inputDevice;
+ int bufferSize;
+ int sampleRate;
+ bool limitOutput;
+ float recTriggerLevel;
+ int resampleQuality;
+};
+
+/* getAudioData
+Returns viewModel object filled with data. */
+
+AudioData getAudioData();
+/*
+AudioDeviceData getAudioDeviceData(size_t index, int channelsCount, int channelsStart);
+*/
+void save(const AudioData&);
+} // namespace giada::c::config
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "core/const.h"
+#include "events.h"
#include "core/clock.h"
-#include "core/mixer.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/eventDispatcher.h"
#include "core/midiEvent.h"
-#include "core/plugins/pluginHost.h"
-#include "core/sequencer.h"
+#include "core/mixer.h"
#include "core/mixerHandler.h"
-#include "core/conf.h"
+#include "core/model/model.h"
+#include "core/plugins/pluginHost.h"
#include "core/recManager.h"
-#include "utils/log.h"
-#include "gui/dialogs/sampleEditor.h"
+#include "core/sequencer.h"
+#include "core/types.h"
+#include "glue/main.h"
+#include "glue/plugin.h"
+#include "glue/sampleEditor.h"
#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/sampleEditor.h"
#include "gui/dialogs/warnings.h"
-#include "gui/elems/mainWindow/mainIO.h"
-#include "gui/elems/mainWindow/mainTimer.h"
#include "gui/elems/basics/dial.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "gui/elems/sampleEditor/volumeTool.h"
-#include "gui/elems/sampleEditor/pitchTool.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainTimer.h"
#include "gui/elems/sampleEditor/panTool.h"
-#include "glue/sampleEditor.h"
-#include "glue/plugin.h"
-#include "glue/main.h"
-#include "events.h"
-
+#include "gui/elems/sampleEditor/pitchTool.h"
+#include "gui/elems/sampleEditor/volumeTool.h"
+#include "utils/log.h"
+#include <FL/Fl.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
-namespace events
+namespace giada::c::events
{
namespace
{
-void pushEvent_(m::mixer::Event e, Thread t)
+void pushEvent_(m::eventDispatcher::Event e, Thread t)
{
bool res = true;
if (t == Thread::MAIN)
- res = m::mixer::UIevents.push(e);
- else
- if (t == Thread::MIDI)
- res = m::mixer::MidiEvents.push(e);
+ res = m::eventDispatcher::UIevents.push(e);
+ else if (t == Thread::MIDI)
+ res = m::eventDispatcher::MidiEvents.push(e);
else
assert(false);
-
+
if (!res)
G_DEBUG("[events] Queue full!\n");
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void pressChannel(ID channelId, int velocity, Thread t)
{
m::MidiEvent e;
e.setVelocity(velocity);
- pushEvent_({ m::mixer::EventType::KEY_PRESS, 0, {0, channelId, 0, e} }, t);
+ pushEvent_({m::eventDispatcher::EventType::KEY_PRESS, 0, channelId, velocity}, t);
}
-
void releaseChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::KEY_RELEASE, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::KEY_RELEASE, 0, channelId, {}}, t);
}
-
void killChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::KEY_KILL, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::KEY_KILL, 0, channelId, {}}, t);
}
-
/* -------------------------------------------------------------------------- */
-
void setChannelVolume(ID channelId, float v, Thread t)
{
v = std::clamp(v, 0.0f, G_MAX_VOLUME);
- pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, channelId, 0, {v} } }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_VOLUME, 0, channelId, v}, t);
sampleEditor::onRefresh(t == Thread::MAIN, [v](v::gdSampleEditor& e) { e.volumeTool->update(v); });
- if (t != Thread::MAIN) {
+ if (t != Thread::MAIN)
+ {
Fl::lock();
G_MainWin->keyboard->getChannel(channelId)->vol->value(v);
Fl::unlock();
}
}
-
/* -------------------------------------------------------------------------- */
-
-void setChannelPitch(ID channelId, float v, Thread t)
-{
+void setChannelPitch(ID channelId, float v, Thread t)
+{
v = std::clamp(v, G_MIN_PITCH, G_MAX_PITCH);
- pushEvent_({ m::mixer::EventType::CHANNEL_PITCH, 0, { 0, channelId, 0, {v} } }, t);
-
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_PITCH, 0, channelId, v}, t);
+
sampleEditor::onRefresh(t == Thread::MAIN, [v](v::gdSampleEditor& e) { e.pitchTool->update(v); });
}
-
/* -------------------------------------------------------------------------- */
-
void sendChannelPan(ID channelId, float v)
{
v = std::clamp(v, 0.0f, G_MAX_PAN);
/* Pan event is currently triggered only by the main thread. */
- pushEvent_({ m::mixer::EventType::CHANNEL_PAN, 0, { 0, channelId, 0, {v} } }, Thread::MAIN);
-
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_PAN, 0, channelId, v}, Thread::MAIN);
+
sampleEditor::onRefresh(/*gui=*/true, [v](v::gdSampleEditor& e) { e.panTool->update(v); });
}
/* -------------------------------------------------------------------------- */
-
void toggleMuteChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_MUTE, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_MUTE, 0, channelId, {}}, t);
}
-
void toggleSoloChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_SOLO, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_SOLO, 0, channelId, {}}, t);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleArmChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_TOGGLE_ARM, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_TOGGLE_ARM, 0, channelId, {}}, t);
}
-
void toggleReadActionsChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_TOGGLE_READ_ACTIONS, 0, channelId, {}}, t);
}
-
void killReadActionsChannel(ID channelId, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_KILL_READ_ACTIONS, 0, {0, channelId} }, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_KILL_READ_ACTIONS, 0, channelId, {}}, t);
}
-
/* -------------------------------------------------------------------------- */
-
void sendMidiToChannel(ID channelId, m::MidiEvent e, Thread t)
{
- pushEvent_({ m::mixer::EventType::MIDI, 0, {0, channelId, 0, e} }, t);
+ pushEvent_({m::eventDispatcher::EventType::MIDI, 0, channelId, m::Action{0, channelId, 0, e}}, t);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleMetronome()
{
m::sequencer::toggleMetronome();
}
-
/* -------------------------------------------------------------------------- */
-
void setMasterInVolume(float v, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, m::mixer::MASTER_IN_CHANNEL_ID, 0, {v} }}, t);
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_VOLUME, 0, m::mixer::MASTER_IN_CHANNEL_ID, v}, t);
- if (t != Thread::MAIN) {
+ if (t != Thread::MAIN)
+ {
Fl::lock();
G_MainWin->mainIO->setInVol(v);
Fl::unlock();
}
}
-
void setMasterOutVolume(float v, Thread t)
{
- pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, m::mixer::MASTER_OUT_CHANNEL_ID, 0, {v} }}, t);
-
- if (t != Thread::MAIN) {
+ pushEvent_({m::eventDispatcher::EventType::CHANNEL_VOLUME, 0, m::mixer::MASTER_OUT_CHANNEL_ID, v}, t);
+
+ if (t != Thread::MAIN)
+ {
Fl::lock();
G_MainWin->mainIO->setOutVol(v);
Fl::unlock();
}
}
-
/* -------------------------------------------------------------------------- */
-
void multiplyBeats()
{
main::setBeats(m::clock::getBeats() * 2, m::clock::getBars());
}
-
void divideBeats()
{
main::setBeats(m::clock::getBeats() / 2, m::clock::getBars());
}
-
/* -------------------------------------------------------------------------- */
-
void startSequencer(Thread t)
-{
- pushEvent_({ m::mixer::EventType::SEQUENCER_START, 0 }, t);
+{
+ pushEvent_({m::eventDispatcher::EventType::SEQUENCER_START, 0, 0, {}}, t);
m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
}
-
void stopSequencer(Thread t)
-{
- pushEvent_({ m::mixer::EventType::SEQUENCER_STOP, 0 }, t);
+{
+ pushEvent_({m::eventDispatcher::EventType::SEQUENCER_STOP, 0, 0, {}}, t);
}
-
void toggleSequencer(Thread t)
-{
+{
m::clock::isRunning() ? stopSequencer(t) : startSequencer(t);
}
-
void rewindSequencer(Thread t)
-{
- pushEvent_({ m::mixer::EventType::SEQUENCER_REWIND_REQ, 0 }, t);
+{
+ pushEvent_({m::eventDispatcher::EventType::SEQUENCER_REWIND, 0, 0, {}}, t);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleActionRecording()
{
m::recManager::toggleActionRec(m::conf::conf.recTriggerMode);
}
-
void toggleInputRecording()
{
- m::recManager::toggleInputRec(m::conf::conf.recTriggerMode);
+ m::recManager::toggleInputRec(m::conf::conf.recTriggerMode, m::conf::conf.inputRecMode);
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
void setPluginParameter(ID pluginId, int paramIndex, float value, bool gui)
{
c::plugin::updateWindow(pluginId, gui);
}
#endif
-}}} // giada::c::events::
+} // namespace giada::c::events
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_EVENTS_H
#define G_GLUE_EVENTS_H
-
#include "core/types.h"
-
/* giada::c::events
Functions that take care of live event dispatching. Every live gesture that
comes from the UI, MIDI thread or keyboard interaction and wants to change the
internal engine state must call these functions. */
-namespace giada {
-namespace m
+namespace giada::m
{
class MidiEvent;
}
-namespace c {
-namespace events
+namespace giada::c::events
{
/* Channel*
Channel-related events. */
-void pressChannel (ID channelId, int velocity, Thread t);
-void releaseChannel (ID channelId, Thread t);
-void killChannel (ID channelId, Thread t);
-void setChannelVolume (ID channelId, float v, Thread t);
-void setChannelPitch (ID channelId, float v, Thread t);
-void sendChannelPan (ID channelId, float v); // FIXME typo: should be setChannelPan
-void toggleMuteChannel (ID channelId, Thread t);
-void toggleSoloChannel (ID channelId, Thread t);
-void toggleArmChannel (ID channelId, Thread t);
+void pressChannel(ID channelId, int velocity, Thread t);
+void releaseChannel(ID channelId, Thread t);
+void killChannel(ID channelId, Thread t);
+void setChannelVolume(ID channelId, float v, Thread t);
+void setChannelPitch(ID channelId, float v, Thread t);
+void sendChannelPan(ID channelId, float v); // FIXME typo: should be setChannelPan
+void toggleMuteChannel(ID channelId, Thread t);
+void toggleSoloChannel(ID channelId, Thread t);
+void toggleArmChannel(ID channelId, Thread t);
void toggleReadActionsChannel(ID channelId, Thread t);
-void killReadActionsChannel (ID channelId, Thread t);
-void sendMidiToChannel (ID channelId, m::MidiEvent e, Thread t);
+void killReadActionsChannel(ID channelId, Thread t);
+void sendMidiToChannel(ID channelId, m::MidiEvent e, Thread t);
/* Main*
Master I/O, transport and other engine-related events. */
-void toggleMetronome ();
-void setMasterInVolume (float v, Thread t);
-void setMasterOutVolume (float v, Thread t);
-void multiplyBeats ();
-void divideBeats ();
-void startSequencer (Thread t);
-void stopSequencer (Thread t);
-void toggleSequencer (Thread t);
-void rewindSequencer (Thread t);
+void toggleMetronome();
+void setMasterInVolume(float v, Thread t);
+void setMasterOutVolume(float v, Thread t);
+void multiplyBeats();
+void divideBeats();
+void startSequencer(Thread t);
+void stopSequencer(Thread t);
+void toggleSequencer(Thread t);
+void rewindSequencer(Thread t);
void toggleActionRecording();
-void toggleInputRecording ();
+void toggleInputRecording();
/* Plug-ins. */
#ifdef WITH_VST
-void setPluginParameter(ID pluginId, int paramIndex, float value, bool gui);
+void setPluginParameter(ID pluginId, int paramIndex, float value, bool gui);
#endif
-}}} // giada::c::events::
-
+} // namespace giada::c::events
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
+#include "io.h"
+#include "channel.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/kernelAudio.h"
+#include "core/midiDispatcher.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/recManager.h"
+#include "core/recorder.h"
+#include "core/recorderHandler.h"
+#include "core/wave.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/midiIO/midiInputBase.h"
#include "gui/dialogs/warnings.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/mainWindow/mainTransport.h"
-#include "gui/elems/mainWindow/mainTimer.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/mainWindow/keyboard/channelButton.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/mainWindow/mainTransport.h"
+#include "main.h"
#include "utils/gui.h"
#include "utils/log.h"
#include "utils/math.h"
-#include "core/model/model.h"
-#include "core/recorder.h"
-#include "core/conf.h"
-#include "core/recManager.h"
-#include "core/kernelAudio.h"
-#include "core/mixer.h"
-#include "core/mixerHandler.h"
-#include "core/wave.h"
-#include "core/midiDispatcher.h"
-#include "core/clock.h"
-#include "core/recorderHandler.h"
-#include "main.h"
-#include "channel.h"
-#include "io.h"
-
+#include <FL/Fl.H>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
-namespace io
+namespace giada::c::io
{
namespace
{
u::gui::rebuildSubWindow(WID_MIDI_INPUT);
u::gui::rebuildSubWindow(WID_MIDI_OUTPUT);
}
-} // {anonymous}
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-Channel_InputData::Channel_InputData(const m::Channel& c)
-: channelId (c.id)
-, channelType (c.getType())
-, enabled (c.midiLearner.state->enabled.load())
-, velocityAsVol(c.samplePlayer ? c.samplePlayer->state->velocityAsVol.load() : 0)
-, filter (c.midiLearner.state->filter.load())
-, keyPress (c.midiLearner.state->keyPress.getValue())
-, keyRelease (c.midiLearner.state->keyRelease.getValue())
-, kill (c.midiLearner.state->kill.getValue())
-, arm (c.midiLearner.state->arm.getValue())
-, volume (c.midiLearner.state->volume.getValue())
-, mute (c.midiLearner.state->mute.getValue())
-, solo (c.midiLearner.state->solo.getValue())
-, pitch (c.midiLearner.state->pitch.getValue())
-, readActions (c.midiLearner.state->readActions.getValue())
+Channel_InputData::Channel_InputData(const m::channel::Data& c)
+: channelId(c.id)
+, channelType(c.type)
+, enabled(c.midiLearner.enabled)
+, velocityAsVol(c.samplePlayer ? c.samplePlayer->velocityAsVol : 0)
+, filter(c.midiLearner.filter)
+, keyPress(c.midiLearner.keyPress.getValue())
+, keyRelease(c.midiLearner.keyRelease.getValue())
+, kill(c.midiLearner.kill.getValue())
+, arm(c.midiLearner.arm.getValue())
+, volume(c.midiLearner.volume.getValue())
+, mute(c.midiLearner.mute.getValue())
+, solo(c.midiLearner.solo.getValue())
+, pitch(c.midiLearner.pitch.getValue())
+, readActions(c.midiLearner.readActions.getValue())
{
#ifdef WITH_VST
- for (ID id : c.pluginIds) {
- m::Plugin& p = m::model::get(m::model::plugins, id);
-
+ for (const m::Plugin* p : c.plugins)
+ {
PluginData pd;
- pd.id = p.id;
- pd.name = p.getName();
- for (int i = 0; i < p.getNumParameters(); i++)
- pd.params.push_back({ i, p.getParameterName(i), p.midiInParams.at(i).getValue() });
-
+ pd.id = p->id;
+ pd.name = p->getName();
+ for (int i = 0; i < p->getNumParameters(); i++)
+ pd.params.push_back({i, p->getParameterName(i), p->midiInParams.at(i).getValue()});
plugins.push_back(pd);
}
#endif
}
-
/* -------------------------------------------------------------------------- */
-
-MidiChannel_OutputData::MidiChannel_OutputData(const m::MidiSender& s)
-: enabled(s.state->enabled.load())
-, filter (s.state->filter.load())
+MidiChannel_OutputData::MidiChannel_OutputData(const m::midiSender::Data& s)
+: enabled(s.enabled)
+, filter(s.filter)
{
}
-
/* -------------------------------------------------------------------------- */
-
-Channel_OutputData::Channel_OutputData(const m::Channel& c)
-: channelId (c.id)
-, lightningEnabled(c.midiLighter.state->enabled.load())
-, lightningPlaying(c.midiLighter.state->playing.getValue())
-, lightningMute (c.midiLighter.state->mute.getValue())
-, lightningSolo (c.midiLighter.state->solo.getValue())
-{
- if (c.getType() == ChannelType::MIDI)
+Channel_OutputData::Channel_OutputData(const m::channel::Data& c)
+: channelId(c.id)
+, lightningEnabled(c.midiLighter.enabled)
+, lightningPlaying(c.midiLighter.playing.getValue())
+, lightningMute(c.midiLighter.mute.getValue())
+, lightningSolo(c.midiLighter.solo.getValue())
+{
+ if (c.type == ChannelType::MIDI)
output = std::make_optional<MidiChannel_OutputData>(*c.midiSender);
}
-
/* -------------------------------------------------------------------------- */
-
Master_InputData::Master_InputData(const m::model::MidiIn& midiIn)
-: enabled (midiIn.enabled)
-, filter (midiIn.filter)
-, rewind (midiIn.rewind)
-, startStop (midiIn.startStop)
-, actionRec (midiIn.actionRec)
-, inputRec (midiIn.inputRec)
-, volumeIn (midiIn.volumeIn)
-, volumeOut (midiIn.volumeOut)
+: enabled(midiIn.enabled)
+, filter(midiIn.filter)
+, rewind(midiIn.rewind)
+, startStop(midiIn.startStop)
+, actionRec(midiIn.actionRec)
+, inputRec(midiIn.inputRec)
+, volumeIn(midiIn.volumeIn)
+, volumeOut(midiIn.volumeOut)
, beatDouble(midiIn.beatDouble)
-, beatHalf (midiIn.beatHalf)
-, metronome (midiIn.metronome)
+, beatHalf(midiIn.beatHalf)
+, metronome(midiIn.metronome)
{
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Channel_InputData channel_getInputData(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
-#ifdef WITH_VST
- mm::PluginsLock ml(mm::plugins);
-#endif
-
- return Channel_InputData(mm::get(mm::channels, channelId));
+ return Channel_InputData(m::model::get().getChannel(channelId));
}
-
/* -------------------------------------------------------------------------- */
-
Channel_OutputData channel_getOutputData(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
- return Channel_OutputData(mm::get(mm::channels, channelId));
+ return Channel_OutputData(m::model::get().getChannel(channelId));
}
-
/* -------------------------------------------------------------------------- */
-
Master_InputData master_getInputData()
{
- namespace mm = m::model;
-
- mm::MidiInLock l(mm::midiIn);
- return Master_InputData(*mm::midiIn.get());
+ return Master_InputData(m::model::get().midiIn);
}
-
/* -------------------------------------------------------------------------- */
-
void channel_enableMidiLearn(ID channelId, bool v)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.midiLearner.state->enabled.store(v);
- });
+ m::model::get().getChannel(channelId).midiLearner.enabled = v;
+ m::model::swap(m::model::SwapType::NONE);
rebuildMidiWindows_();
}
-
/* -------------------------------------------------------------------------- */
-
void channel_enableMidiLightning(ID channelId, bool v)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.midiLighter.state->enabled.store(v);
- });
+ m::model::get().getChannel(channelId).midiLighter.enabled = v;
+ m::model::swap(m::model::SwapType::NONE);
rebuildMidiWindows_();
}
-
/* -------------------------------------------------------------------------- */
-
void channel_enableMidiOutput(ID channelId, bool v)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.midiSender->state->enabled.store(v);
- });
+ m::model::get().getChannel(channelId).midiSender->enabled = v;
+ m::model::swap(m::model::SwapType::NONE);
rebuildMidiWindows_();
}
-
/* -------------------------------------------------------------------------- */
-
void channel_enableVelocityAsVol(ID channelId, bool v)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.samplePlayer->state->velocityAsVol.store(v);
- });
+ m::model::get().getChannel(channelId).samplePlayer->velocityAsVol = v;
+ m::model::swap(m::model::SwapType::NONE);
}
-
/* -------------------------------------------------------------------------- */
-
void channel_setMidiInputFilter(ID channelId, int ch)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.midiLearner.state->filter.store(ch);
- });
+ m::model::get().getChannel(channelId).midiLearner.filter = ch;
+ m::model::swap(m::model::SwapType::NONE);
}
-
void channel_setMidiOutputFilter(ID channelId, int ch)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.midiSender->state->filter.store(ch);
- });
+ m::model::get().getChannel(channelId).midiSender->filter = ch;
+ m::model::swap(m::model::SwapType::NONE);
}
-
/* -------------------------------------------------------------------------- */
-
void channel_setKey(ID channelId, int k)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.state->key.store(k);
- }, /*rebuild=*/true);
+ m::model::get().getChannel(channelId).key = k;
+ m::model::swap(m::model::SwapType::HARD);
}
-
/* -------------------------------------------------------------------------- */
-
void channel_startMidiLearn(int param, ID channelId)
{
m::midiDispatcher::startChannelLearn(param, channelId, rebuildMidiWindows_);
}
-
void master_startMidiLearn(int param)
{
m::midiDispatcher::startMasterLearn(param, rebuildMidiWindows_);
}
-
#ifdef WITH_VST
void plugin_startMidiLearn(int paramIndex, ID pluginId)
#endif
-
/* -------------------------------------------------------------------------- */
-
void stopMidiLearn()
{
m::midiDispatcher::stopLearn();
rebuildMidiWindows_();
}
-
/* -------------------------------------------------------------------------- */
-
void channel_clearMidiLearn(int param, ID channelId)
{
m::midiDispatcher::clearChannelLearn(param, channelId, rebuildMidiWindows_);
}
-
-void master_clearMidiLearn (int param)
+void master_clearMidiLearn(int param)
{
m::midiDispatcher::clearMasterLearn(param, rebuildMidiWindows_);
}
-
#ifdef WITH_VST
-void plugin_clearMidiLearn (int param, ID pluginId)
+void plugin_clearMidiLearn(int param, ID pluginId)
{
m::midiDispatcher::clearPluginLearn(param, pluginId, rebuildMidiWindows_);
}
#endif
-
/* -------------------------------------------------------------------------- */
-
void master_enableMidiLearn(bool v)
{
- m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
- {
- m.enabled = v;
- });
+ m::model::get().midiIn.enabled = v;
+ m::model::swap(m::model::SwapType::NONE);
rebuildMidiWindows_();
}
-
/* -------------------------------------------------------------------------- */
-
void master_setMidiFilter(int c)
{
- m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
- {
- m.filter = c;
- });
+ m::model::get().midiIn.filter = c;
+ m::model::swap(m::model::SwapType::NONE);
}
-}}} // giada::c::io::
+} // namespace giada::c::io
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_IO_H
#define G_GLUE_IO_H
-
-#include <atomic>
-#include "core/types.h"
#include "core/midiEvent.h"
#include "core/model/model.h"
+#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-class Channel;
+struct Data;
}
-namespace c {
-namespace io
+namespace giada::c::io
{
struct PluginParamData
{
- int index;
- std::string name;
- uint32_t value;
+ int index;
+ std::string name;
+ uint32_t value;
};
struct PluginData
{
- ID id;
- std::string name;
- std::vector<PluginParamData> params;
+ ID id;
+ std::string name;
+ std::vector<PluginParamData> params;
};
struct Channel_InputData
{
- Channel_InputData() = default;
- Channel_InputData(const m::Channel&);
-
- ID channelId;
- ChannelType channelType;
- bool enabled;
- bool velocityAsVol;
- int filter;
-
- uint32_t keyPress;
- uint32_t keyRelease;
- uint32_t kill;
- uint32_t arm;
- uint32_t volume;
- uint32_t mute;
- uint32_t solo;
- uint32_t pitch;
- uint32_t readActions;
-
- std::vector<PluginData> plugins;
+ Channel_InputData() = default;
+ Channel_InputData(const m::channel::Data&);
+
+ ID channelId;
+ ChannelType channelType;
+ bool enabled;
+ bool velocityAsVol;
+ int filter;
+
+ uint32_t keyPress;
+ uint32_t keyRelease;
+ uint32_t kill;
+ uint32_t arm;
+ uint32_t volume;
+ uint32_t mute;
+ uint32_t solo;
+ uint32_t pitch;
+ uint32_t readActions;
+
+ std::vector<PluginData> plugins;
};
struct Master_InputData
{
- Master_InputData() = default;
- Master_InputData(const m::model::MidiIn&);
+ Master_InputData() = default;
+ Master_InputData(const m::model::MidiIn&);
- bool enabled;
- int filter;
+ bool enabled;
+ int filter;
uint32_t rewind;
uint32_t startStop;
uint32_t volumeOut;
uint32_t beatDouble;
uint32_t beatHalf;
- uint32_t metronome;
+ uint32_t metronome;
};
struct MidiChannel_OutputData
{
- MidiChannel_OutputData(const m::MidiSender&);
+ MidiChannel_OutputData(const m::midiSender::Data&);
- bool enabled;
- int filter;
+ bool enabled;
+ int filter;
};
struct Channel_OutputData
{
- Channel_OutputData() = default;
- Channel_OutputData(const m::Channel&);
+ Channel_OutputData() = default;
+ Channel_OutputData(const m::channel::Data&);
- ID channelId;
- bool lightningEnabled;
- uint32_t lightningPlaying;
- uint32_t lightningMute;
- uint32_t lightningSolo;
+ ID channelId;
+ bool lightningEnabled;
+ uint32_t lightningPlaying;
+ uint32_t lightningMute;
+ uint32_t lightningSolo;
- std::optional<MidiChannel_OutputData> output;
+ std::optional<MidiChannel_OutputData> output;
};
Channel_InputData channel_getInputData(ID channelId);
void channel_startMidiLearn(int param, ID channelId);
void channel_clearMidiLearn(int param, ID channelId);
-void master_clearMidiLearn (int param);
-void master_startMidiLearn (int param);
+void master_clearMidiLearn(int param);
+void master_startMidiLearn(int param);
void stopMidiLearn();
#ifdef WITH_VST
-void plugin_startMidiLearn (int paramIndex, ID pluginId);
-void plugin_clearMidiLearn (int param, ID pluginId);
+void plugin_startMidiLearn(int paramIndex, ID pluginId);
+void plugin_clearMidiLearn(int param, ID pluginId);
#endif
/* Master functions. */
void master_enableMidiLearn(bool v);
void master_setMidiFilter(int c);
-}}} // giada::c::io::
+} // namespace giada::c::io
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <cassert>
-#include <FL/Fl.H>
-#include "gui/dialogs/warnings.h"
-#include "gui/elems/mainWindow/mainIO.h"
-#include "gui/elems/mainWindow/mainTimer.h"
-#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/dialogs/mainWindow.h"
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "utils/log.h"
-#include "core/model/model.h"
-#include "core/mixerHandler.h"
-#include "core/mixer.h"
+#include "main.h"
#include "core/clock.h"
+#include "core/conf.h"
+#include "core/const.h"
#include "core/init.h"
-#include "core/kernelMidi.h"
#include "core/kernelAudio.h"
+#include "core/kernelMidi.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/recManager.h"
#include "core/recorder.h"
#include "core/recorderHandler.h"
-#include "core/recManager.h"
-#include "core/conf.h"
-#include "core/const.h"
-#include "core/plugins/pluginManager.h"
-#include "core/plugins/pluginHost.h"
-#include "main.h"
-
-
-extern giada::v::gdMainWindow *G_MainWin;
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
+#include <cassert>
+#include <cmath>
+extern giada::v::gdMainWindow* G_MainWin;
-namespace giada {
-namespace c {
-namespace main
+namespace giada::c::main
{
-namespace
-{
-void setBpm_(float current, std::string s)
-{
- if (current < G_MIN_BPM) {
- current = G_MIN_BPM;
- s = G_MIN_BPM_STR;
- }
- else
- if (current > G_MAX_BPM) {
- current = G_MAX_BPM;
- s = G_MAX_BPM_STR;
- }
-
- float previous = m::clock::getBpm();
- m::clock::setBpm(current);
- m::recorderHandler::updateBpm(previous, current, m::clock::getQuantizerStep());
- m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
-
- /* This function might get called by Jack callback BEFORE the UI is up
- and running, that is when G_MainWin == nullptr. */
-
- if (G_MainWin != nullptr) {
- u::gui::refreshActionEditor();
- G_MainWin->mainTimer->setBpm(s.c_str());
- }
-
- u::log::print("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s, m::clock::getBpm());
-}
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
Timer::Timer(const m::model::Clock& c)
-: bpm (c.bpm)
-, beats (c.beats)
-, bars (c.bars)
-, quantize (c.quantize)
-, isUsingJack (m::kernelAudio::getAPI() == G_SYS_API_JACK)
+: bpm(c.bpm)
+, beats(c.beats)
+, bars(c.bars)
+, quantize(c.quantize)
+, isUsingJack(m::kernelAudio::getAPI() == G_SYS_API_JACK)
, isRecordingInput(m::recManager::isRecordingInput())
{
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-IO::IO(const m::Channel& out, const m::Channel& in, const m::model::Mixer& m)
-: masterOutVol (out.state->volume.load())
-, masterInVol (in.state->volume.load())
+IO::IO(const m::channel::Data& out, const m::channel::Data& in, const m::model::Mixer& m)
+: masterOutVol(out.volume)
+, masterInVol(in.volume)
#ifdef WITH_VST
-, masterOutHasPlugins(out.pluginIds.size() > 0)
-, masterInHasPlugins (in.pluginIds.size() > 0)
+, masterOutHasPlugins(out.plugins.size() > 0)
+, masterInHasPlugins(in.plugins.size() > 0)
#endif
-, inToOut (m.inToOut)
+, inToOut(m.inToOut)
{
}
-
/* -------------------------------------------------------------------------- */
-
-float IO::a_getMasterOutPeak()
+float IO::getMasterOutPeak()
{
- return m::mixer::peakOut.load();
+ return m::mixer::getPeakOut();
}
-
-float IO::a_getMasterInPeak()
+float IO::getMasterInPeak()
{
- return m::mixer::peakIn.load();
+ return m::mixer::getPeakIn();
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Timer getTimer()
{
- namespace mm = m::model;
-
- mm::ClockLock c(mm::clock);
- return Timer(*mm::clock.get());
+ return Timer(m::model::get().clock);
}
-
/* -------------------------------------------------------------------------- */
-
IO getIO()
{
- namespace mm = m::model;
+ return IO(m::model::get().getChannel(m::mixer::MASTER_OUT_CHANNEL_ID),
+ m::model::get().getChannel(m::mixer::MASTER_IN_CHANNEL_ID),
+ m::model::get().mixer);
+}
- mm::ChannelsLock cl(mm::channels);
- mm::MixerLock ml(mm::mixer);
+/* -------------------------------------------------------------------------- */
- return IO(mm::get(mm::channels, m::mixer::MASTER_OUT_CHANNEL_ID),
- mm::get(mm::channels, m::mixer::MASTER_IN_CHANNEL_ID),
- *mm::mixer.get());
-}
+Sequencer getSequencer()
+{
+ Sequencer out;
+ m::mixer::RecordInfo recInfo = m::mixer::getRecordInfo();
-/* -------------------------------------------------------------------------- */
+ out.isFreeModeInputRec = m::recManager::isRecordingInput() && m::conf::conf.inputRecMode == InputRecMode::FREE;
+ out.shouldBlink = u::gui::shouldBlink() && (m::clock::getStatus() == ClockStatus::WAITING || out.isFreeModeInputRec);
+ out.beats = m::clock::getBeats();
+ out.bars = m::clock::getBars();
+ out.currentBeat = m::clock::getCurrentBeat();
+ out.recPosition = recInfo.position;
+ out.recMaxLength = recInfo.maxLength;
+
+ return out;
+}
+/* -------------------------------------------------------------------------- */
-void setBpm(const char* v1, const char* v2)
+void setBpm(const char* i, const char* f)
{
/* Never change this stuff while recording audio. */
if (m::recManager::isRecordingInput())
return;
- /* A value such as atof("120.1") will never be 120.1 but 120.0999999, because
- of the rounding error. So we pass the actual "wrong" value to mixer and we show
- the nice looking (but fake) one to the GUI.
- On Linux, let Jack handle the bpm change if it's on. */
-
- float f = static_cast<float>(std::atof(v1) + (std::atof(v2)/10));
- std::string s = std::string(v1) + "." + std::string(v2);
-
-#ifdef WITH_AUDIO_JACK
- if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
- m::kernelAudio::jackSetBpm(f);
- else
-#endif
- setBpm_(f, s);
+ m::clock::setBpm(std::atof(i) + (std::atof(f) / 10.0f));
}
-
/* -------------------------------------------------------------------------- */
-
void setBpm(float f)
{
/* Never change this stuff while recording audio. */
if (m::recManager::isRecordingInput())
return;
- float intpart;
- float fracpart = std::round(std::modf(f, &intpart) * 10);
- std::string s = std::to_string((int) intpart) + "." + std::to_string((int)fracpart);
-
- setBpm_(f, s);
+ m::clock::setBpm(f);
}
-
/* -------------------------------------------------------------------------- */
-
void setBeats(int beats, int bars)
{
/* Never change this stuff while recording audio. */
return;
m::clock::setBeats(beats, bars);
- m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
-
- G_MainWin->mainTimer->setMeter(m::clock::getBeats(), m::clock::getBars());
- u::gui::refreshActionEditor(); // in case the action editor is open
+ m::mixer::allocRecBuffer(m::clock::getMaxFramesInLoop());
}
-
/* -------------------------------------------------------------------------- */
-
void quantize(int val)
{
m::clock::setQuantize(val);
}
-
/* -------------------------------------------------------------------------- */
-
void clearAllSamples()
{
if (!v::gdConfirmWin("Warning", "Free all Sample channels: are you sure?"))
m::recorderHandler::clearAllActions();
}
-
/* -------------------------------------------------------------------------- */
-
void clearAllActions()
{
if (!v::gdConfirmWin("Warning", "Clear all actions: are you sure?"))
m::recorderHandler::clearAllActions();
}
-
/* -------------------------------------------------------------------------- */
-
void setInToOut(bool v)
{
m::mh::setInToOut(v);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleRecOnSignal()
{
- /* Can't set RecTriggerMode::SIGNAL while sequencer is running, in order
- to prevent mistakes while live recording. */
-
- if (m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL && m::clock::isRunning())
+ if (!m::recManager::canEnableRecOnSignal())
+ {
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
return;
+ }
m::conf::conf.recTriggerMode = m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL ? RecTriggerMode::SIGNAL : RecTriggerMode::NORMAL;
}
-
/* -------------------------------------------------------------------------- */
+void toggleFreeInputRec()
+{
+ if (!m::recManager::canEnableFreeInputRec())
+ {
+ m::conf::conf.inputRecMode = InputRecMode::RIGID;
+ return;
+ }
+ m::conf::conf.inputRecMode = m::conf::conf.inputRecMode == InputRecMode::FREE ? InputRecMode::RIGID : InputRecMode::FREE;
+}
+
+/* -------------------------------------------------------------------------- */
void closeProject()
{
m::init::reset();
m::mixer::enable();
}
-}}} // giada::c::main::
+} // namespace giada::c::main
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_MAIN_H
#define G_MAIN_H
-
#include "core/types.h"
-
-namespace giada {
-namespace m
+namespace giada::m::channel
{
-class Channel;
-namespace model
-{
+struct Data;
+}
+namespace giada::m::model
+{
+struct Clock;
struct Clock;
struct Mixer;
-}}
-namespace c {
-namespace main
+struct Mixer;
+} // namespace giada::m::model
+namespace giada::c::main
{
struct Timer
{
- Timer() = default;
- Timer(const m::model::Clock& c);
-
- float bpm;
- int beats;
- int bars;
- int quantize;
- bool isUsingJack;
- bool isRecordingInput;
+ Timer() = default;
+ Timer(const m::model::Clock& c);
+
+ float bpm;
+ int beats;
+ int bars;
+ int quantize;
+ bool isUsingJack;
+ bool isRecordingInput;
};
struct IO
{
- IO() = default;
- IO(const m::Channel& out, const m::Channel& in, const m::model::Mixer& m);
+ IO() = default;
+ IO(const m::channel::Data& out, const m::channel::Data& in, const m::model::Mixer& m);
- float masterOutVol;
- float masterInVol;
+ float masterOutVol;
+ float masterInVol;
#ifdef WITH_VST
- bool masterOutHasPlugins;
- bool masterInHasPlugins;
+ bool masterOutHasPlugins;
+ bool masterInHasPlugins;
#endif
- bool inToOut;
+ bool inToOut;
+
+ float getMasterOutPeak();
+ float getMasterInPeak();
+};
- float a_getMasterOutPeak();
- float a_getMasterInPeak();
+struct Sequencer
+{
+ bool isFreeModeInputRec;
+ bool shouldBlink;
+ int beats;
+ int bars;
+ int currentBeat;
+ Frame recPosition;
+ Frame recMaxLength;
};
/* get*
Returns viewModel objects filled with data. */
-Timer getTimer();
-IO getIO();
+Timer getTimer();
+IO getIO();
+Sequencer getSequencer();
/* setBpm (1)
Sets bpm value from string to float. */
void setInToOut(bool v);
void toggleRecOnSignal();
+void toggleFreeInputRec();
/* closeProject
Resets Giada to init state. If resetGui also refresh all widgets. */
void closeProject();
-}}} // giada::c::main::
+} // namespace giada::c::main
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <cassert>
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "core/plugins/pluginManager.h"
-#include "core/plugins/pluginHost.h"
-#include "core/mixer.h"
#include "core/plugins/plugin.h"
-#include "core/const.h"
#include "core/conf.h"
-#include "utils/gui.h"
+#include "core/const.h"
+#include "core/mixer.h"
+#include "core/model/model.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "gui/dialogs/browser/browserDir.h"
+#include "gui/dialogs/config.h"
#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/pluginWindow.h"
#include "gui/dialogs/pluginList.h"
+#include "gui/dialogs/pluginWindow.h"
#include "gui/dialogs/warnings.h"
-#include "gui/dialogs/config.h"
-#include "gui/dialogs/browser/browserDir.h"
#include "plugin.h"
-
+#include "utils/gui.h"
+#include <FL/Fl.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
-namespace plugin
+namespace giada::c::plugin
{
-Param::Param(const m::Plugin& p, int index)
-: index (index)
+Param::Param(const m::Plugin& p, int index, ID channelId)
+: index(index)
, pluginId(p.id)
-, name (p.getParameterName(index))
-, text (p.getParameterText(index))
-, label (p.getParameterLabel(index))
-, value (p.getParameter(index))
+, channelId(channelId)
+, name(p.getParameterName(index))
+, text(p.getParameterText(index))
+, label(p.getParameterLabel(index))
+, value(p.getParameter(index))
{
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Plugin::Plugin(m::Plugin& p, ID channelId)
-: id (p.id)
-, channelId (channelId)
-, valid (p.valid)
-, hasEditor (p.hasEditor())
-, isBypassed (p.isBypassed())
-, name (p.getName())
-, uniqueId (p.getUniqueId())
+: id(p.id)
+, channelId(channelId)
+, valid(p.valid)
+, hasEditor(p.hasEditor())
+, isBypassed(p.isBypassed())
+, name(p.getName())
+, uniqueId(p.getUniqueId())
, currentProgram(p.getCurrentProgram())
-, m_plugin (p)
+, m_plugin(p)
{
for (int i = 0; i < p.getNumPrograms(); i++)
- programs.push_back({ i, p.getProgramName(i) });
+ programs.push_back({i, p.getProgramName(i)});
for (int i = 0; i < p.getNumParameters(); i++)
paramIndexes.push_back(i);
}
-
/* -------------------------------------------------------------------------- */
-
juce::AudioProcessorEditor* Plugin::createEditor() const
{
- m::model::PluginsLock l(m::model::plugins);
return m_plugin.createEditor();
}
-
/* -------------------------------------------------------------------------- */
+const m::Plugin& Plugin::getPluginRef() const { return m_plugin; }
+
+/* -------------------------------------------------------------------------- */
void Plugin::setResizeCallback(std::function<void(int, int)> f)
{
m_plugin.onEditorResize = f;
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-Plugins::Plugins(const m::Channel& c)
+Plugins::Plugins(const m::channel::Data& c)
: channelId(c.id)
-, pluginIds(c.pluginIds)
+, plugins(c.plugins)
{
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Plugins getPlugins(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
- return Plugins(mm::get(mm::channels, channelId));
+ return Plugins(m::model::get().getChannel(channelId));
}
-
-Plugin getPlugin(ID pluginId, ID channelId)
+Plugin getPlugin(m::Plugin& plugin, ID channelId)
{
- m::model::PluginsLock l(m::model::plugins);
- return Plugin(m::model::get(m::model::plugins, pluginId), channelId);
+ return Plugin(plugin, channelId);
}
-
-Param getParam (int index, ID pluginId)
+Param getParam(int index, const m::Plugin& plugin, ID channelId)
{
- m::model::PluginsLock l(m::model::plugins);
- return Param(m::model::get(m::model::plugins, pluginId), index);
+ return Param(plugin, index, channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void updateWindow(ID pluginId, bool gui)
{
- m::model::PluginsLock l(m::model::plugins);
- const m::Plugin& p = m::model::get(m::model::plugins, pluginId);
+ m::Plugin* p = m::model::find<m::Plugin>(pluginId);
+
+ assert(p != nullptr);
- if (p.hasEditor())
+ if (p->hasEditor())
return;
/* Get the parent window first: the plug-in list. Then, if it exists, get
- the child window - the actual pluginWindow. */
+ the child window - the actual pluginWindow. */
v::gdPluginList* parent = static_cast<v::gdPluginList*>(u::gui::getSubwindow(G_MainWin, WID_FX_LIST));
if (parent == nullptr)
return;
v::gdPluginWindow* child = static_cast<v::gdPluginWindow*>(u::gui::getSubwindow(parent, pluginId + 1));
- if (child == nullptr)
+ if (child == nullptr)
return;
-
- if (!gui) Fl::lock();
+
+ if (!gui)
+ Fl::lock();
child->updateParameters(!gui);
- if (!gui) Fl::unlock();
+ if (!gui)
+ Fl::unlock();
}
-
/* -------------------------------------------------------------------------- */
-
void addPlugin(int pluginListIndex, ID channelId)
{
if (pluginListIndex >= m::pluginManager::countAvailablePlugins())
m::pluginHost::addPlugin(std::move(p), channelId);
}
-
/* -------------------------------------------------------------------------- */
-
-void swapPlugins(ID pluginId1, ID pluginId2, ID channelId)
+void swapPlugins(const m::Plugin& p1, const m::Plugin& p2, ID channelId)
{
- m::pluginHost::swapPlugin(pluginId1, pluginId2, channelId);
+ m::pluginHost::swapPlugin(p1, p2, channelId);
}
-
/* -------------------------------------------------------------------------- */
-
-void freePlugin(ID pluginId, ID channelId)
+void freePlugin(const m::Plugin& plugin, ID channelId)
{
- m::pluginHost::freePlugin(pluginId, channelId);
+ m::pluginHost::freePlugin(plugin, channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void setProgram(ID pluginId, int programIndex)
{
- m::pluginHost::setPluginProgram(pluginId, programIndex);
- updateWindow(pluginId, /*gui=*/true);
+ m::pluginHost::setPluginProgram(pluginId, programIndex);
+ updateWindow(pluginId, /*gui=*/true);
}
-
/* -------------------------------------------------------------------------- */
-
void toggleBypass(ID pluginId)
{
m::pluginHost::toggleBypass(pluginId);
}
-
/* -------------------------------------------------------------------------- */
-
void setPluginPathCb(void* data)
{
- v::gdBrowserDir* browser = (v::gdBrowserDir*) data;
+ v::gdBrowserDir* browser = (v::gdBrowserDir*)data;
- if (browser->getCurrentPath() == "") {
+ if (browser->getCurrentPath() == "")
+ {
v::gdAlert("Invalid path.");
return;
}
v::gdConfig* configWin = static_cast<v::gdConfig*>(u::gui::getSubwindow(G_MainWin, WID_CONFIG));
configWin->refreshVstPath();
}
-
-}}} // giada::c::plugin::
-
+} // namespace giada::c::plugin
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_PLUGIN_H
#define G_GLUE_PLUGIN_H
-
#ifdef WITH_VST
-
-#include <vector>
-#include <string>
#include "core/plugins/pluginHost.h"
#include "core/types.h"
+#include <string>
+#include <vector>
-
-namespace juce {
+namespace juce
+{
class AudioProcessorEditor;
}
-
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Plugin;
-class Channel;
}
-namespace c {
-namespace plugin
+namespace giada::m::channel
+{
+struct Data;
+}
+namespace giada::c::plugin
{
struct Program
{
- int index;
- std::string name;
+ int index;
+ std::string name;
};
struct Param
{
- Param() = default;
- Param(const m::Plugin&, int index);
-
- int index;
- ID pluginId;
- std::string name;
- std::string text;
- std::string label;
- float value;
+ Param() = default;
+ Param(const m::Plugin&, int index, ID channelId);
+
+ int index;
+ ID pluginId;
+ ID channelId;
+ std::string name;
+ std::string text;
+ std::string label;
+ float value;
};
struct Plugin
{
- Plugin(m::Plugin&, ID channelId);
-
- juce::AudioProcessorEditor* createEditor() const;
+ Plugin(m::Plugin&, ID channelId);
- void setResizeCallback(std::function<void(int, int)> f);
+ juce::AudioProcessorEditor* createEditor() const;
+ const m::Plugin& getPluginRef() const;
- ID id;
- ID channelId;
- bool valid;
- bool hasEditor;
- bool isBypassed;
- std::string name;
- std::string uniqueId;
- int currentProgram;
+ void setResizeCallback(std::function<void(int, int)> f);
- std::vector<Program> programs;
- std::vector<int> paramIndexes;
+ ID id;
+ ID channelId;
+ bool valid;
+ bool hasEditor;
+ bool isBypassed;
+ std::string name;
+ std::string uniqueId;
+ int currentProgram;
-private:
+ std::vector<Program> programs;
+ std::vector<int> paramIndexes;
- m::Plugin& m_plugin;
+ private:
+ m::Plugin& m_plugin;
};
struct Plugins
{
- Plugins() = default;
- Plugins(const m::Channel&);
+ Plugins() = default;
+ Plugins(const m::channel::Data&);
- ID channelId;
- std::vector<ID> pluginIds;
+ ID channelId;
+ std::vector<m::Plugin*> plugins;
};
/* get*
Returns ViewModel objects. */
Plugins getPlugins(ID channelId);
-Plugin getPlugin (ID pluginId, ID channelId);
-Param getParam (int index, ID pluginId);
+Plugin getPlugin(m::Plugin& plugin, ID channelId);
+Param getParam(int index, const m::Plugin& plugin, ID channelId);
/* updateWindow
Updates the editor-less plug-in window. This is useless if the plug-in has an
void updateWindow(ID pluginId, bool gui);
void addPlugin(int pluginListIndex, ID channelId);
-void swapPlugins(ID pluginId1, ID pluginId2, ID channelId);
-void freePlugin(ID pluginId, ID channelId);
+void swapPlugins(const m::Plugin& p1, const m::Plugin& p2, ID channelId);
+void freePlugin(const m::Plugin& plugin, ID channelId);
void setProgram(ID pluginId, int programIndex);
void toggleBypass(ID pluginId);
configuration window. */
void setPluginPathCb(void* data);
-}}} // giada::c::plugin::
-
+} // namespace giada::c::plugin
#endif
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "gui/dialogs/warnings.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "core/recorder.h"
+#include "core/action.h"
#include "core/channels/channel.h"
-#include "core/const.h"
#include "core/clock.h"
-#include "core/model/model.h"
+#include "core/const.h"
#include "core/kernelMidi.h"
-#include "core/recorderHandler.h"
-#include "core/recorder.h"
-#include "core/action.h"
#include "core/mixer.h"
+#include "core/model/model.h"
+#include "core/recorderHandler.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "recorder.h"
#include "utils/gui.h"
#include "utils/log.h"
-#include "recorder.h"
-
+#include <cassert>
-namespace giada {
-namespace c {
-namespace recorder
+namespace giada::c::recorder
{
void clearAllActions(ID channelId)
{
updateChannel(channelId, /*updateActionEditor=*/true);
}
-
/* -------------------------------------------------------------------------- */
-
void clearVolumeActions(ID channelId)
{
if (!v::gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
updateChannel(channelId, /*updateActionEditor=*/true);
}
-
/* -------------------------------------------------------------------------- */
-
void clearStartStopActions(ID channelId)
{
if (!v::gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
updateChannel(channelId, /*updateActionEditor=*/true);
}
-
/* -------------------------------------------------------------------------- */
-
void updateChannel(ID channelId, bool updateActionEditor)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.state->hasActions = m::recorder::hasActions(channelId);
- });
-
+ /* TODO - move somewhere else in the core area */
+ m::model::get().getChannel(channelId).hasActions = m::recorder::hasActions(channelId);
+ m::model::swap(m::model::SwapType::HARD);
+
if (updateActionEditor)
u::gui::refreshActionEditor();
}
-
-}}} // giada::c::recorder::
+} // namespace giada::c::recorder
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_RECORDER_H
#define G_GLUE_RECORDER_H
-
-namespace giada {
-namespace c {
-namespace recorder
+namespace giada::c::recorder
{
void clearAllActions(ID channelId);
void clearVolumeActions(ID channelId);
void clearStartStopActions(ID channelId);
void updateChannel(ID channelId, bool updateActionEditor);
-}}} // giada::c::recorder::
+} // namespace giada::c::recorder
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
+#include "gui/dialogs/sampleEditor.h"
+#include "channel.h"
+#include "core/const.h"
+#include "core/mixerHandler.h"
+#include "core/model/model.h"
+#include "core/wave.h"
+#include "core/waveManager.h"
#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/sampleEditor.h"
#include "gui/dialogs/warnings.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/sampleEditor/waveTools.h"
-#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/sampleEditor/boostTool.h"
#include "gui/elems/sampleEditor/panTool.h"
#include "gui/elems/sampleEditor/pitchTool.h"
#include "gui/elems/sampleEditor/rangeTool.h"
#include "gui/elems/sampleEditor/shiftTool.h"
+#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/sampleEditor/waveTools.h"
#include "gui/elems/sampleEditor/waveform.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "core/model/model.h"
-#include "core/wave.h"
-#include "core/waveManager.h"
-#include "core/mixerHandler.h"
-#include "core/const.h"
+#include "sampleEditor.h"
#include "utils/gui.h"
#include "utils/log.h"
-#include "channel.h"
-#include "sampleEditor.h"
-
+#include <FL/Fl.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
-namespace sampleEditor
+namespace giada::c::sampleEditor
{
namespace
{
+m::channel::Data& getChannel_(ID channelId)
+{
+ return m::model::get().getChannel(channelId);
+}
+
+m::samplePlayer::Data& getSamplePlayer_(ID channelId)
+{
+ return getChannel_(channelId).samplePlayer.value();
+}
+
+m::Wave& getWave_(ID channelId)
+{
+ return *const_cast<m::Wave*>(getSamplePlayer_(channelId).getWave());
+}
+
+/* -------------------------------------------------------------------------- */
+
/* waveBuffer
A Wave used during cut/copy/paste operations. */
Frame previewTracker_ = 0;
-
/* -------------------------------------------------------------------------- */
/* resetBeginEnd_
-Resets begin/end points when model has changed and a new Channel pointer is
-needed for the operation. */
+Resets begin/end points to 0/max. */
void resetBeginEnd_(ID channelId)
{
- m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
- {
- Frame begin = c.samplePlayer->state->begin.load();
- Frame end = c.samplePlayer->state->end.load();
- setBeginEnd(channelId, begin, end);
- });
+ Frame begin = getSamplePlayer_(channelId).begin;
+ Frame end = getSamplePlayer_(channelId).getWaveSize();
+ setBeginEnd(channelId, begin, end);
}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* updateWavePtr_
-Updates the Wave pointer in Channel::WaveReader. */
-
-void updateWavePtr_(ID channelId, ID waveId)
-{
- namespace mm = m::model;
-
- mm::WavesLock wl(mm::waves);
- const m::Wave& wave = mm::get(mm::waves, waveId);
-
- mm::onSwap(mm::channels, channelId, [&](m::Channel& c)
- {
- c.samplePlayer->loadWave(&wave);
- });
-}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-Data::Data(const m::Channel& c, const m::Wave& w)
-: channelId (c.id)
-, waveId (w.id)
-, name (c.state->name)
-, volume (c.state->volume.load())
-, pan (c.state->pan.load())
-, pitch (c.samplePlayer->state->pitch.load())
-, begin (c.samplePlayer->state->begin.load())
-, end (c.samplePlayer->state->end.load())
-, shift (c.samplePlayer->state->shift.load())
-, waveSize (w.getSize())
-, waveBits (w.getBits())
-, waveDuration(w.getDuration())
-, waveRate (w.getRate())
-, wavePath (w.getPath())
-, isLogical (w.isLogical())
+Data::Data(const m::channel::Data& c)
+: channelId(c.id)
+, name(c.name)
+, volume(c.volume)
+, pan(c.pan)
+, pitch(c.samplePlayer->pitch)
+, begin(c.samplePlayer->begin)
+, end(c.samplePlayer->end)
+, shift(c.samplePlayer->shift)
+, waveSize(c.samplePlayer->getWave()->getBuffer().countFrames())
+, waveBits(c.samplePlayer->getWave()->getBits())
+, waveDuration(c.samplePlayer->getWave()->getDuration())
+, waveRate(c.samplePlayer->getWave()->getRate())
+, wavePath(c.samplePlayer->getWave()->getPath())
+, isLogical(c.samplePlayer->getWave()->isLogical())
+, m_channel(&c)
{
}
-/* TODO - use c::channel::a_get() */
ChannelStatus Data::a_getPreviewStatus() const
{
- namespace mm = m::model;
-
- mm::ChannelsLock l(mm::channels);
- return mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).state->playStatus.load();
+ return getChannel_(m::mixer::PREVIEW_CHANNEL_ID).state->playStatus.load();
}
-/* TODO - use c::channel::a_get() */
Frame Data::a_getPreviewTracker() const
{
- namespace mm = m::model;
-
- mm::ChannelsLock l(mm::channels);
- return mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).samplePlayer->state->tracker.load();
+ return getChannel_(m::mixer::PREVIEW_CHANNEL_ID).state->tracker.load();
}
+const m::Wave& Data::getWaveRef() const
+{
+ return *m_channel->samplePlayer->getWave();
+}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
Data getData(ID channelId)
{
- namespace mm = m::model;
-
- mm::ChannelsLock cl(mm::channels);
- mm::WavesLock wl(mm::waves);
-
- const m::Channel& channel = mm::get(mm::channels, channelId);
- const m::Wave& wave = mm::get(mm::waves, channel.samplePlayer->getWaveId());
+ /* Prepare the preview channel first, then return Data object. */
+ m::samplePlayer::loadWave(getChannel_(m::mixer::PREVIEW_CHANNEL_ID), &getWave_(channelId));
+ m::model::swap(m::model::SwapType::SOFT);
- /* Prepare the preview channel. */
-
- m::Channel& preview = mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID);
- preview.samplePlayer->loadWave(&wave);
-
- return Data(channel, wave);
+ return Data(getChannel_(channelId));
}
-
/* -------------------------------------------------------------------------- */
-
void onRefresh(bool gui, std::function<void(v::gdSampleEditor&)> f)
{
v::gdSampleEditor* se = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
- if (se == nullptr)
+ if (se == nullptr)
return;
- if (!gui) Fl::lock();
+ if (!gui)
+ Fl::lock();
f(*se);
- if (!gui) Fl::unlock();
+ if (!gui)
+ Fl::unlock();
}
-
v::gdSampleEditor* getSampleEditorWindow()
{
v::gdSampleEditor* se = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
return se;
}
-
/* -------------------------------------------------------------------------- */
-
-void setBeginEnd(ID channelId, int b, int e)
+void setBeginEnd(ID channelId, Frame b, Frame e)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- b = std::clamp(b, 0, c.samplePlayer->getWaveSize() - 1);
- e = std::clamp(e, 1, c.samplePlayer->getWaveSize() - 1);
- if (b >= e) b = e - 1;
- else if (e < b) e = b + 1;
+ m::channel::Data& c = getChannel_(channelId);
- c.samplePlayer->state->begin.store(b);
- c.samplePlayer->state->end.store(e);
- if (c.samplePlayer->state->tracker.load() < b)
- c.samplePlayer->state->tracker.store(b);
- });
+ b = std::clamp(b, 0, c.samplePlayer->getWaveSize() - 1);
+ e = std::clamp(e, 1, c.samplePlayer->getWaveSize() - 1);
+ if (b >= e)
+ b = e - 1;
+ else if (e < b)
+ e = b + 1;
+
+ if (c.state->tracker.load() < b)
+ c.state->tracker.store(b);
+
+ getSamplePlayer_(channelId).begin = b;
+ getSamplePlayer_(channelId).end = e;
+ m::model::swap(m::model::SwapType::SOFT);
/* TODO waveform widget is dumb and wants a rebuild. Refactoring needed! */
getSampleEditorWindow()->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
-void cut(ID channelId, ID waveId, int a, int b)
+void cut(ID channelId, Frame a, Frame b)
{
- copy(waveId, a, b);
- m::wfx::cut(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ copy(channelId, a, b);
+ m::model::DataLock lock;
+ m::wfx::cut(getWave_(channelId), a, b);
resetBeginEnd_(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
-void copy(ID waveId, int a, int b)
+void copy(ID channelId, Frame a, Frame b)
{
- m::model::WavesLock lock(m::model::waves);
- waveBuffer_ = m::waveManager::createFromWave(m::model::get(m::model::waves, waveId), a, b);
+ waveBuffer_ = m::waveManager::createFromWave(getWave_(channelId), a, b);
}
-
/* -------------------------------------------------------------------------- */
-
-void paste(ID channelId, ID waveId, int a)
+void paste(ID channelId, Frame a)
{
- if (!isWaveBufferFull()) {
+ if (!isWaveBufferFull())
+ {
u::log::print("[sampleEditor::paste] Buffer is empty, nothing to paste\n");
return;
}
- m::wfx::paste(*waveBuffer_, waveId, a);
- updateWavePtr_(channelId, waveId);
+ /* Get the existing wave in channel. */
- /* Shift begin/end points to keep the previous position. */
+ m::Wave& wave = getWave_(channelId);
- int delta = waveBuffer_->getSize();
- Frame begin;
- Frame end;
+ /* Temporary disable wave reading in channel. From now on, the audio thread
+ won't be reading any wave, so editing it is safe. */
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- begin = c.samplePlayer->state->begin.load();
- end = c.samplePlayer->state->end.load();
- });
+ m::model::DataLock lock;
+
+ /* Paste copied data to destination wave. */
+
+ m::wfx::paste(*waveBuffer_, wave, a);
+
+ /* Pass the old wave that contains the pasted data to channel. */
+
+ m::samplePlayer::setWave(getChannel_(channelId), &wave, 1.0f);
+
+ /* In the meantime, shift begin/end points to keep the previous position. */
+
+ int delta = waveBuffer_->getBuffer().countFrames();
+ Frame begin = getSamplePlayer_(channelId).begin;
+ Frame end = getSamplePlayer_(channelId).end;
if (a < begin && a < end)
setBeginEnd(channelId, begin + delta, end + delta);
- else
- if (a < end)
+ else if (a < end)
setBeginEnd(channelId, begin, end + delta);
getSampleEditorWindow()->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
-void silence(ID channelId, ID waveId, int a, int b)
+void silence(ID channelId, int a, int b)
{
- m::wfx::silence(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::silence(getWave_(channelId), a, b);
}
-
/* -------------------------------------------------------------------------- */
-
-void fade(ID channelId, ID waveId, int a, int b, m::wfx::Fade type)
+void fade(ID channelId, int a, int b, m::wfx::Fade type)
{
- m::wfx::fade(waveId, a, b, type);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::fade(getWave_(channelId), a, b, type);
}
-
/* -------------------------------------------------------------------------- */
-
-void smoothEdges(ID channelId, ID waveId, int a, int b)
+void smoothEdges(ID channelId, int a, int b)
{
- m::wfx::smooth(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::smooth(getWave_(channelId), a, b);
}
-
/* -------------------------------------------------------------------------- */
-
-void reverse(ID channelId, ID waveId, int a, int b)
+void reverse(ID channelId, Frame a, Frame b)
{
- m::wfx::reverse(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::reverse(getWave_(channelId), a, b);
}
-
/* -------------------------------------------------------------------------- */
-
-void normalize(ID channelId, ID waveId, int a, int b)
+void normalize(ID channelId, int a, int b)
{
- m::wfx::normalize(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::normalize(getWave_(channelId), a, b);
}
-
/* -------------------------------------------------------------------------- */
-
-void trim(ID channelId, ID waveId, int a, int b)
+void trim(ID channelId, int a, int b)
{
- m::wfx::trim(waveId, a, b);
- updateWavePtr_(channelId, waveId);
+ m::model::DataLock lock;
+ m::wfx::trim(getWave_(channelId), a, b);
resetBeginEnd_(channelId);
}
-
/* -------------------------------------------------------------------------- */
-
/* TODO - this arcane logic of keeping previewTracker_ will go away as soon as
the One-shot pause mode is implemented:
https://github.com/monocasual/giada/issues/88 */
void playPreview(bool loop)
-{
+{
setPreviewTracker(previewTracker_);
channel::setSamplePlayerMode(m::mixer::PREVIEW_CHANNEL_ID, loop ? SamplePlayerMode::SINGLE_ENDLESS : SamplePlayerMode::SINGLE_BASIC);
events::pressChannel(m::mixer::PREVIEW_CHANNEL_ID, G_MAX_VELOCITY, Thread::MAIN);
}
-
void stopPreview()
{
/* Let the Sample Editor show the initial tracker position, then kill the
events::killChannel(m::mixer::PREVIEW_CHANNEL_ID, Thread::MAIN);
}
-
void setPreviewTracker(Frame f)
{
namespace mm = m::model;
- mm::onGet(mm::channels, m::mixer::PREVIEW_CHANNEL_ID, [&](m::Channel& c)
- {
- c.samplePlayer->state->tracker.store(f);
- });
+ mm::get().getChannel(m::mixer::PREVIEW_CHANNEL_ID).state->tracker.store(f);
+ mm::swap(mm::SwapType::SOFT);
previewTracker_ = f;
getSampleEditorWindow()->refresh();
}
-
void cleanupPreview()
{
namespace mm = m::model;
- mm::ChannelsLock cl(mm::channels);
- mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).samplePlayer->loadWave(nullptr);
+ m::samplePlayer::loadWave(mm::get().getChannel(m::mixer::PREVIEW_CHANNEL_ID), nullptr);
+ mm::swap(mm::SwapType::SOFT);
}
-
/* -------------------------------------------------------------------------- */
-
-void toNewChannel(ID channelId, ID waveId, int a, int b)
+void toNewChannel(ID channelId, Frame a, Frame b)
{
ID columnId = G_MainWin->keyboard->getChannel(channelId)->getColumnId();
-
- m::model::onGet(m::model::waves, waveId, [&](m::Wave& w)
- {
- m::mh::addAndLoadChannel(columnId, m::waveManager::createFromWave(w, a, b));
- });
+ m::mh::addAndLoadChannel(columnId, m::waveManager::createFromWave(getWave_(channelId), a, b));
}
-
/* -------------------------------------------------------------------------- */
-
bool isWaveBufferFull()
{
return waveBuffer_ != nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
-void reload(ID channelId, ID waveId)
+void reload(ID channelId)
{
if (!v::gdConfirmWin("Warning", "Reload sample: are you sure?"))
return;
- std::string wavePath;
- m::model::onGet(m::model::waves, waveId, [&](const m::Wave& w)
+ if (channel::loadChannel(channelId, getWave_(channelId).getPath()) != G_RES_OK)
{
- wavePath = w.getPath();
- });
-
- if (channel::loadChannel(channelId, wavePath) != G_RES_OK) {
v::gdAlert("Unable to reload sample!");
return;
}
getSampleEditorWindow()->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
-void shift(ID channelId, ID waveId, int offset)
+void shift(ID channelId, Frame offset)
{
- Frame shift;
- m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
- {
- shift = c.samplePlayer->state->shift.load();
- });
-
- m::wfx::shift(waveId, offset - shift);
- updateWavePtr_(channelId, waveId);
+ namespace mm = m::model;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.samplePlayer->state->shift.store(offset);
- });
+ Frame shift = getSamplePlayer_(channelId).shift;
+
+ mm::DataLock lock();
+ m::wfx::shift(getWave_(channelId), offset - shift);
+ getSamplePlayer_(channelId).shift = offset;
getSampleEditorWindow()->shiftTool->update(offset);
}
-}}} // giada::c::sampleEditor::
+} // namespace giada::c::sampleEditor
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_SAMPLE_EDITOR_H
#define G_GLUE_SAMPLE_EDITOR_H
-
-#include <functional>
-#include <string>
#include "core/types.h"
#include "core/waveFx.h"
+#include <functional>
+#include <string>
-
-namespace giada {
-namespace m
+namespace giada::m
{
-class Channel;
class Wave;
}
-namespace v
+namespace giada::m::channel
+{
+struct Data;
+}
+namespace giada::v
{
class gdSampleEditor;
}
-namespace c {
-namespace sampleEditor
+namespace giada::c::sampleEditor
{
struct Data
{
- Data() = default;
- Data(const m::Channel&, const m::Wave&);
-
- ChannelStatus a_getPreviewStatus() const;
- Frame a_getPreviewTracker() const;
-
- ID channelId;
- ID waveId;
- std::string name;
- float volume;
- float pan;
- float pitch;
- Frame begin;
- Frame end;
- Frame shift;
- Frame waveSize;
- int waveBits;
- int waveDuration;
- int waveRate;
- std::string wavePath;
- bool isLogical;
+ Data() = default;
+ Data(const m::channel::Data&);
+
+ ChannelStatus a_getPreviewStatus() const;
+ Frame a_getPreviewTracker() const;
+ const m::Wave& getWaveRef() const; // TODO - getWaveData (or public ptr member to Wave::data)
+
+ ID channelId;
+ std::string name;
+ float volume;
+ float pan;
+ float pitch;
+ Frame begin;
+ Frame end;
+ Frame shift;
+ Frame waveSize;
+ int waveBits;
+ int waveDuration;
+ int waveRate;
+ std::string wavePath;
+ bool isLogical;
+
+ private:
+ const m::channel::Data* m_channel;
};
/* onRefresh --- TODO - wrong name */
/* setBeginEnd
Sets start/end points in the sample editor. */
-void setBeginEnd(ID channelId, int b, int e);
+void setBeginEnd(ID channelId, Frame b, Frame e);
-void cut(ID channelId, ID waveId, int a, int b);
-void copy(ID waveId, int a, int b);
-void paste(ID channelId, ID waveId, int a);
+void cut(ID channelId, Frame a, Frame b);
+void copy(ID channelId, Frame a, Frame b);
+void paste(ID channelId, Frame a);
-void trim(ID channelId, ID waveId, int a, int b);
-void reverse(ID channelId, ID waveId, int a, int b);
-void normalize(ID channelId, ID waveId, int a, int b);
-void silence(ID channelId, ID waveId, int a, int b);
-void fade(ID channelId, ID waveId, int a, int b, m::wfx::Fade type);
-void smoothEdges(ID channelId, ID waveId, int a, int b);
-void shift(ID channelId, ID waveId, int offset);
-void reload(ID channelId, ID waveId);
+void trim(ID channelId, Frame a, Frame b);
+void reverse(ID channelId, Frame a, Frame b);
+void normalize(ID channelId, Frame a, Frame b);
+void silence(ID channelId, Frame a, Frame b);
+void fade(ID channelId, Frame a, Frame b, m::wfx::Fade type);
+void smoothEdges(ID channelId, Frame a, Frame b);
+void shift(ID channelId, Frame offset);
+void reload(ID channelId);
bool isWaveBufferFull();
/* toNewChannel
Copies the selected range into a new sample channel. */
-void toNewChannel(ID channelId, ID waveId, int a, int b);
-}}} // giada::c::sampleEditor::
+void toNewChannel(ID channelId, Frame a, Frame b);
+} // namespace giada::c::sampleEditor
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/model/model.h"
#include "core/model/storage.h"
+#include "channel.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/init.h"
#include "core/mixer.h"
-#include "core/wave.h"
#include "core/mixerHandler.h"
-#include "core/recorderHandler.h"
-#include "core/plugins/pluginManager.h"
-#include "core/plugins/pluginHost.h"
-#include "core/plugins/plugin.h"
-#include "core/conf.h"
+#include "core/model/model.h"
#include "core/patch.h"
-#include "core/init.h"
-#include "core/waveManager.h"
-#include "core/clock.h"
+#include "core/plugins/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/recorderHandler.h"
#include "core/wave.h"
-#include "utils/gui.h"
-#include "utils/log.h"
-#include "utils/string.h"
-#include "utils/fs.h"
-#include "gui/model.h"
+#include "core/waveManager.h"
+#include "gui/dialogs/browser/browserLoad.h"
+#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/warnings.h"
#include "gui/elems/basics/progress.h"
#include "gui/elems/mainWindow/keyboard/column.h"
#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/warnings.h"
-#include "gui/dialogs/browser/browserSave.h"
-#include "gui/dialogs/browser/browserLoad.h"
+#include "gui/model.h"
#include "main.h"
-#include "channel.h"
#include "storage.h"
-
+#include "utils/fs.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace c {
+namespace giada
+{
+namespace c
+{
namespace storage
{
namespace
{
std::string makeWavePath_(const std::string& base, const m::Wave& w, int k)
{
- return base + G_SLASH + w.getBasename(/*ext=*/false) + "-" + std::to_string(k) + "." + w.getExtension();
-}
-
+ return base + G_SLASH + w.getBasename(/*ext=*/false) + "-" + std::to_string(k) + w.getExtension();
+}
bool isWavePathUnique_(const m::Wave& skip, const std::string& path)
{
- m::model::WavesLock l(m::model::waves);
-
- for (const m::Wave* w : m::model::waves)
+ for (const auto& w : m::model::getAll<m::model::WavePtrs>())
if (w->id != skip.id && w->getPath() == path)
return false;
return true;
// TODO - just use a timestamp. e.g. makeWavePath_(..., ..., getTimeStamp())
int k = 0;
- path = makeWavePath_(base, w, k);
+ path = makeWavePath_(base, w, k);
while (!isWavePathUnique_(w, path))
path = makeWavePath_(base, w, k++);
-
+
return path;
}
-
/* -------------------------------------------------------------------------- */
-
bool savePatch_(const std::string& path, const std::string& name)
{
m::patch::init();
return true;
}
-
/* -------------------------------------------------------------------------- */
-
void saveWavesToProject_(const std::string& basePath)
{
- /* No need for a hard Wave swap here: nobody is reading the path data. */
-
- m::model::WavesLock l(m::model::waves);
-
- for (m::Wave* w : m::model::waves) {
+ for (const std::unique_ptr<m::Wave>& w : m::model::getAll<m::model::WavePtrs>())
+ {
w->setPath(makeUniqueWavePath_(basePath, *w));
- m::waveManager::save(*w, w->getPath()); // TODO - error checking
+ m::waveManager::save(*w, w->getPath()); // TODO - error checking
}
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void loadProject(void* data)
{
- v::gdBrowserLoad* browser = static_cast<v::gdBrowserLoad*>(data);
- std::string fullPath = browser->getSelectedItem();
- bool isProject = u::fs::isProject(browser->getSelectedItem());
+ v::gdBrowserLoad* browser = static_cast<v::gdBrowserLoad*>(data);
+ std::string fullPath = browser->getSelectedItem();
browser->showStatusBar();
u::log::print("[loadProject] load from %s\n", fullPath);
- std::string fileToLoad = fullPath; // patch file to read from
- std::string basePath = ""; // base path, in case of reading from a project
- if (isProject) {
- fileToLoad = fullPath + G_SLASH + u::fs::stripExt(u::fs::basename(fullPath)) + ".gptc";
- basePath = fullPath + G_SLASH;
- }
+ std::string fileToLoad = fullPath + G_SLASH + u::fs::stripExt(u::fs::basename(fullPath)) + ".gptc";
+ std::string basePath = fullPath + G_SLASH;
/* Read the patch from file. */
m::patch::init();
int res = m::patch::read(fileToLoad, basePath);
- if (res != G_PATCH_OK) {
+ if (res != G_PATCH_OK)
+ {
if (res == G_PATCH_UNREADABLE)
v::gdAlert("This patch is unreadable.");
- else
- if (res == G_PATCH_INVALID)
+ else if (res == G_PATCH_INVALID)
v::gdAlert("This patch is not valid.");
- else
- if (res == G_PATCH_UNSUPPORTED)
+ else if (res == G_PATCH_UNSUPPORTED)
v::gdAlert("This patch format is no longer supported.");
browser->hideStatusBar();
return;
- }
-
- if (!isProject)
- v::gdAlert("Support for raw patches is deprecated\nand will be removed soon!");
+ }
/* Then reset the system (it disables mixer) and fill the model. */
m::init::reset();
- m::model::load(m::patch::patch);
v::model::load(m::patch::patch);
+ m::model::load(m::patch::patch);
/* Prepare the engine. Recorder has to recompute the actions positions if
the current samplerate != patch samplerate. Clock needs to update frames
m::mh::updateSoloCount();
m::recorderHandler::updateSamplerate(m::conf::conf.samplerate, m::patch::patch.samplerate);
m::clock::recomputeFrames();
- m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
+ m::mixer::allocRecBuffer(m::clock::getMaxFramesInLoop());
/* Mixer is ready to go back online. */
browser->do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void saveProject(void* data)
{
- v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
- std::string name = u::fs::stripExt(browser->getName());
- std::string folderPath = browser->getCurrentPath();
- std::string fullPath = folderPath + G_SLASH + name + ".gprj";
- std::string gptcPath = fullPath + G_SLASH + name + ".gptc";
+ v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
+ std::string name = u::fs::stripExt(browser->getName());
+ std::string folderPath = browser->getCurrentPath();
+ std::string fullPath = folderPath + G_SLASH + name + ".gprj";
+ std::string gptcPath = fullPath + G_SLASH + name + ".gptc";
- if (name == "") {
+ if (name == "")
+ {
v::gdAlert("Please choose a project name.");
return;
}
if (u::fs::dirExists(fullPath) && !v::gdConfirmWin("Warning", "Project exists: overwrite?"))
return;
- if (!u::fs::mkdir(fullPath)) {
+ if (!u::fs::mkdir(fullPath))
+ {
u::log::print("[saveProject] Unable to make project directory!\n");
return;
}
browser->do_callback();
else
v::gdAlert("Unable to save the project!");
-
}
-
/* -------------------------------------------------------------------------- */
-
void loadSample(void* data)
{
v::gdBrowserLoad* browser = static_cast<v::gdBrowserLoad*>(data);
int res = c::channel::loadChannel(browser->getChannelId(), fullPath);
- if (res == G_RES_OK) {
+ if (res == G_RES_OK)
+ {
m::conf::conf.samplePath = u::fs::dirname(fullPath);
browser->do_callback();
G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
}
}
-
/* -------------------------------------------------------------------------- */
-
void saveSample(void* data)
{
- v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
- std::string name = browser->getName();
- std::string folderPath = browser->getCurrentPath();
- ID channelId = browser->getChannelId();
+ v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
+ std::string name = browser->getName();
+ std::string folderPath = browser->getCurrentPath();
+ ID channelId = browser->getChannelId();
- if (name == "") {
+ if (name == "")
+ {
v::gdAlert("Please choose a file name.");
return;
}
if (u::fs::fileExists(filePath) && !v::gdConfirmWin("Warning", "File exists: overwrite?"))
return;
- ID waveId;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- waveId = c.samplePlayer->getWaveId();
- });
-
- std::size_t waveIndex = m::model::getIndex(m::model::waves, waveId);
+ ID waveId = m::model::get().getChannel(channelId).samplePlayer->getWaveId();
+ m::Wave* wave = m::model::find<m::Wave>(waveId);
- std::unique_ptr<m::Wave> wave = m::model::waves.clone(waveIndex);
+ assert(wave != nullptr);
- if (!m::waveManager::save(*wave.get(), filePath)) {
+ if (!m::waveManager::save(*wave, filePath))
+ {
v::gdAlert("Unable to save this sample!");
return;
}
/* Update logical and edited states in Wave. */
+ m::model::DataLock lock;
wave->setLogical(false);
wave->setEdited(false);
- m::model::waves.swap(std::move(wave), waveIndex);
-
/* Finally close the browser. */
browser->do_callback();
}
-}}} // giada::c::storage::
+} // namespace storage
+} // namespace c
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_GLUE_STORAGE_H
#define G_GLUE_STORAGE_H
-
-namespace giada {
-namespace c {
+namespace giada
+{
+namespace c
+{
namespace storage
{
void loadProject(void* data);
void saveProject(void* data);
-void saveSample (void* data);
-void loadSample (void* data);
-}}} // giada::c::storage::
+void saveSample(void* data);
+void loadSample(void* data);
+} // namespace storage
+} // namespace c
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Pixmap.H>
-#include <FL/fl_draw.H>
#include "core/conf.h"
#include "core/const.h"
#include "core/graphics.h"
+#include <FL/Fl_Pixmap.H>
+#include <FL/fl_draw.H>
#ifdef WITH_VST
#include "deps/juce-config.h"
#endif
+#include "about.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "utils/gui.h"
#include "utils/string.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/box.h"
-#include "about.h"
-
-namespace giada {
-namespace v
+namespace giada::v
{
gdAbout::gdAbout()
#ifdef WITH_VST
#else
: gdWindow(340, 330, "About Giada")
#endif
-{
- set_modal();
-
- logo = new geBox(8, 20, 324, 86);
- text = new geBox(8, 120, 324, 140);
- close = new geButton(252, h()-28, 80, 20, "Close");
+, logo(8, 20, 324, 86)
+, text(8, 120, 324, 140)
+, close(252, h() - 28, 80, 20, "Close")
#ifdef WITH_VST
- vstLogo = new geBox(8, 265, 324, 50);
- vstText = new geBox(8, 315, 324, 46);
+, vstText(8, 315, 324, 46)
+, vstLogo(8, 265, 324, 50)
#endif
+{
end();
+ set_modal();
std::string version = G_VERSION_STR;
#ifdef G_DEBUG_MODE
version += " (debug build)";
#endif
- logo->image(new Fl_Pixmap(giada_logo_xpm));
- text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
- text->copy_label(std::string(
- "Version " + version + " (" BUILD_DATE ")\n\n"
- "Developed by Monocasual Laboratories\n\n"
- "Released under the terms of the GNU General\n"
- "Public License (GPL v3)\n\n"
- "News, infos, contacts and documentation:\n"
- "www.giadamusic.com").c_str());
+ logo.image(new Fl_Pixmap(giada_logo_xpm));
+ text.align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
+ text.copy_label(std::string(
+ "Version " + version + " (" BUILD_DATE ")\n\n"
+ "Developed by Monocasual Laboratories\n\n"
+ "Released under the terms of the GNU General\n"
+ "Public License (GPL v3)\n\n"
+ "News, infos, contacts and documentation:\n"
+ "www.giadamusic.com")
+ .c_str());
#ifdef WITH_VST
- vstLogo->image(new Fl_Pixmap(vstLogo_xpm));
- vstLogo->position(vstLogo->x(), text->y() + text->h() + 8);
- vstText->label(
- "VST Plug-In Technology by Steinberg\n"
- "VST is a trademark of Steinberg\nMedia Technologies GmbH"
- );
- vstText->position(vstText->x(), vstLogo->y()+vstLogo->h());
+ vstLogo.image(new Fl_Pixmap(vstLogo_xpm));
+ vstLogo.position(vstLogo.x(), text.y() + text.h() + 8);
+ vstText.label(
+ "VST Plug-In Technology by Steinberg\n"
+ "VST is a trademark of Steinberg\nMedia Technologies GmbH");
+ vstText.position(vstText.x(), vstLogo.y() + vstLogo.h());
#endif
- close->callback(cb_close, (void*)this);
+ close.callback(cb_close, (void*)this);
u::gui::setFavicon(this);
setId(WID_ABOUT);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdAbout::cb_close(Fl_Widget* /*w*/, void* p) { ((gdAbout*)p)->cb_close(); }
-
/* -------------------------------------------------------------------------- */
-
void gdAbout::cb_close()
{
do_callback();
}
-
-}} // giada::v::
\ No newline at end of file
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_ABOUT_H
#define GD_ABOUT_H
+#include "gui/dialogs/window.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
-#include "window.h"
-
-
-class geBox;
-class geButton;
-
-
-namespace giada {
-namespace v
+namespace giada::v
{
class gdAbout : public gdWindow
{
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();
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
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <string>
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "core/conf.h"
+#include "baseActionEditor.h"
#include "core/action.h"
-#include "core/const.h"
#include "core/clock.h"
+#include "core/conf.h"
+#include "core/const.h"
#include "core/midiEvent.h"
#include "glue/channel.h"
#include "gui/elems/actionEditor/gridTool.h"
-#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/choice.h"
-#include "baseActionEditor.h"
-
+#include "gui/elems/basics/scrollPack.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include <cassert>
+#include <string>
-namespace giada {
+namespace giada
+{
namespace v
{
gdBaseActionEditor::gdBaseActionEditor(ID channelId)
-: gdWindow (640, 284)
+: gdWindow(640, 284)
, channelId(channelId)
-, ratio (G_DEFAULT_ZOOM_RATIO)
+, ratio(G_DEFAULT_ZOOM_RATIO)
{
using namespace giada::m;
- if (conf::conf.actionEditorW) {
- resize(conf::conf.actionEditorX, conf::conf.actionEditorY,
- conf::conf.actionEditorW, conf::conf.actionEditorH);
+ if (conf::conf.actionEditorW)
+ {
+ resize(conf::conf.actionEditorX, conf::conf.actionEditorY,
+ conf::conf.actionEditorW, conf::conf.actionEditorH);
ratio = conf::conf.actionEditorZoom;
}
}
-
/* -------------------------------------------------------------------------- */
-
gdBaseActionEditor::~gdBaseActionEditor()
{
using namespace giada::m;
- conf::conf.actionEditorX = x();
- conf::conf.actionEditorY = y();
- conf::conf.actionEditorW = w();
- conf::conf.actionEditorH = h();
+ conf::conf.actionEditorX = x();
+ conf::conf.actionEditorY = y();
+ conf::conf.actionEditorW = w();
+ conf::conf.actionEditorH = h();
conf::conf.actionEditorZoom = ratio;
}
-
/* -------------------------------------------------------------------------- */
-
-void gdBaseActionEditor::cb_zoomIn(Fl_Widget* /*w*/, void* p) { ((gdBaseActionEditor*)p)->zoomIn(); }
+void gdBaseActionEditor::cb_zoomIn(Fl_Widget* /*w*/, void* p) { ((gdBaseActionEditor*)p)->zoomIn(); }
void gdBaseActionEditor::cb_zoomOut(Fl_Widget* /*w*/, void* p) { ((gdBaseActionEditor*)p)->zoomOut(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBaseActionEditor::computeWidth()
{
fullWidth = frameToPixel(m::clock::getFramesInSeq());
loopWidth = frameToPixel(m::clock::getFramesInLoop());
}
-
/* -------------------------------------------------------------------------- */
-
Pixel gdBaseActionEditor::frameToPixel(Frame f) const
{
return f / ratio;
}
-
Frame gdBaseActionEditor::pixelToFrame(Pixel p, bool snap) const
{
return snap ? gridTool->getSnapFrame(p * ratio) : p * ratio;
}
-
/* -------------------------------------------------------------------------- */
-
void gdBaseActionEditor::zoomIn()
{
float ratioPrev = ratio;
if (ratio < MIN_RATIO)
ratio = MIN_RATIO;
- if (ratioPrev != ratio) {
+ if (ratioPrev != ratio)
+ {
rebuild();
centerViewportIn();
redraw();
}
}
-
/* -------------------------------------------------------------------------- */
-
void gdBaseActionEditor::zoomOut()
{
float ratioPrev = ratio;
-
+
ratio *= 2;
if (ratio > MAX_RATIO)
ratio = MAX_RATIO;
- if (ratioPrev != ratio) {
+ if (ratioPrev != ratio)
+ {
rebuild();
centerViewportOut();
redraw();
}
}
-
/* -------------------------------------------------------------------------- */
-
void gdBaseActionEditor::centerViewportIn()
{
Pixel sx = Fl::event_x() + (viewport->xposition() * 2);
- viewport->scroll_to(sx, viewport->yposition());
+ viewport->scroll_to(sx, viewport->yposition());
}
-
void gdBaseActionEditor::centerViewportOut()
{
Pixel sx = -((Fl::event_x() + viewport->xposition()) / 2) + viewport->xposition();
- if (sx < 0) sx = 0;
+ if (sx < 0)
+ sx = 0;
viewport->scroll_to(sx, viewport->yposition());
}
-
/* -------------------------------------------------------------------------- */
-
int gdBaseActionEditor::getActionType() const
{
if (actionType->value() == 0)
return m::MidiEvent::NOTE_ON;
- else
- if (actionType->value() == 1)
+ else if (actionType->value() == 1)
return m::MidiEvent::NOTE_OFF;
- else
- if (actionType->value() == 2)
+ else if (actionType->value() == 2)
return m::MidiEvent::NOTE_KILL;
assert(false);
return -1;
}
-
/* -------------------------------------------------------------------------- */
-
void gdBaseActionEditor::prepareWindow()
{
u::gui::setFavicon(this);
std::string l = "Action Editor";
- if (m_data.channelName != "") l += " - " + m_data.channelName;
+ if (m_data.channelName != "")
+ l += " - " + m_data.channelName;
copy_label(l.c_str());
set_non_modal();
show();
}
-
/* -------------------------------------------------------------------------- */
-
int gdBaseActionEditor::handle(int e)
{
- switch (e) {
- case FL_MOUSEWHEEL:
- Fl::event_dy() == -1 ? zoomIn() : zoomOut();
- return 1;
- default:
- return Fl_Group::handle(e);
+ switch (e)
+ {
+ case FL_MOUSEWHEEL:
+ Fl::event_dy() == -1 ? zoomIn() : zoomOut();
+ return 1;
+ default:
+ return Fl_Group::handle(e);
}
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BASE_ACTION_EDITOR_H
#define GD_BASE_ACTION_EDITOR_H
-
#include "core/types.h"
#include "glue/actionEditor.h"
#include "gui/dialogs/window.h"
-
class geButton;
-
-namespace giada {
+namespace giada
+{
namespace m
{
class Channel;
struct Action;
-}
+} // namespace m
namespace v
{
class geChoice;
class gdBaseActionEditor : public gdWindow
{
public:
-
virtual ~gdBaseActionEditor();
int handle(int e) override;
Pixel frameToPixel(Frame f) const;
- Frame pixelToFrame(Pixel p, bool snap=true) const;
- int getActionType() const;
+ Frame pixelToFrame(Pixel p, bool snap = true) const;
+ int getActionType() const;
ID channelId;
geGridTool* gridTool;
geButton* zoomInBtn;
geButton* zoomOutBtn;
- geScrollPack* viewport; // widget container
+ geScrollPack* viewport; // widget container
float ratio;
- Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer
- Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range
-
-protected:
+ Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer
+ Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range
+ protected:
static constexpr Pixel RESIZER_BAR_H = 20;
static constexpr Pixel MIN_WIDGET_H = 10;
static constexpr float MIN_RATIO = 25.0f;
gdBaseActionEditor(ID channelId);
- void zoomIn();
- void zoomOut();
+ void zoomIn();
+ void zoomOut();
static void cb_zoomIn(Fl_Widget* /*w*/, void* p);
static void cb_zoomOut(Fl_Widget* /*w*/, void* p);
-
+
/* computeWidth
Computes total width, in pixel. */
c::actionEditor::Data m_data;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <string>
+#include "midiActionEditor.h"
#include "core/graphics.h"
#include "glue/actionEditor.h"
#include "glue/channel.h"
-#include "gui/elems/basics/scrollPack.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/resizerBar.h"
-#include "gui/elems/basics/box.h"
+#include "gui/elems/actionEditor/gridTool.h"
#include "gui/elems/actionEditor/noteEditor.h"
-#include "gui/elems/actionEditor/velocityEditor.h"
#include "gui/elems/actionEditor/pianoRoll.h"
-#include "gui/elems/actionEditor/gridTool.h"
-#include "midiActionEditor.h"
-
+#include "gui/elems/actionEditor/velocityEditor.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/resizerBar.h"
+#include "gui/elems/basics/scrollPack.h"
+#include <string>
-namespace giada {
+namespace giada
+{
namespace v
{
gdMidiActionEditor::gdMidiActionEditor(ID channelId)
computeWidth();
gePack* upperArea = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL);
- gridTool = new geGridTool(0, 0);
- geBox* b1 = new geBox (0, 0, w() - 150, G_GUI_UNIT); // padding actionType - zoomButtons
- zoomInBtn = new geButton (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOutBtn = new geButton (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
- upperArea->add(gridTool);
- upperArea->add(b1);
- upperArea->add(zoomInBtn);
- upperArea->add(zoomOutBtn);
+ gridTool = new geGridTool(0, 0);
+ geBox* b1 = new geBox(0, 0, w() - 150, G_GUI_UNIT); // padding actionType - zoomButtons
+ zoomInBtn = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ upperArea->add(gridTool);
+ upperArea->add(b1);
+ upperArea->add(zoomInBtn);
+ upperArea->add(zoomOutBtn);
upperArea->resizable(b1);
/* Main viewport: contains all widgets. */
- viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
- upperArea->w(), h()-44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
- m_ne = new geNoteEditor (0, 0, this);
- m_ner = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
- m_ve = new geVelocityEditor(0, 0, this);
- m_ver = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
+ upperArea->w(), h() - 44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
+ m_ne = new geNoteEditor(0, 0, this);
+ m_ner = new geResizerBar(0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ m_ve = new geVelocityEditor(0, 0, this);
+ m_ver = new geResizerBar(0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ne);
viewport->add(m_ner);
viewport->add(m_ve);
viewport->add(m_ver);
zoomInBtn->callback(cb_zoomIn, (void*)this);
+ zoomInBtn->copy_tooltip("Zoom in");
zoomOutBtn->callback(cb_zoomOut, (void*)this);
+ zoomOutBtn->copy_tooltip("Zoom out");
add(upperArea);
add(viewport);
rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiActionEditor::rebuild()
{
m_data = c::actionEditor::getData(channelId);
m_ve->rebuild(m_data);
m_ver->size(m_ve->w(), m_ver->h());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_ACTION_EDITOR_H
#define GD_MIDI_ACTION_EDITOR_H
-
#include "baseActionEditor.h"
-
class geResizerBar;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geNoteEditor;
class gdMidiActionEditor : public gdBaseActionEditor
{
public:
+ gdMidiActionEditor(ID channelId);
- gdMidiActionEditor(ID channelId);
-
- void rebuild() override;
+ void rebuild() override;
-private:
-
- geNoteEditor* m_ne;
- geResizerBar* m_ner;
+ private:
+ geNoteEditor* m_ne;
+ geResizerBar* m_ner;
geVelocityEditor* m_ve;
- geResizerBar* m_ver;
+ geResizerBar* m_ver;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <string>
-#include "core/model/model.h"
+#include "gui/elems/actionEditor/sampleActionEditor.h"
#include "core/const.h"
-#include "core/midiEvent.h"
#include "core/graphics.h"
+#include "core/midiEvent.h"
+#include "core/model/model.h"
#include "glue/actionEditor.h"
#include "glue/channel.h"
-#include "gui/elems/basics/pack.h"
-#include "gui/elems/basics/scrollPack.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/resizerBar.h"
-#include "gui/elems/basics/choice.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/actionEditor/sampleActionEditor.h"
#include "gui/elems/actionEditor/envelopeEditor.h"
#include "gui/elems/actionEditor/gridTool.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/resizerBar.h"
+#include "gui/elems/basics/scrollPack.h"
#include "sampleActionEditor.h"
+#include <string>
-
-namespace giada {
+namespace giada
+{
namespace v
{
gdSampleActionEditor::gdSampleActionEditor(ID channelId)
resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
gePack* upperArea = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL);
- actionType = new geChoice (0, 0, 80, 20);
- gridTool = new geGridTool(0, 0);
- geBox* b1 = new geBox (0, 0, w() - 232, 20); // padding actionType - zoomButtons
- zoomInBtn = new geButton (0, 0, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOutBtn = new geButton (0, 0, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ actionType = new geChoice(0, 0, 80, 20);
+ gridTool = new geGridTool(0, 0);
+ geBox* b1 = new geBox(0, 0, w() - 232, 20); // padding actionType - zoomButtons
+ zoomInBtn = new geButton(0, 0, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton(0, 0, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
upperArea->add(actionType);
upperArea->add(gridTool);
upperArea->add(b1);
actionType->add("Key release");
actionType->add("Kill chan");
actionType->value(0);
+ actionType->copy_tooltip("Action type to add");
if (!canChangeActionType())
actionType->deactivate();
zoomInBtn->callback(cb_zoomIn, (void*)this);
+ zoomInBtn->copy_tooltip("Zoom in");
zoomOutBtn->callback(cb_zoomOut, (void*)this);
+ zoomOutBtn->copy_tooltip("Zoom out");
/* Main viewport: contains all widgets. */
- viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
- upperArea->w(), h()-44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
- m_ae = new geSampleActionEditor(0, 0, this);
- m_aer = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
- m_ee = new geEnvelopeEditor (0, 0, "volume", this);
- m_eer = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
+ upperArea->w(), h() - 44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
+ m_ae = new geSampleActionEditor(0, 0, this);
+ m_aer = new geResizerBar(0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ m_ee = new geEnvelopeEditor(0, 0, "volume", this);
+ m_eer = new geResizerBar(0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ae);
viewport->add(m_aer);
viewport->add(m_ee);
rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
bool gdSampleActionEditor::canChangeActionType()
{
- return m_data.sample->channelMode != SamplePlayerMode::SINGLE_PRESS &&
+ return m_data.sample->channelMode != SamplePlayerMode::SINGLE_PRESS &&
m_data.sample->isLoopMode == false;
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleActionEditor::rebuild()
{
m_data = c::actionEditor::getData(channelId);
- canChangeActionType() ? actionType->activate() : actionType->deactivate();
+ canChangeActionType() ? actionType->activate() : actionType->deactivate();
computeWidth();
-
+
m_ae->rebuild(m_data);
m_aer->size(m_ae->w(), m_aer->h());
- m_ee->rebuild(m_data);
+ m_ee->rebuild(m_data);
m_eer->size(m_ee->w(), m_eer->h());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_SAMPLE_ACTION_EDITOR_H
#define GD_SAMPLE_ACTION_EDITOR_H
-
#include "baseActionEditor.h"
-
class geResizerBar;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geSampleActionEditor;
class geEnvelopeEditor;
class gdSampleActionEditor : public gdBaseActionEditor
-{
+{
public:
+ gdSampleActionEditor(ID channelId);
- gdSampleActionEditor(ID channelId);
-
- void rebuild() override;
-
-private:
+ void rebuild() override;
+ private:
bool canChangeActionType();
geSampleActionEditor* m_ae;
- geResizerBar* m_aer;
+ geResizerBar* m_aer;
- geEnvelopeEditor* m_ee;
- geResizerBar* m_eer;
+ geEnvelopeEditor* m_ee;
+ geResizerBar* m_eer;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cstring>
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "core/mixer.h"
+#include "beatsInput.h"
#include "core/clock.h"
#include "core/conf.h"
#include "core/const.h"
+#include "core/mixer.h"
#include "glue/main.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
-#include "beatsInput.h"
+#include "gui/elems/basics/input.h"
#include "mainWindow.h"
-
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <cstring>
extern giada::v::gdMainWindow* mainWin;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdBeatsInput::gdBeatsInput()
: gdWindow(u::gui::centerWindowX(180), u::gui::centerWindowY(36), 180, 36, "Beats")
{
set_modal();
- beats = new geInput(8, 8, 43, G_GUI_UNIT);
- bars = new geInput(beats->x()+beats->w()+4, 8, 43, G_GUI_UNIT);
- ok = new geButton(bars->x()+bars->w()+4, 8, 70, G_GUI_UNIT, "Ok");
+ beats = new geInput(8, 8, 43, G_GUI_UNIT);
+ bars = new geInput(beats->x() + beats->w() + 4, 8, 43, G_GUI_UNIT);
+ ok = new geButton(bars->x() + bars->w() + 4, 8, 70, G_GUI_UNIT, "Ok");
end();
beats->maximum_size(2);
beats->value(std::to_string(m::clock::getBeats()).c_str());
beats->type(FL_INT_INPUT);
-
+
bars->maximum_size(2);
bars->value(std::to_string(m::clock::getBars()).c_str());
bars->type(FL_INT_INPUT);
-
+
ok->shortcut(FL_Enter);
ok->callback(cb_update, (void*)this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBeatsInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdBeatsInput*)p)->cb_update(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBeatsInput::cb_update()
{
if (!strcmp(beats->value(), "") || !strcmp(bars->value(), ""))
do_callback();
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BEATSINPUT_H
#define GD_BEATSINPUT_H
-
#include "window.h"
-
class geInput;
class geButton;
class geCheck;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class gdBeatsInput : public gdWindow
{
public:
+ gdBeatsInput();
- gdBeatsInput();
-
-private:
-
+ private:
static void cb_update(Fl_Widget* /*w*/, void* p);
- void cb_update();
+ void cb_update();
- geInput* beats;
- geInput* bars;
+ geInput* beats;
+ geInput* bars;
geButton* ok;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cstring>
+#include "bpmInput.h"
+#include "core/clock.h"
#include "core/conf.h"
#include "core/const.h"
#include "core/mixer.h"
-#include "core/clock.h"
#include "glue/main.h"
-#include "utils/gui.h"
-#include "utils/string.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/input.h"
-#include "bpmInput.h"
#include "mainWindow.h"
-
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <cstring>
extern giada::v::gdMainWindow* mainWin;
-
-namespace giada {
-namespace v
+namespace giada::v
{
gdBpmInput::gdBpmInput(const char* label)
: gdWindow(u::gui::centerWindowX(144), u::gui::centerWindowY(36), 144, 36, "Bpm")
{
set_modal();
- input_a = new geInput(8, 8, 30, G_GUI_UNIT);
+ input_a = new geInput(8, 8, 30, G_GUI_UNIT);
input_b = new geInput(42, 8, 20, G_GUI_UNIT);
- ok = new geButton(66, 8, 70, G_GUI_UNIT, "Ok");
+ ok = new geButton(66, 8, 70, G_GUI_UNIT, "Ok");
end();
+ std::vector<std::string> parts = u::string::split(label, ".");
+
input_a->maximum_size(3);
input_a->type(FL_INT_INPUT);
- input_a->value(u::string::fToString(m::clock::getBpm(), 0).c_str());
+ input_a->value(parts[0].c_str());
- /* Use the decimal value from the string label. */
-
- std::vector<std::string> tokens = u::string::split(label, ".");
-
input_b->maximum_size(1);
input_b->type(FL_INT_INPUT);
- input_b->value(tokens[1].c_str());
+ input_b->value(parts[1].c_str());
ok->shortcut(FL_Enter);
ok->callback(cb_update, (void*)this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBpmInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdBpmInput*)p)->cb_update(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBpmInput::cb_update()
{
if (strcmp(input_a->value(), "") == 0)
c::main::setBpm(input_a->value(), input_b->value());
do_callback();
}
-
-
-}} // giada::v::
\ No newline at end of file
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BPMINPUT_H
#define GD_BPMINPUT_H
-
#include "window.h"
-
class geInput;
class geButton;
-
-namespace giada {
-namespace v
+namespace giada::v
{
class gdBpmInput : public gdWindow
{
public:
-
gdBpmInput(const char* label); // pointer to mainWin->timing->bpm->label()
private:
+ static void cb_update(Fl_Widget* /*w*/, void* p);
+ void cb_update();
- static void cb_update(Fl_Widget* /*w*/, void* p);
- void cb_update();
-
- geInput* input_a;
- geInput* input_b;
- geButton* ok;
+ geInput* input_a;
+ geInput* input_b;
+ geButton* ok;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/graphics.h"
+#include "browserBase.h"
#include "core/conf.h"
#include "core/const.h"
-#include "utils/gui.h"
-#include "utils/fs.h"
-#include "gui/elems/browser.h"
+#include "core/graphics.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/check.h"
#include "gui/elems/basics/input.h"
#include "gui/elems/basics/progress.h"
-#include "gui/elems/basics/check.h"
-#include "browserBase.h"
-
+#include "gui/elems/browser.h"
+#include "utils/fs.h"
+#include "utils/gui.h"
-namespace giada {
+namespace giada
+{
namespace v
{
-gdBrowserBase::gdBrowserBase(const std::string& title, const std::string& path,
- std::function<void(void*)> callback, ID channelId)
-: gdWindow (m::conf::conf.browserX, m::conf::conf.browserY, m::conf::conf.browserW,
- m::conf::conf.browserH, title.c_str()),
- m_callback (callback),
- m_channelId(channelId)
+gdBrowserBase::gdBrowserBase(const std::string& title, const std::string& path,
+ std::function<void(void*)> callback, ID channelId)
+: gdWindow(m::conf::conf.browserX, m::conf::conf.browserY, m::conf::conf.browserW,
+ m::conf::conf.browserH, title.c_str())
+, m_callback(callback)
+, m_channelId(channelId)
{
set_non_modal();
- groupTop = new Fl_Group(8, 8, w()-16, 48);
- hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
- where = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h()+8, 20, 20);
- updir = new geButton(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
+ groupTop = new Fl_Group(8, 8, w() - 16, 48);
+ hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
+ where = new geInput(groupTop->x(), hiddenFiles->y() + hiddenFiles->h() + 8, 20, 20);
+ updir = new geButton(groupTop->x() + groupTop->w() - 20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
groupTop->end();
groupTop->resizable(where);
- hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
+ hiddenFiles->callback(cb_toggleHiddenFiles, (void*)this);
where->readonly(true);
where->cursor_color(G_COLOR_BLACK);
where->value(path.c_str());
- updir->callback(cb_up, (void*) this);
+ updir->callback(cb_up, (void*)this);
- browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w()-16, h()-101);
+ browser = new geBrowser(8, groupTop->y() + groupTop->h() + 8, w() - 16, h() - 101);
browser->loadDir(path);
if (path == m::conf::conf.browserLastPath)
browser->preselect(m::conf::conf.browserPosition, m::conf::conf.browserLastValue);
- Fl_Group* groupButtons = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, 20);
- ok = new geButton(w()-88, groupButtons->y(), 80, 20);
- cancel = new geButton(w()-ok->w()-96, groupButtons->y(), 80, 20, "Cancel");
- status = new geProgress(8, groupButtons->y(), cancel->x()-16, 20);
- status->minimum(0);
- status->maximum(1);
- status->hide(); // show the bar only if necessary
+ Fl_Group* groupButtons = new Fl_Group(8, browser->y() + browser->h() + 8, w() - 16, 20);
+ ok = new geButton(w() - 88, groupButtons->y(), 80, 20);
+ cancel = new geButton(w() - ok->w() - 96, groupButtons->y(), 80, 20, "Cancel");
+ status = new geProgress(8, groupButtons->y(), cancel->x() - 16, 20);
+ status->minimum(0);
+ status->maximum(1);
+ status->hide(); // show the bar only if necessary
groupButtons->resizable(status);
groupButtons->end();
end();
- cancel->callback(cb_close, (void*) this);
+ cancel->callback(cb_close, (void*)this);
resizable(browser);
size_range(320, 200);
show();
}
-
/* -------------------------------------------------------------------------- */
-
gdBrowserBase::~gdBrowserBase()
{
- m::conf::conf.browserX = x();
- m::conf::conf.browserY = y();
- m::conf::conf.browserW = w();
- m::conf::conf.browserH = h();
+ m::conf::conf.browserX = x();
+ m::conf::conf.browserY = y();
+ m::conf::conf.browserW = w();
+ m::conf::conf.browserH = h();
m::conf::conf.browserPosition = browser->position();
m::conf::conf.browserLastPath = browser->getCurrentDir();
m::conf::conf.browserLastValue = browser->value();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::cb_up(Fl_Widget* /*v*/, void* p) { ((gdBrowserBase*)p)->cb_up(); }
void gdBrowserBase::cb_close(Fl_Widget* /*v*/, void* p) { ((gdBrowserBase*)p)->cb_close(); }
void gdBrowserBase::cb_toggleHiddenFiles(Fl_Widget* /*v*/, void* p) { ((gdBrowserBase*)p)->cb_toggleHiddenFiles(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::cb_up()
{
browser->loadDir(u::fs::getUpDir(browser->getCurrentDir()));
where->value(browser->getCurrentDir().c_str());
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::cb_close()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::cb_toggleHiddenFiles()
{
browser->toggleHiddenFiles();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::setStatusBar(float v)
{
status->value(status->value() + v);
Fl::wait(0);
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::showStatusBar()
{
status->show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::hideStatusBar()
{
status->hide();
}
-
/* -------------------------------------------------------------------------- */
-
-std::string gdBrowserBase::getCurrentPath() const
-{
- return where->value();
+std::string gdBrowserBase::getCurrentPath() const
+{
+ return where->value();
}
-
-ID gdBrowserBase::getChannelId() const
-{
- return m_channelId;
+ID gdBrowserBase::getChannelId() const
+{
+ return m_channelId;
}
-
-std::string gdBrowserBase::getSelectedItem() const
+std::string gdBrowserBase::getSelectedItem() const
{
return browser->getSelectedItem();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserBase::fireCallback() const
-{
+{
m_callback((void*)this);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BROWSER_BASE_H
#define GD_BROWSER_BASE_H
-
-#include<functional>
-#include "gui/dialogs/window.h"
#include "core/types.h"
-
+#include "gui/dialogs/window.h"
+#include <functional>
+#include <string>
class Fl_Group;
class geCheck;
-class geBrowser;
class geButton;
class geInput;
class geProgress;
-
-namespace giada {
-namespace m
-{
-class Channel;
+namespace giada
+{
+namespace m
+{
+class Channel;
}
namespace v
{
+class geBrowser;
class gdBrowserBase : public gdWindow
{
public:
-
~gdBrowserBase();
/* getSelectedItem
std::string getSelectedItem() const;
std::string getCurrentPath() const;
- ID getChannelId() const;
- void fireCallback() const;
-
+ ID getChannelId() const;
+ void fireCallback() const;
+
/* setStatusBar
Increments status bar for progress tracking. */
void showStatusBar();
void hideStatusBar();
-protected:
-
- gdBrowserBase(const std::string& title, const std::string& path,
- std::function<void(void*)> f, ID channelId);
+ protected:
+ gdBrowserBase(const std::string& title, const std::string& path,
+ std::function<void(void*)> f, ID channelId);
static void cb_up(Fl_Widget* /*w*/, void* p);
static void cb_close(Fl_Widget* /*w*/, void* p);
static void cb_toggleHiddenFiles(Fl_Widget* /*w*/, void* p);
- void cb_up();
- void cb_close();
- void cb_toggleHiddenFiles();
+ void cb_up();
+ void cb_close();
+ void cb_toggleHiddenFiles();
/* m_callback
Fired when the save/load button is pressed. */
geButton* updir;
geProgress* status;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "utils/fs.h"
-#include "gui/elems/browser.h"
+#include "browserDir.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/input.h"
-#include "browserDir.h"
-
+#include "gui/elems/browser.h"
+#include "utils/fs.h"
-namespace giada {
+namespace giada
+{
namespace v
{
-gdBrowserDir::gdBrowserDir(const std::string& title, const std::string& path,
- std::function<void(void*)> cb)
+gdBrowserDir::gdBrowserDir(const std::string& title, const std::string& path,
+ std::function<void(void*)> cb)
: gdBrowserBase(title, path, cb, 0)
{
- where->size(groupTop->w()-updir->w()-8, 20);
+ where->size(groupTop->w() - updir->w() - 8, 20);
- browser->callback(cb_down, (void*) this);
+ browser->callback(cb_down, (void*)this);
ok->label("Select");
- ok->callback(cb_load, (void*) this);
+ ok->callback(cb_load, (void*)this);
ok->shortcut(FL_ENTER);
/* On OS X the 'where' input doesn't get resized properly on startup. Let's
force it. */
-
+
where->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserDir::cb_load(Fl_Widget* /*v*/, void* p) { ((gdBrowserDir*)p)->cb_load(); }
void gdBrowserDir::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserDir*)p)->cb_down(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserDir::cb_load()
{
fireCallback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserDir::cb_down()
{
std::string path = browser->getSelectedItem();
where->value(browser->getCurrentDir().c_str());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BROWSER_DIR_H
#define GD_BROWSER_DIR_H
-
#include "browserBase.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class gdBrowserDir : public gdBrowserBase
{
public:
-
- gdBrowserDir(const std::string& title, const std::string& path,
- std::function<void(void*)> cb);
-
-private:
-
- static void cb_load(Fl_Widget* /*w*/, void* p);
- static void cb_down(Fl_Widget* /*w*/, void* p);
- void cb_load();
- void cb_down();
+ gdBrowserDir(const std::string& title, const std::string& path,
+ std::function<void(void*)> cb);
+
+ private:
+ static void cb_load(Fl_Widget* /*w*/, void* p);
+ static void cb_down(Fl_Widget* /*w*/, void* p);
+ void cb_load();
+ void cb_down();
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "utils/fs.h"
-#include "gui/elems/browser.h"
+#include "browserLoad.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/input.h"
-#include "browserLoad.h"
-
+#include "gui/elems/browser.h"
+#include "utils/fs.h"
-namespace giada {
+namespace giada
+{
namespace v
{
-gdBrowserLoad::gdBrowserLoad(const std::string& title, const std::string& path,
- std::function<void(void*)> cb, ID channelId)
+gdBrowserLoad::gdBrowserLoad(const std::string& title, const std::string& path,
+ std::function<void(void*)> cb, ID channelId)
: gdBrowserBase(title, path, cb, channelId)
{
- where->size(groupTop->w()-updir->w()-8, 20);
+ where->size(groupTop->w() - updir->w() - 8, 20);
- browser->callback(cb_down, (void*) this);
+ browser->callback(cb_down, (void*)this);
ok->label("Load");
- ok->callback(cb_load, (void*) this);
+ ok->callback(cb_load, (void*)this);
ok->shortcut(FL_ENTER);
/* On OS X the 'where' input doesn't get resized properly on startup. Let's
force it. */
-
+
where->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserLoad::cb_load(Fl_Widget* /*v*/, void* p) { ((gdBrowserLoad*)p)->cb_load(); }
void gdBrowserLoad::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserLoad*)p)->cb_down(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserLoad::cb_load()
{
fireCallback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserLoad::cb_down()
{
std::string path = browser->getSelectedItem();
where->value(browser->getCurrentDir().c_str());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BROWSER_LOAD_H
#define GD_BROWSER_LOAD_H
-
#include "browserBase.h"
-
-namespace giada {
-namespace m
-{
-class Channel;
+namespace giada
+{
+namespace m
+{
+class Channel;
}
namespace v
{
class gdBrowserLoad : public gdBrowserBase
{
public:
+ gdBrowserLoad(const std::string& title, const std::string& path,
+ std::function<void(void*)> cb, ID channelId);
- gdBrowserLoad(const std::string& title, const std::string& path,
- std::function<void(void*)> cb, ID channelId);
-
-private:
-
+ private:
static void cb_load(Fl_Widget* /*w*/, void* p);
static void cb_down(Fl_Widget* /*w*/, void* p);
- void cb_load();
- void cb_down();
+ void cb_load();
+ void cb_down();
};
-}} // giada::v::
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "utils/fs.h"
-#include "gui/elems/browser.h"
+#include "browserSave.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/input.h"
-#include "browserSave.h"
-
+#include "gui/elems/browser.h"
+#include "utils/fs.h"
-namespace giada {
+namespace giada
+{
namespace v
{
-gdBrowserSave::gdBrowserSave(const std::string& title, const std::string& path,
- const std::string& name_, std::function<void(void*)> cb, ID channelId)
+gdBrowserSave::gdBrowserSave(const std::string& title, const std::string& path,
+ const std::string& name_, std::function<void(void*)> cb, ID channelId)
: gdBrowserBase(title, path, cb, channelId)
{
- where->size(groupTop->w()-236, 20);
+ where->size(groupTop->w() - 236, 20);
- name = new geInput(where->x()+where->w()+8, where->y(), 200, 20);
+ name = new geInput(where->x() + where->w() + 8, where->y(), 200, 20);
name->value(name_.c_str());
groupTop->add(name);
- browser->callback(cb_down, (void*) this);
+ browser->callback(cb_down, (void*)this);
ok->label("Save");
- ok->callback(cb_save, (void*) this);
+ ok->callback(cb_save, (void*)this);
ok->shortcut(FL_ENTER);
/* On OS X the 'where' and 'name' inputs don't get resized properly on startup.
name->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserSave::cb_save(Fl_Widget* /*v*/, void* p) { ((gdBrowserSave*)p)->cb_save(); }
void gdBrowserSave::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserSave*)p)->cb_down(); }
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserSave::cb_down()
{
std::string path = browser->getSelectedItem();
- if (path.empty()) // when click on an empty area
+ if (path.empty()) // when click on an empty area
return;
/* if the selected item is a directory just load its content. If it's a file
* use it as the file name (i.e. fill name->value()). */
- if (u::fs::isDir(path)) {
+ if (u::fs::isDir(path))
+ {
browser->loadDir(path);
where->value(browser->getCurrentDir().c_str());
}
name->value(browser->getSelectedItem(false).c_str());
}
-
/* -------------------------------------------------------------------------- */
-
std::string gdBrowserSave::getName() const
{
- return name->value();
+ return name->value();
}
-
/* -------------------------------------------------------------------------- */
-
void gdBrowserSave::cb_save()
{
fireCallback();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_BROWSER_SAVE_H
#define GD_BROWSER_SAVE_H
-
#include "browserBase.h"
-
class geInput;
-
-namespace giada {
-namespace m
-{
-class Channel;
+namespace giada
+{
+namespace m
+{
+class Channel;
}
namespace v
{
class gdBrowserSave : public gdBrowserBase
{
public:
-
- gdBrowserSave(const std::string& title, const std::string& path,
- const std::string& name, std::function<void(void*)> cb,
- ID channelId);
+ gdBrowserSave(const std::string& title, const std::string& path,
+ const std::string& name, std::function<void(void*)> cb,
+ ID channelId);
std::string getName() const;
-private:
+ private:
+ geInput* name;
- geInput* name;
-
- static void cb_down(Fl_Widget* /*w*/, void* p);
- static void cb_save(Fl_Widget* /*w*/, void* p);
- void cb_down();
- void cb_save();
+ static void cb_down(Fl_Widget* /*w*/, void* p);
+ static void cb_save(Fl_Widget* /*w*/, void* p);
+ void cb_down();
+ void cb_save();
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "glue/channel.h"
-#include "utils/gui.h"
-#include "core/model/model.h"
-#include "core/const.h"
+#include "channelNameInput.h"
#include "core/conf.h"
+#include "core/const.h"
+#include "core/model/model.h"
+#include "glue/channel.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/input.h"
-#include "channelNameInput.h"
-
-
+#include "utils/gui.h"
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdChannelNameInput::gdChannelNameInput(const c::channel::Data& d)
-: gdWindow(u::gui::centerWindowX(400), u::gui::centerWindowY(64), 400, 64, "New channel name"),
- m_data (d)
+: gdWindow(u::gui::centerWindowX(400), u::gui::centerWindowY(64), 400, 64, "New channel name")
+, m_data(d)
{
set_modal();
m_name = new geInput(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), G_GUI_UNIT);
- m_ok = new geButton(w() - 70 - G_GUI_OUTER_MARGIN, m_name->y()+m_name->h() + G_GUI_OUTER_MARGIN, 70, G_GUI_UNIT, "Ok");
+ m_ok = new geButton(w() - 70 - G_GUI_OUTER_MARGIN, m_name->y() + m_name->h() + G_GUI_OUTER_MARGIN, 70, G_GUI_UNIT, "Ok");
m_cancel = new geButton(m_ok->x() - 70 - G_GUI_OUTER_MARGIN, m_ok->y(), 70, G_GUI_UNIT, "Cancel");
end();
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdChannelNameInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdChannelNameInput*)p)->cb_update(); }
void gdChannelNameInput::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdChannelNameInput*)p)->cb_cancel(); }
-
/* -------------------------------------------------------------------------- */
-
void gdChannelNameInput::cb_cancel()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdChannelNameInput::cb_update()
{
c::channel::setName(m_data.id, m_name->value());
do_callback();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_CHANNEL_NAME_INPUT_H
#define GD_CHANNEL_NAME_INPUT_H
-
#include "window.h"
-
class geInput;
class geButton;
-
-namespace giada {
-namespace v
+namespace giada::c::channel
+{
+struct Data;
+}
+namespace giada::v
{
class gdChannelNameInput : public gdWindow
{
public:
-
gdChannelNameInput(const c::channel::Data& d);
-private:
-
+ private:
static void cb_update(Fl_Widget* /*w*/, void* p);
static void cb_cancel(Fl_Widget* /*w*/, void* p);
- void cb_update();
- void cb_cancel();
+ void cb_update();
+ void cb_cancel();
const c::channel::Data& m_data;
geButton* m_ok;
geButton* m_cancel;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Tabs.H>
+#include "config.h"
#include "core/conf.h"
#include "core/const.h"
-#include "utils/gui.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/config/tabMisc.h"
-#include "gui/elems/config/tabMidi.h"
#include "gui/elems/config/tabAudio.h"
#include "gui/elems/config/tabBehaviors.h"
+#include "gui/elems/config/tabMidi.h"
+#include "gui/elems/config/tabMisc.h"
#include "gui/elems/config/tabPlugins.h"
-#include "config.h"
-
+#include "utils/gui.h"
+#include <FL/Fl_Tabs.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdConfig::gdConfig(int w, int h)
: gdWindow(u::gui::centerWindowX(w), u::gui::centerWindowY(h), w, h, "Configuration")
{
- Fl_Tabs* tabs = new Fl_Tabs(8, 8, w-16, h-44);
+ Fl_Tabs* tabs = new Fl_Tabs(8, 8, w - 16, h - 44);
tabs->box(G_CUSTOM_BORDER_BOX);
tabs->labelcolor(G_COLOR_LIGHT_2);
tabs->begin();
- tabAudio = new geTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabMidi = new geTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabBehaviors = new geTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabMisc = new geTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabAudio = new geTabAudio(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
+ tabMidi = new geTabMidi(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
+ tabBehaviors = new geTabBehaviors(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
+ tabMisc = new geTabMisc(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
#ifdef WITH_VST
- tabPlugins = new geTabPlugins(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabPlugins = new geTabPlugins(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
#endif
tabs->end();
- save = new geButton (w-88, h-28, 80, 20, "Save");
- cancel = new geButton (w-176, h-28, 80, 20, "Cancel");
+ save = new geButton(w - 88, h - 28, 80, 20, "Save");
+ cancel = new geButton(w - 176, h - 28, 80, 20, "Cancel");
end();
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdConfig::cb_save_config(Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_save_config(); }
-void gdConfig::cb_cancel (Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_cancel(); }
-
+void gdConfig::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_cancel(); }
/* -------------------------------------------------------------------------- */
-
void gdConfig::cb_save_config()
{
tabAudio->save();
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdConfig::cb_cancel()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
#ifdef WITH_VST
#endif
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_CONFIG_H
#define GD_CONFIG_H
-
#include "window.h"
+class geButton;
+class geCheck;
+class geInput;
+class geBox;
+namespace giada
+{
+namespace v
+{
+class geChoice;
class geTabAudio;
class geTabBehaviors;
class geTabMidi;
#ifdef WITH_VST
class geTabPlugins;
#endif
-class geButton;
-class geCheck;
-class geInput;
-class geRadio;
-class geBox;
-
-
-namespace giada {
-namespace v
-{
-class geChoice;
class gdConfig : public gdWindow
{
public:
-
gdConfig(int w, int h);
#ifdef WITH_VST
geTabMidi* tabMidi;
geTabMisc* tabMisc;
#ifdef WITH_VST
- geTabPlugins* tabPlugins;
+ geTabPlugins* tabPlugins;
#endif
- geButton* save;
- geButton* cancel;
-
-private:
+ geButton* save;
+ geButton* cancel;
+ private:
static void cb_save_config(Fl_Widget* /*w*/, void* p);
static void cb_cancel(Fl_Widget* /*w*/, void* p);
- void cb_save_config();
- void cb_cancel();
+ void cb_save_config();
+ void cb_cancel();
};
-}} // giada::v::
+} // namespace v
+} // namespace giada
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "core/kernelAudio.h"
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/box.h"
-#include "window.h"
-#include "devInfo.h"
-
-
-namespace giada {
-namespace v
-{
-gdDevInfo::gdDevInfo(unsigned dev)
-: gdWindow(340, 300, "Device information")
-{
- set_modal();
-
- text = new geBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
- close = new geButton(252, h()-28, 80, 20, "Close");
- end();
-
- std::string body = "";
- int lines = 7;
-
- body = "Device name: " + m::kernelAudio::getDeviceName(dev) + "\n";
- body += "Total output(s): " + std::to_string(m::kernelAudio::getMaxOutChans(dev)) + "\n";
- body += "Total input(s): " + std::to_string(m::kernelAudio::getMaxInChans(dev)) + "\n";
- body += "Duplex channel(s): " + std::to_string(m::kernelAudio::getDuplexChans(dev)) + "\n";
- body += "Default output: " + std::string(m::kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
- body += "Default input: " + std::string(m::kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
-
- int totalFreq = m::kernelAudio::getTotalFreqs(dev);
- body += "Supported frequencies: " + std::to_string(totalFreq);
-
- for (int i=0; i<totalFreq; i++) {
- if (i % 6 == 0) {
- body += "\n "; // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
- lines++;
- }
- body += std::to_string(m::kernelAudio::getFreq(dev, i)) + " ";
- }
-
- text->copy_label(body.c_str());
-
- /* Resize the window to fit the content. fl_height() returns the height of a
- line. fl_height() * total lines + margins + button size */
-
- resize(x(), y(), w(), (lines * fl_height()) + 8 + 8 + 8 + 20);
- close->position(close->x(), (lines * fl_height()) + 8 + 8);
-
- close->callback(cb_window_closer, (void*)this);
- u::gui::setFavicon(this);
- show();
-}
-}} // giada::v::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GD_DEV_INFO_H
-#define GD_DEV_INFO_H
-
-
-#include "window.h"
-
-
-class geBox;
-class geButton;
-
-
-namespace giada {
-namespace v
-{
-class gdDevInfo : public gdWindow
-{
-public:
-
- gdDevInfo(unsigned dev);
-
-private:
-
- geBox* text;
- geButton* close;
-};
-}} // giada::v::
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "glue/io.h"
-#include "core/model/model.h"
+#include "keyGrabber.h"
+#include "config.h"
#include "core/conf.h"
-#include "utils/log.h"
+#include "core/model/model.h"
+#include "glue/io.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/mainWindow/keyboard/channelButton.h"
-#include "keyGrabber.h"
-#include "config.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "mainWindow.h"
-
+#include "utils/gui.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include <cassert>
extern giada::v::gdMainWindow* mainWin;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdKeyGrabber::gdKeyGrabber(const c::channel::Data& d)
: gdWindow(300, 126, "Key configuration")
-, m_data (d)
+, m_data(d)
{
begin();
m_text = new geBox(8, 8, 284, 80, "");
- m_clear = new geButton(w()-88, m_text->y()+m_text->h()+8, 80, 20, "Clear");
- m_cancel = new geButton(m_clear->x()-88, m_clear->y(), 80, 20, "Close");
+ m_clear = new geButton(w() - 88, m_text->y() + m_text->h() + 8, 80, 20, "Clear");
+ m_cancel = new geButton(m_clear->x() - 88, m_clear->y(), 80, 20, "Close");
end();
m_clear->callback(cb_clear, (void*)this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdKeyGrabber::rebuild()
{
updateText(m_data.key);
}
-
/* -------------------------------------------------------------------------- */
-
-void gdKeyGrabber::cb_clear (Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
+void gdKeyGrabber::cb_clear(Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
void gdKeyGrabber::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); }
-
/* -------------------------------------------------------------------------- */
-
void gdKeyGrabber::cb_cancel()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdKeyGrabber::cb_clear()
{
updateText(0);
setButtonLabel(0);
}
-
/* -------------------------------------------------------------------------- */
-
void gdKeyGrabber::setButtonLabel(int key)
{
c::io::channel_setKey(m_data.id, key);
/* -------------------------------------------------------------------------- */
-
void gdKeyGrabber::updateText(int key)
{
std::string tmp = "Press a key.\n\nCurrent binding: ";
m_text->copy_label(tmp.c_str());
}
-
/* -------------------------------------------------------------------------- */
-
int gdKeyGrabber::handle(int e)
{
int ret = Fl_Group::handle(e);
- switch(e) {
- case FL_KEYUP: {
- int x = Fl::event_key();
- if (strlen(Fl::event_text()) != 0
- && x != FL_BackSpace
- && x != FL_Enter
- && x != FL_Delete
- && x != FL_Tab
- && x != FL_End
- && x != ' ')
- {
- u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_data.id);
- setButtonLabel(x);
- updateText(x);
- break;
- }
- else
- u::log::print("invalid key\n");
+ switch (e)
+ {
+ case FL_KEYUP:
+ {
+ int x = Fl::event_key();
+ if (strlen(Fl::event_text()) != 0 && x != FL_BackSpace && x != FL_Enter && x != FL_Delete && x != FL_Tab && x != FL_End && x != ' ')
+ {
+ u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_data.id);
+ setButtonLabel(x);
+ updateText(x);
+ break;
}
+ else
+ u::log::print("invalid key\n");
+ }
}
- return(ret);
+ return (ret);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_KEYGRABBER_H
#define GD_KEYGRABBER_H
-
-#include <FL/Fl.H>
#include "window.h"
-
+#include <FL/Fl.H>
class geBox;
class geButton;
-
-namespace giada {
-namespace c {
+namespace giada
+{
+namespace c
+{
namespace channel
{
struct Data;
-}}
-namespace v
+}
+} // namespace c
+namespace v
{
class gdKeyGrabber : public gdWindow
{
public:
-
gdKeyGrabber(const c::channel::Data& d);
- int handle(int e) override;
+ int handle(int e) override;
void rebuild() override;
-private:
-
- static void cb_clear (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_clear(Fl_Widget* /*w*/, void* p);
static void cb_cancel(Fl_Widget* /*w*/, void* p);
- void cb_clear ();
- void cb_cancel();
+ void cb_clear();
+ void cb_cancel();
void setButtonLabel(int key);
void updateText(int key);
-
+
const c::channel::Data& m_data;
geBox* m_text;
geButton* m_clear;
geButton* m_cancel;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include "core/const.h"
+#include "mainWindow.h"
+#include "core/clock.h"
#include "core/conf.h"
+#include "core/const.h"
#include "core/init.h"
-#include "utils/gui.h"
#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/mainIO.h"
#include "gui/elems/mainWindow/mainMenu.h"
#include "gui/elems/mainWindow/mainTimer.h"
#include "gui/elems/mainWindow/mainTransport.h"
-#include "gui/elems/mainWindow/beatMeter.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/sequencer.h"
+#include "utils/gui.h"
#include "warnings.h"
-#include "mainWindow.h"
-
+#include <FL/Fl.H>
+#include <FL/Fl_Tooltip.H>
-namespace giada {
-namespace v
+namespace giada::v
{
gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** argv)
- : gdWindow(W, H, title)
+: gdWindow(W, H, title)
{
Fl::visible_focus(0);
Fl::background(25, 25, 25); // TODO use G_COLOR_GREY_1
Fl::set_boxtype(G_CUSTOM_BORDER_BOX, g_customBorderBox, 1, 1, 2, 2);
- Fl::set_boxtype(G_CUSTOM_UP_BOX, g_customUpBox, 1, 1, 2, 2);
- Fl::set_boxtype(G_CUSTOM_DOWN_BOX, g_customDownBox, 1, 1, 2, 2);
+ Fl::set_boxtype(G_CUSTOM_UP_BOX, g_customUpBox, 1, 1, 2, 2);
+ Fl::set_boxtype(G_CUSTOM_DOWN_BOX, g_customDownBox, 1, 1, 2, 2);
Fl::set_boxtype(FL_BORDER_BOX, G_CUSTOM_BORDER_BOX);
- Fl::set_boxtype(FL_UP_BOX, G_CUSTOM_UP_BOX);
- Fl::set_boxtype(FL_DOWN_BOX, G_CUSTOM_DOWN_BOX);
+ Fl::set_boxtype(FL_UP_BOX, G_CUSTOM_UP_BOX);
+ Fl::set_boxtype(FL_DOWN_BOX, G_CUSTOM_DOWN_BOX);
+
+ Fl_Tooltip::color(G_COLOR_GREY_1);
+ Fl_Tooltip::textcolor(G_COLOR_LIGHT_2);
+ Fl_Tooltip::size(G_GUI_FONT_SIZE_BASE);
+ Fl_Tooltip::enable(m::conf::conf.showTooltips);
size_range(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT);
- mainMenu = new v::geMainMenu(8, 0);
+ mainMenu = new v::geMainMenu(8, 0);
#if defined(WITH_VST)
- mainIO = new v::geMainIO(412, 8);
+ mainIO = new v::geMainIO(412, 8);
#else
- mainIO = new v::geMainIO(460, 8);
+ mainIO = new v::geMainIO(460, 8);
#endif
mainTransport = new v::geMainTransport(8, 39);
mainTimer = new v::geMainTimer(571, 44);
- beatMeter = new v::geBeatMeter(100, 83, 609, 20);
- keyboard = new v::geKeyboard(8, 122, w()-16, 380);
+ sequencer = new v::geSequencer(100, 78, 609, 30);
+ keyboard = new v::geKeyboard(8, 122, w() - 16, 380);
/* zone 1 - menus, and I/O tools */
- Fl_Group* zone1 = new Fl_Group(8, 0, W-16, 28);
+ Fl_Group* zone1 = new Fl_Group(8, 0, W - 16, 28);
zone1->add(mainMenu);
zone1->resizable(new Fl_Box(300, 8, 80, 20));
zone1->add(mainIO);
/* zone 2 - mainTransport and timing tools */
- Fl_Group* zone2 = new Fl_Group(8, mainTransport->y(), W-16, mainTransport->h());
+ Fl_Group* zone2 = new Fl_Group(8, mainTransport->y(), W - 16, mainTransport->h());
zone2->add(mainTransport);
- zone2->resizable(new Fl_Box(mainTransport->x()+mainTransport->w()+4, zone2->y(), 80, 20));
+ zone2->resizable(new Fl_Box(mainTransport->x() + mainTransport->w() + 4, zone2->y(), 80, 20));
zone2->add(mainTimer);
/* zone 3 - beat meter */
- Fl_Group* zone3 = new Fl_Group(8, beatMeter->y(), W-16, beatMeter->h());
- zone3->add(beatMeter);
+ Fl_Group* zone3 = new Fl_Group(8, sequencer->y(), W - 16, sequencer->h());
+ zone3->add(sequencer);
/* zone 4 - the keyboard (Fl_Group is unnecessary here, keyboard is
* a group by itself) */
});
u::gui::setFavicon(this);
+ refresh();
+
show(argc, argv);
}
-
/* -------------------------------------------------------------------------- */
-
gdMainWindow::~gdMainWindow()
{
m::conf::conf.mainWindowX = x();
m::conf::conf.mainWindowH = h();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMainWindow::refresh()
{
mainIO->refresh();
mainTimer->refresh();
mainTransport->refresh();
- beatMeter->refresh();
+ sequencer->refresh();
keyboard->refresh();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMainWindow::rebuild()
{
keyboard->rebuild();
mainTimer->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMainWindow::clearKeyboard()
{
keyboard->init();
}
-}} // giada::v::
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MAINWINDOW_H
#define GD_MAINWINDOW_H
-
#include "window.h"
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geKeyboard;
class geMainIO;
class geMainMenu;
-class geBeatMeter;
+class geSequencer;
class geMainTransport;
class geMainTimer;
class gdMainWindow : public gdWindow
{
public:
-
- gdMainWindow(int w, int h, const char* title, int argc, char** argv);
+ gdMainWindow(int w, int h, const char* title, int argc, char** argv);
~gdMainWindow();
void refresh() override;
- void rebuild() override;
+ void rebuild() override;
/* clearKeyboard
Resets Keyboard to initial state, with no columns. */
void clearKeyboard();
geKeyboard* keyboard;
- geBeatMeter* beatMeter;
+ geSequencer* sequencer;
geMainMenu* mainMenu;
geMainIO* mainIO;
geMainTimer* mainTimer;
geMainTransport* mainTransport;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "glue/io.h"
-#include "core/conf.h"
#include "midiInputBase.h"
+#include "core/conf.h"
+#include "glue/io.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char* title)
: gdWindow(x, y, w, h, title)
{
}
-
/* -------------------------------------------------------------------------- */
-
gdMidiInputBase::~gdMidiInputBase()
{
c::io::stopMidiLearn();
-
+
m::conf::conf.midiInputX = x();
m::conf::conf.midiInputY = y();
m::conf::conf.midiInputW = w();
m::conf::conf.midiInputH = h();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputBase::cb_close(Fl_Widget* /*w*/, void* p) { ((gdMidiInputBase*)p)->cb_close(); }
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputBase::cb_close()
{
do_callback();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_INPUT_BASE_H
#define GD_MIDI_INPUT_BASE_H
-
#include "gui/dialogs/window.h"
#include "gui/elems/midiIO/midiLearner.h"
-
class geButton;
class geCheck;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geChoice;
class gdMidiInputBase : public gdWindow
{
public:
-
virtual ~gdMidiInputBase();
-protected:
-
- gdMidiInputBase(int x, int y, int w, int h, const char* title="");
+ protected:
+ gdMidiInputBase(int x, int y, int w, int h, const char* title = "");
static void cb_close(Fl_Widget* /*w*/, void* p);
- void cb_close();
+ void cb_close();
geButton* m_ok;
geCheck* m_enable;
geChoice* m_channel;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl_Pack.H>
+#include "core/conf.h"
+#include "core/const.h"
#include "utils/gui.h"
#include "utils/log.h"
-#include "core/const.h"
-#include "core/conf.h"
+#include <FL/Fl_Pack.H>
+#include <cassert>
#ifdef WITH_VST
#include "core/plugins/plugin.h"
#endif
-#include "utils/string.h"
-#include "gui/elems/midiIO/midiLearner.h"
-#include "gui/elems/midiIO/midiLearnerPack.h"
-#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/group.h"
+#include "gui/elems/basics/scrollPack.h"
+#include "gui/elems/midiIO/midiLearner.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
#include "midiInputChannel.h"
+#include "utils/string.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geChannelLearnerPack::geChannelLearnerPack(int x, int y, const c::io::Channel_InputData& channel)
: geMidiLearnerPack(x, y, "Channel")
{
setCallbacks(
- [channelId=channel.channelId] (int param) { c::io::channel_startMidiLearn(param, channelId); },
- [channelId=channel.channelId] (int param) { c::io::channel_clearMidiLearn(param, channelId); }
- );
- addMidiLearner("keyPress", G_MIDI_IN_KEYPRESS);
- addMidiLearner("key release", G_MIDI_IN_KEYREL);
- addMidiLearner("key kill", G_MIDI_IN_KILL);
- addMidiLearner("arm", G_MIDI_IN_ARM);
- addMidiLearner("mute", G_MIDI_IN_MUTE);
- addMidiLearner("solo", G_MIDI_IN_SOLO);
- addMidiLearner("volume", G_MIDI_IN_VOLUME);
- addMidiLearner("pitch", G_MIDI_IN_PITCH, /*visible=*/channel.channelType == ChannelType::SAMPLE);
+ [channelId = channel.channelId](int param) { c::io::channel_startMidiLearn(param, channelId); },
+ [channelId = channel.channelId](int param) { c::io::channel_clearMidiLearn(param, channelId); });
+ addMidiLearner("keyPress", G_MIDI_IN_KEYPRESS);
+ addMidiLearner("key release", G_MIDI_IN_KEYREL);
+ addMidiLearner("key kill", G_MIDI_IN_KILL);
+ addMidiLearner("arm", G_MIDI_IN_ARM);
+ addMidiLearner("mute", G_MIDI_IN_MUTE);
+ addMidiLearner("solo", G_MIDI_IN_SOLO);
+ addMidiLearner("volume", G_MIDI_IN_VOLUME);
+ addMidiLearner("pitch", G_MIDI_IN_PITCH, /*visible=*/channel.channelType == ChannelType::SAMPLE);
addMidiLearner("read actions", G_MIDI_IN_READ_ACTIONS, /*visible=*/channel.channelType == ChannelType::SAMPLE);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelLearnerPack::update(const c::io::Channel_InputData& d)
{
learners[0]->update(d.keyPress);
setEnabled(d.enabled);
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
gePluginLearnerPack::gePluginLearnerPack(int x, int y, const c::io::PluginData& plugin)
: geMidiLearnerPack(x, y, plugin.name)
{
setCallbacks(
- [pluginId=plugin.id] (int param) { c::io::plugin_startMidiLearn(param, pluginId); },
- [pluginId=plugin.id] (int param) { c::io::plugin_clearMidiLearn(param, pluginId); }
- );
+ [pluginId = plugin.id](int param) { c::io::plugin_startMidiLearn(param, pluginId); },
+ [pluginId = plugin.id](int param) { c::io::plugin_clearMidiLearn(param, pluginId); });
for (const c::io::PluginParamData& param : plugin.params)
addMidiLearner(param.name, param.index);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginLearnerPack::update(const c::io::PluginData& d, bool enabled)
{
std::size_t i = 0;
#endif
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
gdMidiInputChannel::gdMidiInputChannel(ID channelId)
-: gdMidiInputBase(m::conf::conf.midiInputX,
- m::conf::conf.midiInputY,
- m::conf::conf.midiInputW,
- m::conf::conf.midiInputH)
-, m_channelId (channelId)
-, m_data (c::io::channel_getInputData(channelId))
+: gdMidiInputBase(m::conf::conf.midiInputX,
+ m::conf::conf.midiInputY,
+ m::conf::conf.midiInputW,
+ m::conf::conf.midiInputH)
+, m_channelId(channelId)
+, m_data(c::io::channel_getInputData(channelId))
{
end();
/* Header */
geGroup* groupHeader = new geGroup(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
- m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
- m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
- m_veloAsVol = new geCheck(0, m_enable->y() + m_enable->h() + G_GUI_OUTER_MARGIN, w() - 16, G_GUI_UNIT,
- "Velocity drives volume (Sample Channels)");
+ m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
+ m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
+ m_veloAsVol = new geCheck(0, m_enable->y() + m_enable->h() + G_GUI_OUTER_MARGIN, w() - 16, G_GUI_UNIT,
+ "Velocity drives volume (Sample Channels)");
groupHeader->add(m_enable);
groupHeader->add(m_channel);
groupHeader->add(m_veloAsVol);
/* Main scrollable content. */
- m_container = new geScrollPack(G_GUI_OUTER_MARGIN, groupHeader->y() + groupHeader->h() + G_GUI_OUTER_MARGIN,
- w() - 16, h() - groupHeader->h() - 52);
+ m_container = new geScrollPack(G_GUI_OUTER_MARGIN, groupHeader->y() + groupHeader->h() + G_GUI_OUTER_MARGIN,
+ w() - 16, h() - groupHeader->h() - 52);
m_container->add(new geChannelLearnerPack(0, 0, m_data));
#ifdef WITH_VST
for (c::io::PluginData& plugin : m_data.plugins)
/* Footer buttons. */
geGroup* groupButtons = new geGroup(G_GUI_OUTER_MARGIN, m_container->y() + m_container->h() + G_GUI_OUTER_MARGIN);
- geBox* spacer = new geBox(0, 0, w() - 80, G_GUI_UNIT); // spacer window border <-> buttons
- m_ok = new geButton(w() - 96, 0, 80, G_GUI_UNIT, "Close");
+ geBox* spacer = new geBox(0, 0, w() - 80, G_GUI_UNIT); // spacer window border <-> buttons
+ m_ok = new geButton(w() - 96, 0, 80, G_GUI_UNIT, "Close");
groupButtons->add(spacer);
groupButtons->add(m_ok);
groupButtons->resizable(spacer);
m_channel->add("Channel 16");
m_channel->callback(cb_setChannel, (void*)this);
- m_veloAsVol->callback(cb_veloAsVol, (void*)this);
+ m_veloAsVol->callback(cb_veloAsVol, (void*)this);
add(groupHeader);
add(m_container);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputChannel::rebuild()
{
m_data = c::io::channel_getInputData(m_channelId);
m_enable->value(m_data.enabled);
- if (m_data.channelType == ChannelType::SAMPLE) {
+ if (m_data.channelType == ChannelType::SAMPLE)
+ {
m_veloAsVol->activate();
m_veloAsVol->value(m_data.velocityAsVol);
}
m_channel->value(m_data.filter == -1 ? 0 : m_data.filter + 1);
- if (m_data.enabled) {
+ if (m_data.enabled)
+ {
m_channel->activate();
if (m_data.channelType == ChannelType::SAMPLE)
m_veloAsVol->activate();
}
- else {
+ else
+ {
m_channel->deactivate();
m_veloAsVol->deactivate();
}
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputChannel::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); }
void gdMidiInputChannel::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_setChannel(); }
void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_veloAsVol(); }
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputChannel::cb_enable()
{
c::io::channel_enableMidiLearn(m_data.channelId, m_enable->value());
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputChannel::cb_veloAsVol()
{
c::io::channel_enableVelocityAsVol(m_data.channelId, m_veloAsVol->value());
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputChannel::cb_setChannel()
{
- c::io::channel_setMidiInputFilter(m_data.channelId,
- m_channel->value() == 0 ? -1 : m_channel->value() - 1);
+ c::io::channel_setMidiInputFilter(m_data.channelId,
+ m_channel->value() == 0 ? -1 : m_channel->value() - 1);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_INPUT_CHANNEL_H
#define GD_MIDI_INPUT_CHANNEL_H
-
#include "glue/io.h"
#include "gui/elems/midiIO/midiLearnerPack.h"
#include "midiInputBase.h"
-
class geCheck;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geChoice;
class geScrollPack;
class geChannelLearnerPack : public geMidiLearnerPack
{
public:
-
geChannelLearnerPack(int x, int y, const c::io::Channel_InputData& d);
void update(const c::io::Channel_InputData&);
};
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
class gePluginLearnerPack : public geMidiLearnerPack
{
public:
-
gePluginLearnerPack(int x, int y, const c::io::PluginData&);
void update(const c::io::PluginData&, bool enabled);
#endif
-
/* -------------------------------------------------------------------------- */
-
-
class gdMidiInputChannel : public gdMidiInputBase
{
public:
-
gdMidiInputChannel(ID channelId);
void rebuild() override;
-private:
-
+ private:
static void cb_enable(Fl_Widget* /*w*/, void* p);
static void cb_setChannel(Fl_Widget* /*w*/, void* p);
static void cb_veloAsVol(Fl_Widget* /*w*/, void* p);
- void cb_enable();
- void cb_setChannel();
- void cb_veloAsVol();
+ void cb_enable();
+ void cb_setChannel();
+ void cb_veloAsVol();
ID m_channelId;
-
+
c::io::Channel_InputData m_data;
geScrollPack* m_container;
geCheck* m_veloAsVol;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Pack.H>
-#include "utils/gui.h"
+#include "midiInputMaster.h"
#include "core/conf.h"
#include "core/const.h"
-#include "gui/elems/midiIO/midiLearner.h"
-#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/group.h"
-#include "midiInputMaster.h"
-
+#include "gui/elems/basics/scrollPack.h"
+#include "gui/elems/midiIO/midiLearner.h"
+#include "utils/gui.h"
+#include <FL/Fl_Pack.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geMasterLearnerPack::geMasterLearnerPack(int x, int y)
: geMidiLearnerPack(x, y)
{
setCallbacks(
- [] (int param) { c::io::master_startMidiLearn(param); },
- [] (int param) { c::io::master_clearMidiLearn(param); }
- );
- addMidiLearner("rewind", G_MIDI_IN_REWIND);
- addMidiLearner("play/stop", G_MIDI_IN_START_STOP);
+ [](int param) { c::io::master_startMidiLearn(param); },
+ [](int param) { c::io::master_clearMidiLearn(param); });
+ addMidiLearner("rewind", G_MIDI_IN_REWIND);
+ addMidiLearner("play/stop", G_MIDI_IN_START_STOP);
addMidiLearner("action recording", G_MIDI_IN_ACTION_REC);
- addMidiLearner("input recording", G_MIDI_IN_INPUT_REC);
- addMidiLearner("metronome", G_MIDI_IN_METRONOME);
- addMidiLearner("input volume", G_MIDI_IN_VOLUME_IN);
- addMidiLearner("output volume", G_MIDI_IN_VOLUME_OUT);
- addMidiLearner("sequencer ×2", G_MIDI_IN_BEAT_DOUBLE);
- addMidiLearner("sequencer ÷2", G_MIDI_IN_BEAT_HALF);
+ addMidiLearner("input recording", G_MIDI_IN_INPUT_REC);
+ addMidiLearner("metronome", G_MIDI_IN_METRONOME);
+ addMidiLearner("input volume", G_MIDI_IN_VOLUME_IN);
+ addMidiLearner("output volume", G_MIDI_IN_VOLUME_OUT);
+ addMidiLearner("sequencer ×2", G_MIDI_IN_BEAT_DOUBLE);
+ addMidiLearner("sequencer ÷2", G_MIDI_IN_BEAT_HALF);
}
-
/* -------------------------------------------------------------------------- */
-
void geMasterLearnerPack::update(const c::io::Master_InputData& d)
{
learners[0]->update(d.rewind);
learners[1]->update(d.startStop);
learners[2]->update(d.actionRec);
learners[3]->update(d.inputRec);
- learners[4]->update(d.metronome);
+ learners[4]->update(d.metronome);
learners[5]->update(d.volumeIn);
learners[6]->update(d.volumeOut);
learners[7]->update(d.beatDouble);
setEnabled(d.enabled);
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
gdMidiInputMaster::gdMidiInputMaster()
: gdMidiInputBase(m::conf::conf.midiInputX, m::conf::conf.midiInputY, 300, 284, "MIDI Input Setup (global)")
{
end();
geGroup* groupHeader = new geGroup(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
- m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
- m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
+ m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
+ m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
groupHeader->resizable(nullptr);
groupHeader->add(m_enable);
groupHeader->add(m_channel);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputMaster::rebuild()
{
m_data = c::io::master_getInputData();
m_data.enabled ? m_channel->activate() : m_channel->deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputMaster::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputMaster*)p)->cb_enable(); }
void gdMidiInputMaster::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputMaster*)p)->cb_setChannel(); }
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputMaster::cb_enable()
{
c::io::master_enableMidiLearn(m_enable->value());
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiInputMaster::cb_setChannel()
{
c::io::master_setMidiFilter(m_channel->value() == 0 ? -1 : m_channel->value() - 1);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_INPUT_MASTER_H
#define GD_MIDI_INPUT_MASTER_H
-
#include "glue/io.h"
#include "gui/elems/midiIO/midiLearnerPack.h"
#include "midiInputBase.h"
-
class geCheck;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geChoice;
class geMasterLearnerPack : public geMidiLearnerPack
{
public:
-
geMasterLearnerPack(int x, int y);
void update(const c::io::Master_InputData&);
};
-
/* -------------------------------------------------------------------------- */
-
class gdMidiInputMaster : public gdMidiInputBase
{
public:
-
- gdMidiInputMaster();
+ gdMidiInputMaster();
void rebuild() override;
-private:
-
+ private:
static void cb_enable(Fl_Widget* /*w*/, void* p);
static void cb_setChannel(Fl_Widget* /*w*/, void* p);
- void cb_enable();
- void cb_setChannel();
+ void cb_enable();
+ void cb_setChannel();
c::io::Master_InputData m_data;
geMasterLearnerPack* m_learners;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "midiOutputBase.h"
#include "glue/io.h"
-#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/check.h"
-#include "midiOutputBase.h"
-
+#include "gui/elems/midiIO/midiLearner.h"
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geLightningLearnerPack::geLightningLearnerPack(int x, int y, ID channelId)
: geMidiLearnerPack(x, y)
{
setCallbacks(
- [channelId] (int param) { c::io::channel_startMidiLearn(param, channelId); },
- [channelId] (int param) { c::io::channel_clearMidiLearn(param, channelId); }
- );
+ [channelId](int param) { c::io::channel_startMidiLearn(param, channelId); },
+ [channelId](int param) { c::io::channel_clearMidiLearn(param, channelId); });
addMidiLearner("playing", G_MIDI_OUT_L_PLAYING);
- addMidiLearner("mute", G_MIDI_OUT_L_MUTE);
- addMidiLearner("solo", G_MIDI_OUT_L_SOLO);
+ addMidiLearner("mute", G_MIDI_OUT_L_MUTE);
+ addMidiLearner("solo", G_MIDI_OUT_L_SOLO);
}
-
/* -------------------------------------------------------------------------- */
-
void geLightningLearnerPack::update(const c::io::Channel_OutputData& d)
{
learners[0]->update(d.lightningPlaying);
setEnabled(d.lightningEnabled);
}
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
gdMidiOutputBase::gdMidiOutputBase(int w, int h, ID channelId)
-: gdWindow (w, h, "Midi Output Setup")
+: gdWindow(w, h, "Midi Output Setup")
, m_channelId(channelId)
{
}
-
/* -------------------------------------------------------------------------- */
-
gdMidiOutputBase::~gdMidiOutputBase()
{
c::io::stopMidiLearn();
}
-
/* -------------------------------------------------------------------------- */
-
-void gdMidiOutputBase::cb_close (Fl_Widget* /*w*/, void* p) { ((gdMidiOutputBase*)p)->cb_close(); }
+void gdMidiOutputBase::cb_close(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputBase*)p)->cb_close(); }
void gdMidiOutputBase::cb_enableLightning(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputBase*)p)->cb_enableLightning(); }
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputBase::cb_close()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputBase::cb_enableLightning()
{
c::io::channel_enableMidiLightning(m_channelId, m_enableLightning->value());
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputBase::setTitle(ID channelId)
{
- std::string tmp = "MIDI Output Setup (channel " + std::to_string(channelId) + ")";
+ std::string tmp = "MIDI Output Setup (channel " + std::to_string(channelId) + ")";
copy_label(tmp.c_str());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_OUTPUT_BASE_H
#define GD_MIDI_OUTPUT_BASE_H
-
#include "core/types.h"
#include "glue/io.h"
-#include "gui/elems/midiIO/midiLearnerPack.h"
-#include "gui/elems/midiIO/midiLearner.h"
#include "gui/dialogs/window.h"
-
+#include "gui/elems/midiIO/midiLearner.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
class geButton;
class geCheck;
-
/* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI
output master is managed by the configuration window, hence gdMidiOutput deals
only with channels.
Both MidiOutputMidiCh and MidiOutputSampleCh have the MIDI lighting widget set.
In addition MidiOutputMidiCh has the MIDI message output box. */
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geLightningLearnerPack : public geMidiLearnerPack
{
public:
-
geLightningLearnerPack(int x, int y, ID channelId);
void update(const c::io::Channel_OutputData&);
};
-
/* -------------------------------------------------------------------------- */
-
class gdMidiOutputBase : public gdWindow
{
public:
-
gdMidiOutputBase(int w, int h, ID channelId);
~gdMidiOutputBase();
-protected:
-
+ protected:
/* cb_close
close current window. */
static void cb_close(Fl_Widget* /*w*/, void* p);
- void cb_close();
+ void cb_close();
static void cb_enableLightning(Fl_Widget* /*w*/, void* p);
- void cb_enableLightning();
+ void cb_enableLightning();
/* setTitle
* set window title. */
void setTitle(ID channelId);
ID m_channelId;
-
+
c::io::Channel_OutputData m_data;
-
+
geLightningLearnerPack* m_learners;
geButton* m_close;
geCheck* m_enableLightning;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Pack.H>
+#include "midiOutputMidiCh.h"
#include "glue/io.h"
-#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/choice.h"
+#include "gui/elems/midiIO/midiLearner.h"
#include "utils/gui.h"
-#include "midiOutputMidiCh.h"
-
+#include <FL/Fl_Pack.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
: gdMidiOutputBase(300, 168, channelId)
setTitle(m_channelId + 1);
m_enableOut = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 150, G_GUI_UNIT, "Enable MIDI output");
- m_chanListOut = new geChoice(w()-108, G_GUI_OUTER_MARGIN, 100, G_GUI_UNIT);
-
- m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, m_chanListOut->y() + m_chanListOut->h() + G_GUI_OUTER_MARGIN,
- 120, G_GUI_UNIT, "Enable MIDI lightning output");
-
- m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
- m_enableLightning->y() + m_enableLightning->h() + G_GUI_OUTER_MARGIN, channelId);
-
+ m_chanListOut = new geChoice(w() - 108, G_GUI_OUTER_MARGIN, 100, G_GUI_UNIT);
+
+ m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, m_chanListOut->y() + m_chanListOut->h() + G_GUI_OUTER_MARGIN,
+ 120, G_GUI_UNIT, "Enable MIDI lightning output");
+
+ m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
+ m_enableLightning->y() + m_enableLightning->h() + G_GUI_OUTER_MARGIN, channelId);
+
m_close = new geButton(w() - 88, m_learners->y() + m_learners->h() + G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
add(m_enableOut);
m_chanListOut->add("Channel 15");
m_chanListOut->add("Channel 16");
m_chanListOut->value(0);
-
+
m_chanListOut->callback(cb_setChannel, (void*)this);
m_enableOut->callback(cb_enableOut, (void*)this);
m_enableLightning->callback(cb_enableLightning, (void*)this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputMidiCh::rebuild()
{
m_data = c::io::channel_getOutputData(m_channelId);
assert(m_data.output.has_value());
-
+
m_learners->update(m_data);
m_chanListOut->value(m_data.output->filter);
m_enableOut->value(m_data.output->enabled);
m_data.output->enabled ? m_chanListOut->activate() : m_chanListOut->deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
-void gdMidiOutputMidiCh::cb_enableOut (Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_enableOut(); }
+void gdMidiOutputMidiCh::cb_enableOut(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_enableOut(); }
void gdMidiOutputMidiCh::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_setChannel(); }
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputMidiCh::cb_enableOut()
{
c::io::channel_enableMidiOutput(m_channelId, m_enableOut->value());
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputMidiCh::cb_setChannel()
{
c::io::channel_setMidiOutputFilter(m_channelId, m_chanListOut->value());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_OUTPUT_MIDI_CH_H
#define GD_MIDI_OUTPUT_MIDI_CH_H
-
#include "midiOutputBase.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
+class geChoice;
class gdMidiOutputMidiCh : public gdMidiOutputBase
{
public:
-
gdMidiOutputMidiCh(ID channelId);
void rebuild() override;
-private:
-
- static void cb_enableOut (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_enableOut(Fl_Widget* /*w*/, void* p);
static void cb_setChannel(Fl_Widget* /*w*/, void* p);
- void cb_enableOut();
- void cb_setChannel();
+ void cb_enableOut();
+ void cb_setChannel();
geCheck* m_enableOut;
geChoice* m_chanListOut;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Pack.H>
+#include "midiOutputSampleCh.h"
#include "core/model/model.h"
-#include "utils/gui.h"
-#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
-#include "midiOutputSampleCh.h"
-
+#include "gui/elems/midiIO/midiLearner.h"
+#include "utils/gui.h"
+#include <FL/Fl_Pack.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gdMidiOutputSampleCh::gdMidiOutputSampleCh(ID channelId)
: gdMidiOutputBase(300, 140, channelId)
m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, 20, "Enable MIDI lightning output");
- m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
- m_enableLightning->y() + m_enableLightning->h() + 8, channelId);
+ m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
+ m_enableLightning->y() + m_enableLightning->h() + 8, channelId);
m_close = new geButton(w() - 88, m_learners->y() + m_learners->h() + 8, 80, 20, "Close");
m_close->callback(cb_close, (void*)this);
m_enableLightning->callback(cb_enableLightning, (void*)this);
-
+
u::gui::setFavicon(this);
set_modal();
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdMidiOutputSampleCh::rebuild()
{
m_data = c::io::channel_getOutputData(m_channelId);
m_enableLightning->value(m_data.lightningEnabled);
m_learners->update(m_data);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_MIDI_OUTPUT_SAMPLE_CH_H
#define GD_MIDI_OUTPUT_SAMPLE_CH_H
-
#include "midiOutputBase.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class gdMidiOutputSampleCh : public gdMidiOutputBase
{
public:
-
- gdMidiOutputSampleCh(ID channelId);
+ gdMidiOutputSampleCh(ID channelId);
void rebuild() override;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include "glue/plugin.h"
-#include "utils/gui.h"
+#include "pluginChooser.h"
#include "core/conf.h"
-#include "core/plugins/pluginManager.h"
#include "core/plugins/pluginHost.h"
-#include "gui/elems/plugin/pluginBrowser.h"
+#include "core/plugins/pluginManager.h"
+#include "glue/plugin.h"
+#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
-#include "gui/elems/basics/box.h"
-#include "pluginChooser.h"
-
+#include "gui/elems/plugin/pluginBrowser.h"
+#include "utils/gui.h"
-namespace giada {
+namespace giada
+{
namespace v
{
gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID channelId)
-: gdWindow (X, Y, W, H, "Available plugins")
+: gdWindow(X, Y, W, H, "Available plugins")
, m_channelId(channelId)
{
/* top area */
- Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
- sortMethod = new geChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
- geBox *b1 = new geBox(sortMethod->x()+sortMethod->w(), group_top->y(), 100, 20); // spacer window border <-> menu
+ Fl_Group* group_top = new Fl_Group(8, 8, w() - 16, 20);
+ sortMethod = new geChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
+ geBox* b1 = new geBox(sortMethod->x() + sortMethod->w(), group_top->y(), 100, 20); // spacer window border <-> menu
group_top->resizable(b1);
group_top->end();
/* center browser */
- browser = new v::gePluginBrowser(8, 36, w()-16, h()-70);
+ browser = new v::gePluginBrowser(8, 36, w() - 16, h() - 70);
/* ok/cancel buttons */
- Fl_Group *group_btn = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, h()-browser->h()-16);
- geBox *b2 = new geBox(8, browser->y()+browser->h(), 100, 20); // spacer window border <-> buttons
- addBtn = new geButton(w()-88, group_btn->y(), 80, 20, "Add");
- cancelBtn = new geButton(addBtn->x()-88, group_btn->y(), 80, 20, "Cancel");
+ Fl_Group* group_btn = new Fl_Group(8, browser->y() + browser->h() + 8, w() - 16, h() - browser->h() - 16);
+ geBox* b2 = new geBox(8, browser->y() + browser->h(), 100, 20); // spacer window border <-> buttons
+ addBtn = new geButton(w() - 88, group_btn->y(), 80, 20, "Add");
+ cancelBtn = new geButton(addBtn->x() - 88, group_btn->y(), 80, 20, "Cancel");
group_btn->resizable(b2);
group_btn->end();
sortMethod->add("Name");
sortMethod->add("Category");
sortMethod->add("Manufacturer");
- sortMethod->callback(cb_sort, (void*) this);
+ sortMethod->callback(cb_sort, (void*)this);
sortMethod->value(m::conf::conf.pluginSortMethod);
- addBtn->callback(cb_add, (void*) this);
+ addBtn->callback(cb_add, (void*)this);
addBtn->shortcut(FL_Enter);
- cancelBtn->callback(cb_close, (void*) this);
+ cancelBtn->callback(cb_close, (void*)this);
resizable(browser);
u::gui::setFavicon(this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
gdPluginChooser::~gdPluginChooser()
{
- m::conf::conf.pluginChooserX = x();
- m::conf::conf.pluginChooserY = y();
- m::conf::conf.pluginChooserW = w();
- m::conf::conf.pluginChooserH = h();
+ m::conf::conf.pluginChooserX = x();
+ m::conf::conf.pluginChooserY = y();
+ m::conf::conf.pluginChooserW = w();
+ m::conf::conf.pluginChooserH = h();
m::conf::conf.pluginSortMethod = sortMethod->value();
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginChooser::cb_close(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_close(); }
-void gdPluginChooser::cb_add (Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_add(); }
-void gdPluginChooser::cb_sort (Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_sort(); }
-
+void gdPluginChooser::cb_add(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_add(); }
+void gdPluginChooser::cb_sort(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_sort(); }
/* -------------------------------------------------------------------------- */
-
void gdPluginChooser::cb_close()
{
do_callback();
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginChooser::cb_sort()
{
m::pluginManager::sortPlugins(static_cast<m::pluginManager::SortMethod>(sortMethod->value()));
browser->refresh();
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginChooser::cb_add()
{
int pluginIndex = browser->value() - 3; // subtract header lines
c::plugin::addPlugin(pluginIndex, m_channelId);
do_callback();
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
#ifndef GD_PLUGIN_CHOOSER_H
#define GD_PLUGIN_CHOOSER_H
-
+#include "core/types.h"
+#include "window.h"
#include <FL/Fl.H>
#include <FL/Fl_Scroll.H>
-#include "window.h"
-
class geButton;
class geButton;
-namespace giada {
+namespace giada
+{
namespace v
{
class geChoice;
class gdPluginChooser : public gdWindow
{
public:
-
gdPluginChooser(int x, int y, int w, int h, ID channelId);
~gdPluginChooser();
-private:
-
+ private:
static void cb_close(Fl_Widget* /*w*/, void* p);
- static void cb_add (Fl_Widget* /*w*/, void* p);
- static void cb_sort (Fl_Widget* /*w*/, void* p);
- void cb_close();
- void cb_add ();
- void cb_sort ();
+ static void cb_add(Fl_Widget* /*w*/, void* p);
+ static void cb_sort(Fl_Widget* /*w*/, void* p);
+ void cb_close();
+ void cb_add();
+ void cb_sort();
geChoice* sortMethod;
geButton* addBtn;
ID m_channelId;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <cassert>
-#include <string>
+#include "pluginList.h"
#include "core/conf.h"
#include "core/const.h"
-#include "utils/string.h"
-#include "utils/gui.h"
-#include "gui/elems/basics/liquidScroll.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/liquidScroll.h"
#include "gui/elems/basics/statusButton.h"
-#include "gui/elems/mainWindow/mainIO.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/mainIO.h"
#include "gui/elems/plugin/pluginElement.h"
-#include "pluginChooser.h"
#include "mainWindow.h"
-#include "pluginList.h"
-
+#include "pluginChooser.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <cassert>
+#include <string>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
gdPluginList::gdPluginList(ID channelId)
-: gdWindow (m::conf::conf.pluginListX, m::conf::conf.pluginListY, 468, 204)
+: gdWindow(m::conf::conf.pluginListX, m::conf::conf.pluginListY, 468, 204)
, m_channelId(channelId)
{
end();
- list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN,
- w() - (G_GUI_OUTER_MARGIN*2), h() - (G_GUI_OUTER_MARGIN*2));
+ list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN,
+ w() - (G_GUI_OUTER_MARGIN * 2), h() - (G_GUI_OUTER_MARGIN * 2));
list->end();
add(list);
resizable(list);
show();
}
-
/* -------------------------------------------------------------------------- */
-
gdPluginList::~gdPluginList()
{
m::conf::conf.pluginListX = x();
m::conf::conf.pluginListY = y();
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginList::cb_addPlugin(Fl_Widget* /*v*/, void* p) { ((gdPluginList*)p)->cb_addPlugin(); }
-
/* -------------------------------------------------------------------------- */
-
void gdPluginList::rebuild()
{
m_plugins = c::plugin::getPlugins(m_channelId);
if (m_plugins.channelId == m::mixer::MASTER_OUT_CHANNEL_ID)
label("Master Out Plug-ins");
- else
- if (m_plugins.channelId == m::mixer::MASTER_IN_CHANNEL_ID)
+ else if (m_plugins.channelId == m::mixer::MASTER_IN_CHANNEL_ID)
label("Master In Plug-ins");
- else {
+ else
+ {
std::string l = "Channel " + u::string::iToString(m_plugins.channelId) + " Plug-ins";
copy_label(l.c_str());
}
list->clear();
list->scroll_to(0, 0);
- for (ID pluginId : m_plugins.pluginIds)
- list->addWidget(new gePluginElement(0, 0, c::plugin::getPlugin(pluginId, m_plugins.channelId)));
-
+ for (m::Plugin* plugin : m_plugins.plugins)
+ list->addWidget(new gePluginElement(0, 0, c::plugin::getPlugin(*plugin, m_plugins.channelId)));
+
addPlugin = list->addWidget(new geButton(0, 0, 0, G_GUI_UNIT, "-- add new plugin --"));
-
+
addPlugin->callback(cb_addPlugin, (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginList::cb_addPlugin()
{
int wx = m::conf::conf.pluginChooserX;
int wy = m::conf::conf.pluginChooserY;
int ww = m::conf::conf.pluginChooserW;
int wh = m::conf::conf.pluginChooserH;
- u::gui::openSubWindow(G_MainWin, new v::gdPluginChooser(wx, wy, ww, wh,
- m_plugins.channelId), WID_FX_CHOOSER);
+ u::gui::openSubWindow(G_MainWin, new v::gdPluginChooser(wx, wy, ww, wh, m_plugins.channelId), WID_FX_CHOOSER);
}
-
/* -------------------------------------------------------------------------- */
-
const gePluginElement& gdPluginList::getNextElement(const gePluginElement& currEl) const
{
int curr = list->find(currEl);
return *static_cast<gePluginElement*>(list->child(next));
}
-
const gePluginElement& gdPluginList::getPrevElement(const gePluginElement& currEl) const
{
int curr = list->find(currEl);
prev = 0;
return *static_cast<gePluginElement*>(list->child(prev));
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GD_PLUGINLIST_H
#define GD_PLUGINLIST_H
-
#include "glue/plugin.h"
#include "window.h"
-
class geLiquidScroll;
class geButton;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class gePluginElement;
class gdPluginList : public gdWindow
{
public:
-
gdPluginList(ID channelId);
~gdPluginList();
const gePluginElement& getNextElement(const gePluginElement& curr) const;
const gePluginElement& getPrevElement(const gePluginElement& curr) const;
-private:
-
+ private:
static void cb_addPlugin(Fl_Widget* /*w*/, void* p);
- void cb_addPlugin();
+ void cb_addPlugin();
geButton* addPlugin;
- geLiquidScroll* list;
+ geLiquidScroll* list;
- ID m_channelId;
+ ID m_channelId;
c::plugin::Plugins m_plugins;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <FL/fl_draw.H>
-#include "glue/plugin.h"
-#include "utils/gui.h"
+#include "pluginWindow.h"
#include "core/const.h"
+#include "glue/plugin.h"
#include "gui/elems/basics/liquidScroll.h"
#include "gui/elems/plugin/pluginParameter.h"
-#include "pluginWindow.h"
-
+#include "utils/gui.h"
+#include <FL/fl_draw.H>
-namespace giada {
-namespace v
+namespace giada::v
{
gdPluginWindow::gdPluginWindow(const c::plugin::Plugin& plugin)
: gdWindow(450, 156)
{
set_non_modal();
- m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN,
- w()-(G_GUI_OUTER_MARGIN*2), h()-(G_GUI_OUTER_MARGIN*2));
-
+ m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN,
+ w() - (G_GUI_OUTER_MARGIN * 2), h() - (G_GUI_OUTER_MARGIN * 2));
+
m_list->type(Fl_Scroll::VERTICAL_ALWAYS);
m_list->begin();
- int labelWidth = 100; // TODO
- for (int index : m_plugin.paramIndexes) {
- int py = m_list->y() + (index * (G_GUI_UNIT + G_GUI_INNER_MARGIN));
- int pw = m_list->w() - m_list->scrollbar_size() - (G_GUI_OUTER_MARGIN*3);
- new v::gePluginParameter(m_list->x(), py, pw, labelWidth, c::plugin::getParam(index, m_plugin.id));
- }
+ int labelWidth = 100; // TODO
+ for (int index : m_plugin.paramIndexes)
+ {
+ int py = m_list->y() + (index * (G_GUI_UNIT + G_GUI_INNER_MARGIN));
+ int pw = m_list->w() - m_list->scrollbar_size() - (G_GUI_OUTER_MARGIN * 3);
+ new v::gePluginParameter(m_list->x(), py, pw, labelWidth, c::plugin::getParam(index, m_plugin.getPluginRef(), m_plugin.channelId));
+ }
m_list->end();
end();
label(m_plugin.name.c_str());
- size_range(450, (G_GUI_UNIT + (G_GUI_OUTER_MARGIN*2)));
+ size_range(450, (G_GUI_UNIT + (G_GUI_OUTER_MARGIN * 2)));
resizable(m_list);
u::gui::setFavicon(this);
show();
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginWindow::updateParameters(bool changeSlider)
{
for (int index : m_plugin.paramIndexes)
- static_cast<v::gePluginParameter*>(m_list->child(index))->update(c::plugin::getParam(index, m_plugin.id), changeSlider);
+ static_cast<v::gePluginParameter*>(m_list->child(index))->update(c::plugin::getParam(index, m_plugin.getPluginRef(), m_plugin.channelId), changeSlider);
}
-}} // giada::v::
-
+} // namespace giada::v
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GD_PLUGIN_WINDOW_H
#define GD_PLUGIN_WINDOW_H
-
#include "window.h"
-
class geBox;
class geSlider;
class geLiquidScroll;
-
-namespace giada {
-namespace c {
-namespace plugin
+namespace giada::c::plugin
{
-class Plugin;
-}}
-namespace m
+struct Plugin;
+}
+namespace giada::m
{
class Plugin;
}
-namespace v
+namespace giada::v
{
class gdPluginWindow : public gdWindow
{
public:
-
gdPluginWindow(const c::plugin::Plugin&);
- void updateParameters(bool changeSlider=false);
+ void updateParameters(bool changeSlider = false);
-private:
-
+ private:
const c::plugin::Plugin& m_plugin;
-
+
geLiquidScroll* m_list;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <FL/x.H>
-#include "utils/log.h"
-#include "utils/gui.h"
-#include "glue/plugin.h"
-#include "core/const.h"
#include "pluginWindowGUI.h"
+#include "core/const.h"
+#include "glue/plugin.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include <FL/x.H>
#ifdef G_OS_MAC
#import "utils/cocoa.h" // objective-c
#endif
-
-namespace giada {
+namespace giada
+{
namespace v
{
gdPluginWindowGUI::gdPluginWindowGUI(c::plugin::Plugin& p)
: gdWindow(320, 200)
#endif
, m_plugin(p)
-, m_ui (nullptr)
+, m_ui(nullptr)
{
show();
#endif
u::log::print("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
- (void*) this, (void*) fl_xid(this));
+ (void*)this, (void*)fl_xid(this));
#ifdef G_OS_MAC
- void* cocoaWindow = (void*) fl_xid(this);
+ void* cocoaWindow = (void*)fl_xid(this);
openEditor(cocoa_getViewFromWindow(cocoaWindow));
#else
- openEditor((void*) fl_xid(this));
+ openEditor((void*)fl_xid(this));
int pluginW = m_ui->getWidth();
int pluginH = m_ui->getHeight();
resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
- m_plugin.setResizeCallback([this] (int w, int h)
- {
+ m_plugin.setResizeCallback([this](int w, int h) {
resize(x(), y(), w, h);
});
#endif
#ifdef G_OS_LINUX
- Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+ Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*)this);
#endif
copy_label(m_plugin.name.c_str());
}
-
/* -------------------------------------------------------------------------- */
-
gdPluginWindowGUI::~gdPluginWindowGUI()
{
cb_close();
}
-
/* -------------------------------------------------------------------------- */
-
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()
{
#ifdef G_OS_LINUX
Fl::remove_timeout(cb_refresh);
#endif
closeEditor();
- u::log::print("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
+ u::log::print("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginWindowGUI::cb_refresh()
{
m::pluginHost::runDispatchLoop();
- Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+ Fl::repeat_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginWindowGUI::openEditor(void* parent)
{
m_ui = m_plugin.createEditor();
- if (m_ui == nullptr) {
+ if (m_ui == nullptr)
+ {
u::log::print("[gdPluginWindowGUI::openEditor] unable to create editor!\n");
return;
}
m_ui->setOpaque(true);
- m_ui->addToDesktop(0, parent);
+ m_ui->addToDesktop(0, parent);
}
-
/* -------------------------------------------------------------------------- */
-
void gdPluginWindowGUI::closeEditor()
{
delete m_ui;
m_ui = nullptr;
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GD_PLUGIN_WINDOW_GUI_H
#define GD_PLUGIN_WINDOW_GUI_H
-
+#include "deps/juce-config.h"
+#include "window.h"
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
-#include "window.h"
-
-namespace giada {
-namespace c {
+namespace giada
+{
+namespace c
+{
namespace plugin
{
struct Plugin;
-}}
+}
+} // namespace c
namespace v
{
class gdPluginWindowGUI : public gdWindow
{
public:
-
gdPluginWindowGUI(c::plugin::Plugin&);
~gdPluginWindowGUI();
-private:
-
+ private:
static void cb_close(Fl_Widget* /*w*/, void* p);
static void cb_refresh(void* data);
- void cb_close();
- void cb_refresh();
+ void cb_close();
+ void cb_refresh();
- void openEditor(void* parent);
- void closeEditor();
+ void openEditor(void* parent);
+ void closeEditor();
c::plugin::Plugin& m_plugin;
juce::AudioProcessorEditor* m_ui;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // include guard
-
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <cassert>
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include "glue/channel.h"
#include "glue/sampleEditor.h"
-#include "core/waveFx.h"
#include "core/conf.h"
#include "core/const.h"
#include "core/graphics.h"
#include "core/mixer.h"
#include "core/wave.h"
-#include "utils/gui.h"
-#include "utils/string.h"
+#include "core/waveFx.h"
+#include "glue/channel.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/statusButton.h"
-#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/check.h"
#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/dial.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/check.h"
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/group.h"
-#include "gui/elems/sampleEditor/waveform.h"
-#include "gui/elems/sampleEditor/waveTools.h"
-#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/sampleEditor/boostTool.h"
#include "gui/elems/sampleEditor/panTool.h"
#include "gui/elems/sampleEditor/pitchTool.h"
#include "gui/elems/sampleEditor/rangeTool.h"
#include "gui/elems/sampleEditor/shiftTool.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "gui/dialogs/warnings.h"
+#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/sampleEditor/waveTools.h"
+#include "gui/elems/sampleEditor/waveform.h"
#include "sampleEditor.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <cassert>
+#include <cmath>
-
-namespace giada {
-namespace v
+namespace giada::v
{
gdSampleEditor::gdSampleEditor(ID channelId)
-: gdWindow (m::conf::conf.sampleEditorX, m::conf::conf.sampleEditorY,
- m::conf::conf.sampleEditorW, m::conf::conf.sampleEditorH)
+: gdWindow(m::conf::conf.sampleEditorX, m::conf::conf.sampleEditorY,
+ m::conf::conf.sampleEditorW, m::conf::conf.sampleEditorH)
, m_channelId(channelId)
{
end();
gePack* upperBar = createUpperBar();
-
- waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN,
- w()-16, h()-168);
-
- gePack* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN,
- h()-waveTools->h()-upperBar->h()-32);
+
+ waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y() + upperBar->h() + G_GUI_OUTER_MARGIN,
+ w() - 16, h() - 168);
+
+ gePack* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y() + waveTools->h() + G_GUI_OUTER_MARGIN,
+ h() - waveTools->h() - upperBar->h() - 32);
add(upperBar);
add(waveTools);
show();
}
-
/* -------------------------------------------------------------------------- */
-
gdSampleEditor::~gdSampleEditor()
{
- m::conf::conf.sampleEditorX = x();
- m::conf::conf.sampleEditorY = y();
- m::conf::conf.sampleEditorW = w();
- m::conf::conf.sampleEditorH = h();
+ m::conf::conf.sampleEditorX = x();
+ m::conf::conf.sampleEditorY = y();
+ m::conf::conf.sampleEditorW = w();
+ m::conf::conf.sampleEditorH = h();
m::conf::conf.sampleEditorGridVal = atoi(grid->text());
m::conf::conf.sampleEditorGridOn = snap->value();
-
+
c::sampleEditor::stopPreview();
c::sampleEditor::cleanupPreview();
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::rebuild()
{
m_data = c::sampleEditor::getData(m_channelId);
updateInfo();
if (m_data.isLogical) // Logical samples (aka takes) cannot be reloaded.
- reload->deactivate();
+ reload->deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::refresh()
{
waveTools->refresh();
play->setStatus(m_data.a_getPreviewStatus() == ChannelStatus::PLAY);
}
-
/* -------------------------------------------------------------------------- */
-
gePack* gdSampleEditor::createUpperBar()
{
reload = new geButton(0, 0, 70, G_GUI_UNIT, "Reload");
grid = new geChoice(0, 0, 50, G_GUI_UNIT);
- snap = new geCheck (0, 0, 12, G_GUI_UNIT, "Snap");
- sep1 = new geBox (0, 0, w() - 208, G_GUI_UNIT);
+ snap = new geCheck(0, 0, 12, G_GUI_UNIT, "Snap");
+ sep1 = new geBox(0, 0, w() - 208, G_GUI_UNIT);
zoomOut = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
zoomIn = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
grid->add("16");
grid->add("32");
grid->add("64");
+ grid->copy_tooltip("Grid frequency");
+
if (m::conf::conf.sampleEditorGridVal == 0)
grid->value(0);
- else
+ else
grid->value(grid->find_item(u::string::iToString(m::conf::conf.sampleEditorGridVal).c_str()));
grid->callback(cb_changeGrid, (void*)this);
snap->value(m::conf::conf.sampleEditorGridOn);
+ snap->copy_tooltip("Snap to grid");
snap->callback(cb_enableSnap, (void*)this);
/* TODO - redraw grid if != (off) */
zoomOut->callback(cb_zoomOut, (void*)this);
+ zoomOut->copy_tooltip("Zoom out");
zoomIn->callback(cb_zoomIn, (void*)this);
+ zoomIn->copy_tooltip("Zoom in");
gePack* g = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL);
g->add(reload);
g->add(snap);
g->add(sep1);
g->add(zoomOut);
- g->add(zoomIn);
+ g->add(zoomIn);
g->resizable(sep1);
return g;
}
-\
/* -------------------------------------------------------------------------- */
-\
gePack* gdSampleEditor::createOpTools(int x, int y)
{
volumeTool = new geVolumeTool(m_data, 0, 0);
- panTool = new gePanTool (m_data, 0, 0);
- pitchTool = new gePitchTool (m_data, 0, 0);
- rangeTool = new geRangeTool (m_data, 0, 0);
- shiftTool = new geShiftTool (m_data, 0, 0);
-
+ panTool = new gePanTool(m_data, 0, 0);
+ pitchTool = new gePitchTool(m_data, 0, 0);
+ rangeTool = new geRangeTool(m_data, 0, 0);
+ shiftTool = new geShiftTool(m_data, 0, 0);
+
gePack* g = new gePack(x, y, Direction::VERTICAL);
g->add(volumeTool);
g->add(panTool);
return g;
}
-
/* -------------------------------------------------------------------------- */
-
geGroup* gdSampleEditor::createPreviewBox(int x, int y, int h)
{
rewind = new geButton(x, y + (h / 2) - 12, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
return g;
}
-
/* -------------------------------------------------------------------------- */
-
gePack* gdSampleEditor::createBottomBar(int x, int y, int h)
{
geGroup* previewBox = createPreviewBox(0, 0, h);
- geBox* divisor1 = new geBox (0, 0, 1, h);
- Fl_Group* opTools = createOpTools (0, 0);
- geBox* divisor2 = new geBox (0, 0, 1, h);
- info = new geBox (0, 0, 400, h);
+ geBox* divisor1 = new geBox(0, 0, 1, h);
+ Fl_Group* opTools = createOpTools(0, 0);
+ geBox* divisor2 = new geBox(0, 0, 1, h);
+ info = new geBox(0, 0, 400, h);
divisor1->box(FL_BORDER_BOX);
divisor2->box(FL_BORDER_BOX);
return g;
}
-
/* -------------------------------------------------------------------------- */
-
-void gdSampleEditor::cb_reload (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_reload(); }
-void gdSampleEditor::cb_zoomIn (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomIn(); }
-void gdSampleEditor::cb_zoomOut (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomOut(); }
-void gdSampleEditor::cb_changeGrid (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); }
-void gdSampleEditor::cb_enableSnap (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); }
+void gdSampleEditor::cb_reload(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_reload(); }
+void gdSampleEditor::cb_zoomIn(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomIn(); }
+void gdSampleEditor::cb_zoomOut(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomOut(); }
+void gdSampleEditor::cb_changeGrid(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); }
+void gdSampleEditor::cb_enableSnap(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); }
void gdSampleEditor::cb_togglePreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_togglePreview(); }
void gdSampleEditor::cb_rewindPreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_rewindPreview(); }
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_enableSnap()
{
waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_togglePreview()
{
if (!play->getStatus())
c::sampleEditor::stopPreview();
}
-
void gdSampleEditor::cb_rewindPreview()
{
c::sampleEditor::setPreviewTracker(m_data.begin);
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_reload()
{
- c::sampleEditor::reload(m_data.channelId, m_data.waveId);
+ c::sampleEditor::reload(m_data.channelId);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_zoomIn()
{
waveTools->waveform->setZoom(geWaveform::Zoom::IN);
waveTools->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_zoomOut()
{
waveTools->waveform->setZoom(geWaveform::Zoom::OUT);
waveTools->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::cb_changeGrid()
{
waveTools->waveform->setGridLevel(atoi(grid->text()));
}
-
/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::updateInfo()
{
std::string bitDepth = m_data.waveBits != 0 ? u::string::iToString(m_data.waveBits) : "(unknown)";
- std::string infoText =
- "File: " + m_data.wavePath + "\n"
- "Size: " + u::string::iToString(m_data.waveSize) + " frames\n"
- "Duration: " + u::string::iToString(m_data.waveDuration) + " seconds\n"
- "Bit depth: " + bitDepth + "\n"
- "Frequency: " + u::string::iToString(m_data.waveRate) + " Hz\n";
+ std::string infoText =
+ "File: " + m_data.wavePath + "\n"
+ "Size: " +
+ u::string::iToString(m_data.waveSize) + " frames\n"
+ "Duration: " +
+ u::string::iToString(m_data.waveDuration) + " seconds\n"
+ "Bit depth: " +
+ bitDepth + "\n"
+ "Frequency: " +
+ u::string::iToString(m_data.waveRate) + " Hz\n";
info->copy_label(infoText.c_str());
}
-}} // giada::v::
+} // namespace giada::v
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_EDITOR_H
#define GD_EDITOR_H
-
#include "core/types.h"
#include "glue/sampleEditor.h"
#include "window.h"
-
class geButton;
class geCheck;
class geBox;
class geButton;
class geStatusButton;
-
-namespace giada {
-namespace m
+namespace giada::m
{
class Wave;
}
-namespace v
+namespace giada::v
{
class geChoice;
class gePack;
class geShiftTool;
class gdSampleEditor : public gdWindow
{
-friend class geWaveform;
+ friend class geWaveform;
public:
-
gdSampleEditor(ID channelId);
~gdSampleEditor();
geBox* sep1;
geButton* zoomIn;
geButton* zoomOut;
-
+
geWaveTools* waveTools;
geVolumeTool* volumeTool;
geCheck* loop;
geBox* info;
-
-private:
-
- gePack* createUpperBar();
- gePack* createBottomBar(int x, int y, int h);
+ private:
+ gePack* createUpperBar();
+ gePack* createBottomBar(int x, int y, int h);
geGroup* createPreviewBox(int x, int y, int h);
- gePack* createOpTools(int x, int y);
+ gePack* createOpTools(int x, int y);
- static void cb_reload (Fl_Widget* /*w*/, void* p);
- static void cb_zoomIn (Fl_Widget* /*w*/, void* p);
- static void cb_zoomOut (Fl_Widget* /*w*/, void* p);
+ static void cb_reload(Fl_Widget* /*w*/, void* p);
+ static void cb_zoomIn(Fl_Widget* /*w*/, void* p);
+ static void cb_zoomOut(Fl_Widget* /*w*/, void* p);
static void cb_changeGrid(Fl_Widget* /*w*/, void* p);
static void cb_enableSnap(Fl_Widget* /*w*/, void* p);
static void cb_togglePreview(Fl_Widget* /*w*/, void* p);
static void cb_rewindPreview(Fl_Widget* /*w*/, void* p);
- void cb_reload();
- void cb_zoomIn();
- void cb_zoomOut();
- void cb_changeGrid();
- void cb_enableSnap();
- void cb_togglePreview();
- void cb_rewindPreview();
+ void cb_reload();
+ void cb_zoomIn();
+ void cb_zoomOut();
+ void cb_changeGrid();
+ void cb_enableSnap();
+ void cb_togglePreview();
+ void cb_rewindPreview();
void updateInfo();
c::sampleEditor::Data m_data;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include "utils/gui.h"
+#include "warnings.h"
#include "core/const.h"
-#include "gui/elems/basics/button.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+#include "utils/gui.h"
#include "window.h"
-#include "warnings.h"
-
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
void gdAlert(const char* c)
{
gdWindow* modal = new gdWindow(
- (Fl::w() / 2) - 150,
- (Fl::h() / 2) - 47,
- 300, 90, "Alert");
+ (Fl::w() / 2) - 150,
+ (Fl::h() / 2) - 47,
+ 300, 90, "Alert");
modal->set_modal();
modal->begin();
- geBox* box = new geBox(10, 10, 280, 40, c);
- geButton* b = new geButton(210, 60, 80, 20, "Close");
+ geBox* box = new geBox(10, 10, 280, 40, c);
+ geButton* b = new geButton(210, 60, 80, 20, "Close");
modal->end();
box->labelsize(G_GUI_FONT_SIZE_BASE);
- b->callback(cb_window_closer, (void *)modal);
+ b->callback(cb_window_closer, (void*)modal);
b->shortcut(FL_Enter);
u::gui::setFavicon(modal);
modal->show();
}
-
int gdConfirmWin(const char* title, const char* msg)
{
gdWindow* win = new gdWindow(
- (Fl::w() / 2) - 150,
- (Fl::h() / 2) - 47,
- 300, 90, title);
+ (Fl::w() / 2) - 150,
+ (Fl::h() / 2) - 47,
+ 300, 90, title);
win->set_modal();
win->begin();
- new geBox(10, 10, 280, 40, msg);
- geButton* ok = new geButton(212, 62, 80, 20, "Ok");
- geButton* ko = new geButton(124, 62, 80, 20, "Cancel");
+ new geBox(10, 10, 280, 40, msg);
+ geButton* ok = new geButton(212, 62, 80, 20, "Ok");
+ geButton* ko = new geButton(124, 62, 80, 20, "Cancel");
win->end();
ok->shortcut(FL_Enter);
u::gui::setFavicon(win);
/* no callbacks here. readqueue() check the event stack. */
int r = 0;
- while (true) {
+ while (true)
+ {
Fl_Widget* o = Fl::readqueue();
- if (!o) Fl::wait();
- else if (o == ok) {r = 1; break;}
- else if (o == ko) {r = 0; break;}
+ if (!o)
+ Fl::wait();
+ else if (o == ok)
+ {
+ r = 1;
+ break;
+ }
+ else if (o == ko)
+ {
+ r = 0;
+ break;
+ }
}
//delete win;
win->hide();
return r;
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_WARNINGS_H
#define GD_WARNINGS_H
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
-void gdAlert(const char *c);
-int gdConfirmWin(const char *title, const char *msg);
-}} // giada::v::
+void gdAlert(const char* c);
+int gdConfirmWin(const char* title, const char* msg);
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "utils/log.h"
#include "window.h"
+#include "utils/log.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
void cb_window_closer(Fl_Widget* /*v*/, void* p)
{
- delete (Fl_Window*) p;
+ delete (Fl_Window*)p;
}
-
/* -------------------------------------------------------------------------- */
-
gdWindow::gdWindow(int x, int y, int w, int h, const char* title, int id)
- : Fl_Double_Window(x, y, w, h, title), id(id), parent(nullptr)
+: Fl_Double_Window(x, y, w, h, title)
+, id(id)
+, parent(nullptr)
{
}
-
/* -------------------------------------------------------------------------- */
-
gdWindow::gdWindow(int w, int h, const char* title, int id)
- : Fl_Double_Window(w, h, title), id(id), parent(nullptr)
+: Fl_Double_Window(w, h, title)
+, id(id)
+, parent(nullptr)
{
}
-
/* -------------------------------------------------------------------------- */
-
gdWindow::~gdWindow()
{
/* delete all subwindows in order to empty the stack */
subWindows.clear();
}
-
/* -------------------------------------------------------------------------- */
/* this is the default callback of each window, fired when the user closes
void gdWindow::cb_closeChild(Fl_Widget* w, void* /*p*/)
{
- gdWindow* child = (gdWindow*) w;
+ gdWindow* child = (gdWindow*)w;
if (child->getParent() != nullptr)
(child->getParent())->delSubWindow(child);
}
-
/* -------------------------------------------------------------------------- */
-
void gdWindow::addSubWindow(gdWindow* w)
{
w->setParent(this);
//debug();
}
-
/* -------------------------------------------------------------------------- */
-
void gdWindow::delSubWindow(gdWindow* w)
{
for (unsigned j = 0; j < subWindows.size(); j++)
- if (w->getId() == subWindows.at(j)->getId()) {
+ if (w->getId() == subWindows.at(j)->getId())
+ {
delete subWindows.at(j);
subWindows.erase(subWindows.begin() + j);
return;
}
}
-
/* -------------------------------------------------------------------------- */
-
void gdWindow::delSubWindow(int wid)
{
for (unsigned j = 0; j < subWindows.size(); j++)
- if (subWindows.at(j)->getId() == wid) {
+ if (subWindows.at(j)->getId() == wid)
+ {
delete subWindows.at(j);
subWindows.erase(subWindows.begin() + j);
return;
}
}
-
/* -------------------------------------------------------------------------- */
-
int gdWindow::getId() const
{
return id;
}
-
void gdWindow::setId(int wid)
{
id = wid;
}
-
/* -------------------------------------------------------------------------- */
-
void gdWindow::debug() const
{
/* TODO - use G_DEBUG
*/
}
-
/* -------------------------------------------------------------------------- */
-
gdWindow* gdWindow::getParent()
{
return parent;
}
-
void gdWindow::setParent(gdWindow* w)
{
parent = w;
}
-
/* -------------------------------------------------------------------------- */
-
bool gdWindow::hasWindow(int wid) const
{
for (unsigned j = 0; j < subWindows.size(); j++)
return false;
}
-
/* -------------------------------------------------------------------------- */
-
gdWindow* gdWindow::getChild(int wid)
{
for (unsigned j = 0; j < subWindows.size(); j++)
return subWindows.at(j);
return nullptr;
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GD_WINDOW_H
#define GD_WINDOW_H
-
-#include <vector>
#include <FL/Fl_Double_Window.H>
+#include <vector>
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
/* cb_window_closer
Callback for closing windows. Deletes the widget (delete). */
class gdWindow : public Fl_Double_Window
{
public:
-
- gdWindow(int x, int y, int w, int h, const char* title=0, int id=0);
- gdWindow(int w, int h, const char* title=0, int id=0);
+ gdWindow(int x, int y, int w, int h, const char* title = 0, int id = 0);
+ gdWindow(int w, int h, const char* title = 0, int id = 0);
~gdWindow();
static void cb_closeChild(Fl_Widget* /*w*/, void* p);
Rebuild() is called by the View Updater when something structural changes
(e.g. a new channel added). Refresh() is called periodically by the View
Updater during the refresh loop. */
-
- virtual void rebuild() {};
- virtual void refresh() {};
+
+ virtual void rebuild(){};
+ virtual void refresh(){};
/* hasWindow
True if the window with id 'id' exists in the stack. */
bool hasWindow(int id) const;
-
+
int getId() const;
void debug() const;
- void addSubWindow(gdWindow* w);
- void delSubWindow(gdWindow* w);
- void delSubWindow(int id);
- void setId(int id);
- void setParent(gdWindow* w);
+ void addSubWindow(gdWindow* w);
+ void delSubWindow(gdWindow* w);
+ void delSubWindow(int id);
+ void setId(int id);
+ void setParent(gdWindow* w);
gdWindow* getParent();
gdWindow* getChild(int id);
-protected:
-
+ protected:
std::vector<gdWindow*> subWindows;
- int id;
- gdWindow* parent;
+ int id;
+ gdWindow* parent;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
+#include "dispatcher.h"
#include "core/init.h"
#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "dispatcher.h"
-
+#include <FL/Fl.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace v {
+namespace giada
+{
+namespace v
+{
namespace dispatcher
{
namespace
bool esc_ = false;
bool key_ = false;
-
std::function<void()> signalCb_ = nullptr;
-
/* -------------------------------------------------------------------------- */
-
void perform_(ID channelId, int event)
{
- if (event == FL_KEYDOWN) {
+ if (event == FL_KEYDOWN)
+ {
if (Fl::event_ctrl())
c::events::toggleMuteChannel(channelId, Thread::MAIN);
- else
- if (Fl::event_shift())
+ else if (Fl::event_shift())
c::events::killChannel(channelId, Thread::MAIN);
else
c::events::pressChannel(channelId, G_MAX_VELOCITY, Thread::MAIN);
}
- else
- if (event == FL_KEYUP)
+ else if (event == FL_KEYUP)
c::events::releaseChannel(channelId, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
/* Walk channels array, trying to match button's bound key with the event. If
found, trigger the key-press/key-release function. */
void dispatchChannels_(int event)
{
- G_MainWin->keyboard->forEachChannel([=](geChannel& c)
- {
+ G_MainWin->keyboard->forEachChannel([=](geChannel& c) {
if (c.handleKey(event))
perform_(c.getData().id, event);
});
}
-
/* -------------------------------------------------------------------------- */
-
void triggerSignalCb_()
{
- if (signalCb_ == nullptr)
+ if (signalCb_ == nullptr)
return;
signalCb_();
signalCb_ = nullptr;
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void dispatchKey(int event)
{
/* These events come from the keyboard, not from a direct interaction on the
UI with the mouse/touch. */
- if (event == FL_KEYDOWN) {
- if (Fl::event_key() == FL_BackSpace && !backspace_) {
+ if (event == FL_KEYDOWN)
+ {
+ if (Fl::event_key() == FL_BackSpace && !backspace_)
+ {
backspace_ = true;
c::events::rewindSequencer(Thread::MAIN);
}
- else if (Fl::event_key() == FL_End && !end_) {
+ else if (Fl::event_key() == FL_End && !end_)
+ {
end_ = true;
c::events::toggleInputRecording();
}
- else if (Fl::event_key() == FL_Enter && !enter_) {
+ else if (Fl::event_key() == FL_Enter && !enter_)
+ {
enter_ = true;
c::events::toggleActionRecording();
}
- else if (Fl::event_key() == ' ' && !space_) {
+ else if (Fl::event_key() == ' ' && !space_)
+ {
space_ = true;
c::events::toggleSequencer(Thread::MAIN);
}
- else if (Fl::event_key() == FL_Escape && !esc_) {
+ else if (Fl::event_key() == FL_Escape && !esc_)
+ {
esc_ = true;
m::init::closeMainWindow();
}
- else if (!key_) {
+ else if (!key_)
+ {
key_ = true;
triggerSignalCb_();
dispatchChannels_(event);
}
}
- else if (event == FL_KEYUP) {
+ else if (event == FL_KEYUP)
+ {
if (Fl::event_key() == FL_BackSpace)
backspace_ = false;
else if (Fl::event_key() == FL_End)
enter_ = false;
else if (Fl::event_key() == FL_Escape)
esc_ = false;
- else {
+ else
+ {
key_ = false;
dispatchChannels_(event);
}
}
}
-
/* -------------------------------------------------------------------------- */
-
void dispatchTouch(const geChannel& gch, bool status)
{
triggerSignalCb_();
perform_(gch.getData().id, status ? FL_KEYDOWN : FL_KEYUP);
}
-
/* -------------------------------------------------------------------------- */
-
void setSignalCallback(std::function<void()> f)
{
signalCb_ = f;
}
-}}} // giada::v::dispatcher
+} // namespace dispatcher
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_V_DISPATCHER_H
#define G_V_DISPATCHER_H
-
#include <functional>
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geChannel;
void dispatchTouch(const geChannel& gch, bool status);
void setSignalCallback(std::function<void()> f);
-}}} // giada::v::dispatcher
-
+} // namespace dispatcher
+} // namespace v
+} // namespace giada
#endif
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "drawing.h"
+#include <FL/Fl.H>
+
+namespace giada::v
+{
+void drawRectf(geompp::Rect<int> r, Fl_Color c)
+{
+ fl_rectf(r.x, r.y, r.w, r.h, c);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void drawRect(geompp::Rect<int> r, Fl_Color c)
+{
+ fl_rect(r.x, r.y, r.w, r.h, c);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void drawLine(geompp::Line<int> l, Fl_Color c)
+{
+ fl_color(c);
+ fl_line(l.x1, l.y1, l.x2, l.y2);
+}
+} // namespace giada::v
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef G_V_DRAWING_H
+#define G_V_DRAWING_H
+
+#include "deps/geompp/src/rect.hpp"
+#include <FL/fl_draw.H>
+
+namespace giada::v
+{
+void drawRectf(geompp::Rect<int> r, Fl_Color c);
+void drawRect(geompp::Rect<int> r, Fl_Color c);
+void drawLine(geompp::Line<int> l, Fl_Color c);
+} // namespace giada::v
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "baseAction.h"
#include <FL/Fl.H>
#include <FL/fl_draw.H>
-#include "baseAction.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable,
- m::Action a1, m::Action a2)
-: Fl_Box (X, Y, W, H)
+ m::Action a1, m::Action a2)
+: Fl_Box(X, Y, W, H)
, onRightEdge(false)
-, onLeftEdge (false)
-, hovered (false)
-, altered (false)
-, pick (0)
-, a1 (a1)
-, a2 (a2)
+, onLeftEdge(false)
+, hovered(false)
+, altered(false)
+, pick(0)
+, a1(a1)
+, a2(a2)
, m_resizable(resizable)
{
if (w() < MIN_WIDTH)
size(MIN_WIDTH, h());
}
-
/* -------------------------------------------------------------------------- */
-
int geBaseAction::handle(int e)
{
- switch (e) {
- case FL_ENTER: {
- hovered = true;
- redraw();
- return 1;
- }
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- hovered = false;
- redraw();
- return 1;
- }
- case FL_MOVE: {
- if (m_resizable) {
- onLeftEdge = false;
- onRightEdge = false;
- if (Fl::event_x() >= x() && Fl::event_x() < x() + HANDLE_WIDTH) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x() + w() - HANDLE_WIDTH &&
- Fl::event_x() <= x() + w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ switch (e)
+ {
+ case FL_ENTER:
+ {
+ hovered = true;
+ redraw();
+ return 1;
+ }
+ case FL_LEAVE:
+ {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ hovered = false;
+ redraw();
+ return 1;
+ }
+ case FL_MOVE:
+ {
+ if (m_resizable)
+ {
+ onLeftEdge = false;
+ onRightEdge = false;
+ if (Fl::event_x() >= x() && Fl::event_x() < x() + HANDLE_WIDTH)
+ {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
}
- return 1;
+ else if (Fl::event_x() >= x() + w() - HANDLE_WIDTH &&
+ Fl::event_x() <= x() + w())
+ {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
}
- default:
- return Fl_Widget::handle(e);
+ return 1;
+ }
+ default:
+ return Fl_Widget::handle(e);
}
}
-
/* -------------------------------------------------------------------------- */
-
void geBaseAction::setLeftEdge(Pixel p)
{
resize(p, y(), x() - p + w(), h());
size(MIN_WIDTH, h());
}
-
/* -------------------------------------------------------------------------- */
-
void geBaseAction::setRightEdge(Pixel p)
{
size(p, h());
size(MIN_WIDTH, h());
}
-
/* -------------------------------------------------------------------------- */
-
void geBaseAction::setPosition(Pixel p)
{
position(p, y());
}
-
/* -------------------------------------------------------------------------- */
-
bool geBaseAction::isOnEdges() const
{
return onLeftEdge || onRightEdge;
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BASE_ACTION_H
#define GE_BASE_ACTION_H
-
-#include <FL/Fl_Box.H>
#include "core/recorder.h"
#include "core/types.h"
+#include <FL/Fl_Box.H>
-
-namespace giada {
-namespace m
+namespace giada::m
{
struct Action;
}
-namespace v
+namespace giada::v
{
class geBaseAction : public Fl_Box
{
public:
-
static const Pixel MIN_WIDTH = 12;
static const Pixel HANDLE_WIDTH = 6;
- geBaseAction(Pixel x, Pixel y, Pixel w, Pixel h, bool resizable,
- m::Action a1, m::Action a2);
+ geBaseAction(Pixel x, Pixel y, Pixel w, Pixel h, bool resizable,
+ m::Action a1, m::Action a2);
int handle(int e) override;
void setPosition(Pixel p);
- bool onRightEdge;
- bool onLeftEdge;
- bool hovered;
- bool altered;
+ bool onRightEdge;
+ bool onLeftEdge;
+ bool hovered;
+ bool altered;
Pixel pick;
m::Action a1;
m::Action a2;
-
-protected:
-
+
+ protected:
bool m_resizable;
};
-}} // giada::v::
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "core/const.h"
-#include "core/clock.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
-#include "gridTool.h"
#include "baseAction.h"
#include "baseActionEditor.h"
+#include "core/clock.h"
+#include "core/const.h"
+#include "gridTool.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
-
-namespace giada {
+namespace giada
+{
namespace v
{
geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h,
- gdBaseActionEditor* base)
+ gdBaseActionEditor* base)
: Fl_Group(x, y, w, h)
-, m_data (nullptr)
-, m_base (base)
+, m_data(nullptr)
+, m_base(base)
, m_action(nullptr)
{
}
-
/* -------------------------------------------------------------------------- */
-
geBaseAction* geBaseActionEditor::getActionAtCursor() const
{
- for (int i = 0; i < children(); i++) {
+ for (int i = 0; i < children(); i++)
+ {
geBaseAction* a = static_cast<geBaseAction*>(child(i));
if (a->hovered)
return a;
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
void geBaseActionEditor::baseDraw(bool clear) const
{
/* Clear the screen. */
/* Draw grid, beats and bars. A grid set to 1 has a cell size == beat, so
painting it is useless. */
- if (m_base->gridTool->getValue() > 1) {
+ if (m_base->gridTool->getValue() > 1)
+ {
fl_color(G_COLOR_GREY_3);
drawVerticals(m_base->gridTool->getCellSize());
}
Pixel coverWidth = m_base->fullWidth - m_base->loopWidth;
if (coverWidth != 0)
- fl_rectf(m_base->loopWidth+x(), y()+1, coverWidth, h()-2, G_COLOR_GREY_4);
+ fl_rectf(m_base->loopWidth + x(), y() + 1, coverWidth, h() - 2, G_COLOR_GREY_4);
}
-
/* -------------------------------------------------------------------------- */
-
void geBaseActionEditor::drawVerticals(int steps) const
{
/* Start drawing from steps, not from 0. The zero-th element is always
graphically useless. */
- for (Frame i=steps; i<m::clock::getFramesInLoop(); i+=steps) {
+ for (Frame i = steps; i < m::clock::getFramesInLoop(); i += steps)
+ {
Pixel p = m_base->frameToPixel(i) + x();
- fl_line(p, y()+1, p, y()+h()-2);
- }
+ fl_line(p, y() + 1, p, y() + h() - 2);
+ }
}
/* -------------------------------------------------------------------------- */
-
int geBaseActionEditor::handle(int e)
{
- switch (e) {
- case FL_PUSH:
- return push();
- case FL_DRAG:
- return drag();
- case FL_RELEASE:
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); // Make sure cursor returns normal
- return release();
- default:
- return Fl_Group::handle(e);
+ switch (e)
+ {
+ case FL_PUSH:
+ return push();
+ case FL_DRAG:
+ return drag();
+ case FL_RELEASE:
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); // Make sure cursor returns normal
+ return release();
+ default:
+ return Fl_Group::handle(e);
}
}
-
/* -------------------------------------------------------------------------- */
-
int geBaseActionEditor::push()
{
m_action = getActionAtCursor();
- if (Fl::event_button1()) { // Left button
- if (m_action == nullptr) { // No action under cursor: add a new one
+ if (Fl::event_button1())
+ { // Left button
+ if (m_action == nullptr)
+ { // No action under cursor: add a new one
if (Fl::event_x() < m_base->loopWidth) // Avoid click on grey area
onAddAction();
}
- else // Prepare for dragging
+ else // Prepare for dragging
m_action->pick = Fl::event_x() - m_action->x();
}
- else
- if (Fl::event_button3()) { // Right button
- if (m_action != nullptr) {
+ else if (Fl::event_button3())
+ { // Right button
+ if (m_action != nullptr)
+ {
onDeleteAction();
m_action = nullptr;
}
}
- return 1;
+ return 1;
}
-
/* -------------------------------------------------------------------------- */
-
int geBaseActionEditor::drag()
{
if (m_action == nullptr)
return 1;
}
-
/* -------------------------------------------------------------------------- */
-
int geBaseActionEditor::release()
{
int ret = 0;
- if (m_action != nullptr && m_action->altered) {
+ if (m_action != nullptr && m_action->altered)
+ {
onRefreshAction();
ret = 1;
}
m_action = nullptr;
return ret;
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BASE_ACTION_EDITOR_H
#define GE_BASE_ACTION_EDITOR_H
-
+#include "core/types.h"
#include <FL/Fl_Group.H>
-
-namespace giada {
-namespace v
+namespace giada::c::actionEditor
+{
+struct Data;
+}
+namespace giada::v
{
class gdBaseActionEditor;
class geBaseAction;
-
class geBaseActionEditor : public Fl_Group
{
public:
-
/* updateActions
Rebuild the actions widgets from scratch. */
-
+
virtual void rebuild(c::actionEditor::Data& d) = 0;
/* handle
Override base FL_Group events. */
-
+
int handle(int e) override;
/* getActionAtCursor
geBaseAction* getActionAtCursor() const;
-protected:
-
+ protected:
geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, gdBaseActionEditor*);
c::actionEditor::Data* m_data;
/* baseDraw
Draws basic things like borders and grids. Optional background clear. */
- void baseDraw(bool clear=true) const;
+ void baseDraw(bool clear = true) const;
virtual void onAddAction() = 0;
virtual void onDeleteAction() = 0;
virtual void onResizeAction() = 0;
virtual void onRefreshAction() = 0;
-private:
-
+ private:
/* drawVerticals
Draws generic vertical lines (beats, bars, grid lines...). */
-
+
void drawVerticals(int steps) const;
-
+
int push();
int drag();
int release();
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "utils/log.h"
-#include "utils/math.h"
-#include "core/const.h"
-#include "core/conf.h"
+#include "envelopeEditor.h"
#include "core/action.h"
+#include "core/conf.h"
+#include "core/const.h"
#include "core/recorder.h"
+#include "envelopePoint.h"
#include "glue/actionEditor.h"
#include "glue/channel.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
-#include "envelopePoint.h"
-#include "envelopeEditor.h"
-
+#include "utils/log.h"
+#include "utils/math.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor* b)
copy_label(l);
}
-
/* -------------------------------------------------------------------------- */
-
geEnvelopeEditor::~geEnvelopeEditor()
{
m::conf::conf.envelopeEditorH = h();
}
-
/* -------------------------------------------------------------------------- */
-
-void geEnvelopeEditor::draw()
+void geEnvelopeEditor::draw()
{
baseDraw();
fl_color(G_COLOR_GREY_4);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_draw(label(), x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT));
+ fl_draw(label(), x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT));
if (children() == 0)
return;
- reposition it on the y axis, only if there's no point selected (dragged
around). */
- for (int i=0; i<children(); i++) {
+ for (int i = 0; i < children(); i++)
+ {
geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
if (m_action == nullptr)
p->position(p->x(), valueToY(p->a1.event.getVelocity()));
- if (i > 0) {
+ if (i > 0)
+ {
x2 = p->x() + side;
y2 = p->y() + side;
fl_line(x1, y1, x2, y2);
draw_children();
}
-
/* -------------------------------------------------------------------------- */
-
void geEnvelopeEditor::rebuild(c::actionEditor::Data& d)
{
m_data = &d;
clear();
size(m_base->fullWidth, h());
- for (const m::Action& a : m_data->actions) {
+ for (const m::Action& a : m_data->actions)
+ {
if (a.event.getStatus() != m::MidiEvent::ENVELOPE)
continue;
- add(new geEnvelopePoint(frameToX(a.frame), valueToY(a.event.getVelocity()), a));
+ add(new geEnvelopePoint(frameToX(a.frame), valueToY(a.event.getVelocity()), a));
}
resizable(nullptr);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
bool geEnvelopeEditor::isFirstPoint() const
{
return find(m_action) == 0;
}
-
bool geEnvelopeEditor::isLastPoint() const
{
return find(m_action) == children() - 1;
}
-
/* -------------------------------------------------------------------------- */
-
Pixel geEnvelopeEditor::frameToX(Frame frame) const
{
return x() + m_base->frameToPixel(frame) - (geEnvelopePoint::SIDE / 2);
}
-
Pixel geEnvelopeEditor::valueToY(int value) const
{
return u::math::map<int, Pixel>(value, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y());
}
-
int geEnvelopeEditor::yToValue(Pixel pixel, Pixel offset) const
{
- return u::math::map<Pixel, int>(pixel, h() - offset, 0, 0, G_MAX_VELOCITY);
+ return u::math::map<Pixel, int>(pixel, h() - offset, 0, 0, G_MAX_VELOCITY);
}
-
/* -------------------------------------------------------------------------- */
-
-void geEnvelopeEditor::onAddAction()
+void geEnvelopeEditor::onAddAction()
{
Frame f = m_base->pixelToFrame(Fl::event_x() - x());
int v = yToValue(Fl::event_y() - y());
-
+
c::actionEditor::recordEnvelopeAction(m_data->channelId, f, v);
-
+
m_base->rebuild(); // TODO - USELESS
}
-
/* -------------------------------------------------------------------------- */
-
-void geEnvelopeEditor::onDeleteAction()
+void geEnvelopeEditor::onDeleteAction()
{
c::actionEditor::deleteEnvelopeAction(m_data->channelId, m_action->a1);
-
+
m_base->rebuild(); // TODO - USELESS
}
-
/* -------------------------------------------------------------------------- */
-
-void geEnvelopeEditor::onMoveAction()
+void geEnvelopeEditor::onMoveAction()
{
Pixel side = geEnvelopePoint::SIDE / 2;
Pixel ex = Fl::event_x() - side;
Pixel y2 = y() + h() - geEnvelopePoint::SIDE;
/* x-axis constraints. */
- if (isFirstPoint() || ex < x1) ex = x1;
- else if (isLastPoint() || ex > x2) ex = x2;
+ if (isFirstPoint() || ex < x1)
+ ex = x1;
+ else if (isLastPoint() || ex > x2)
+ ex = x2;
/* y-axis constraints. */
- if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
+ if (ey < y1)
+ ey = y1;
+ else if (ey > y2)
+ ey = y2;
m_action->position(ex, ey);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
-void geEnvelopeEditor::onRefreshAction()
+void geEnvelopeEditor::onRefreshAction()
{
Frame f = m_base->pixelToFrame((m_action->x() - x()) + geEnvelopePoint::SIDE / 2);
float v = yToValue(m_action->y() - y(), geEnvelopePoint::SIDE);
m_base->rebuild();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_ENVELOPE_EDITOR_H
#define GE_ENVELOPE_EDITOR_H
-
#include "baseActionEditor.h"
-
-namespace giada {
+namespace giada
+{
namespace m
{
class SampleChannel;
class geEnvelopeEditor : public geBaseActionEditor
{
public:
-
geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor*);
~geEnvelopeEditor();
void rebuild(c::actionEditor::Data& d) override;
-private:
-
- void onAddAction() override;
- void onDeleteAction() override;
- void onMoveAction() override;
- void onResizeAction() override{}; // Nothing to do here
+ private:
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override{}; // Nothing to do here
void onRefreshAction() override;
Pixel frameToX(Frame frame) const;
Pixel valueToY(int value) const;
- int yToValue(Pixel pixel, Pixel offset=0) const;
+ int yToValue(Pixel pixel, Pixel offset = 0) const;
bool isFirstPoint() const;
- bool isLastPoint() const;
+ bool isLastPoint() const;
};
-}} // giada::v::
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
#include "envelopePoint.h"
+#include "core/const.h"
+#include <FL/fl_draw.H>
-
-namespace giada {
+namespace giada
+{
namespace v
{
geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, m::Action a)
- : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, {})
+: geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, {})
{
}
-
/* -------------------------------------------------------------------------- */
-
void geEnvelopePoint::draw()
{
fl_rectf(x(), y(), w(), h(), hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1);
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_ENVELOPE_POINT_H
#define GE_ENVELOPE_POINT_H
-
-#include "core/recorder.h"
#include "baseAction.h"
+#include "core/recorder.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geEnvelopePoint : public geBaseAction
{
public:
-
static const Pixel SIDE = 12;
geEnvelopePoint(Pixel x, Pixel y, m::Action a);
void draw() override;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* ------------------------------------------------------------------------------
*
-* Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* --------------------------------------------------------------------------- */
-
-#include <FL/Fl_Double_Window.H>
-#include "core/conf.h"
+#include "gridTool.h"
#include "core/clock.h"
-#include "utils/math.h"
-#include "gui/elems/basics/choice.h"
+#include "core/conf.h"
#include "gui/elems/basics/check.h"
-#include "gridTool.h"
-
+#include "gui/elems/basics/choice.h"
+#include "utils/math.h"
+#include <FL/Fl_Double_Window.H>
-namespace giada {
-namespace v
+namespace giada::v
{
geGridTool::geGridTool(Pixel x, Pixel y)
-: Fl_Group(x, y, 80, 20)
+: Fl_Group(x, y, 80, 20)
{
gridType = new geChoice(x, y, 40, 20);
gridType->add("1");
active->value(m::conf::conf.actionEditorGridOn);
end();
-}
+ gridType->copy_tooltip("Grid resolution");
+ active->copy_tooltip("Snap to grid");
+}
/* -------------------------------------------------------------------------- */
-
geGridTool::~geGridTool()
{
m::conf::conf.actionEditorGridVal = gridType->value();
m::conf::conf.actionEditorGridOn = active->value();
}
-
/* -------------------------------------------------------------------------- */
-
void geGridTool::cb_changeType(Fl_Widget* /*w*/, void* p) { ((geGridTool*)p)->cb_changeType(); }
-
/* -------------------------------------------------------------------------- */
-
void geGridTool::cb_changeType()
{
window()->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
bool geGridTool::isOn() const
{
return active->value();
}
-
/* -------------------------------------------------------------------------- */
-
int geGridTool::getValue() const
{
- switch (gridType->value()) {
- case 0: return 1;
- case 1: return 2;
- case 2: return 3;
- case 3: return 4;
- case 4: return 6;
- case 5: return 8;
- case 6: return 16;
- case 7: return 32;
+ switch (gridType->value())
+ {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 3;
+ case 3:
+ return 4;
+ case 4:
+ return 6;
+ case 5:
+ return 8;
+ case 6:
+ return 16;
+ case 7:
+ return 32;
}
return 0;
}
-
/* -------------------------------------------------------------------------- */
-
Frame geGridTool::getSnapFrame(Frame v) const
{
if (!isOn())
return u::math::quantize(v, getCellSize());
}
-
/* -------------------------------------------------------------------------- */
-
Frame geGridTool::getCellSize() const
{
return m::clock::getFramesInBeat() / getValue();
}
-}} // giada::v::
\ No newline at end of file
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_GRID_TOOL_H
#define GE_GRID_TOOL_H
-
-#include <FL/Fl_Group.H>
#include "core/types.h"
-
+#include <FL/Fl_Group.H>
class geCheck;
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geChoice;
class geGridTool : public Fl_Group
{
public:
-
geGridTool(Pixel x, Pixel y);
~geGridTool();
- int getValue() const;
+ int getValue() const;
bool isOn() const;
Frame getSnapFrame(Frame f) const;
Frame getCellSize() const;
-private:
-
- geChoice* gridType;
+ private:
+ geChoice* gridType;
geCheck* active;
static void cb_changeType(Fl_Widget* /*w*/, void* p);
- void cb_changeType();
+ void cb_changeType();
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include "core/const.h"
+#include "noteEditor.h"
#include "core/conf.h"
+#include "core/const.h"
#include "gui/dialogs/actionEditor/midiActionEditor.h"
#include "pianoRoll.h"
-#include "noteEditor.h"
-
+#include <FL/Fl.H>
-namespace giada {
+namespace giada
+{
namespace v
{
geNoteEditor::geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base)
: geScroll(x, y, 200, 422)
-, m_base (base)
+, m_base(base)
{
end();
-
+
type(Fl_Scroll::VERTICAL_ALWAYS);
size(m_base->fullWidth, m::conf::conf.pianoRollH);
add(pianoRoll);
}
-
/* -------------------------------------------------------------------------- */
-
geNoteEditor::~geNoteEditor()
{
m::conf::conf.pianoRollH = h();
m::conf::conf.pianoRollY = pianoRoll->y();
}
-
/* -------------------------------------------------------------------------- */
-
void geNoteEditor::scroll()
{
Pixel ey = Fl::event_y() - pianoRoll->pick;
Pixel y1 = y();
Pixel y2 = (y() + h()) - pianoRoll->h();
- if (ey > y1) ey = y1; else if (ey < y2) ey = y2;
+ if (ey > y1)
+ ey = y1;
+ else if (ey < y2)
+ ey = y2;
pianoRoll->position(x(), ey);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geNoteEditor::rebuild(c::actionEditor::Data& d)
{
size(m_base->fullWidth, h());
pianoRoll->rebuild(d);
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_NOTE_EDITOR_H
#define GE_NOTE_EDITOR_H
-
+#include "core/types.h"
#include "gui/elems/basics/scroll.h"
-
-namespace giada {
-namespace v
+namespace giada::c::actionEditor
+{
+struct Data;
+}
+namespace giada::v
{
class gdMidiActionEditor;
class gePianoRoll;
class geNoteEditor : public geScroll
{
public:
-
geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base);
~geNoteEditor();
gePianoRoll* pianoRoll;
-private:
-
- gdMidiActionEditor* m_base;
+ private:
+ gdMidiActionEditor* m_base;
};
-}} // giada::v::
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
+#include "pianoItem.h"
#include "core/action.h"
+#include "core/const.h"
#include "core/midiEvent.h"
#include "utils/math.h"
-#include "pianoItem.h"
-
+#include <FL/fl_draw.H>
-namespace giada {
+namespace giada
+{
namespace v
{
gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, m::Action a1,
- m::Action a2)
-: geBaseAction(X, Y, W, H, /*resizable=*/true, a1, a2),
- m_ringLoop (a2.isValid() && a1.frame > a2.frame),
- m_orphaned (!a2.isValid())
+ m::Action a2)
+: geBaseAction(X, Y, W, H, /*resizable=*/true, a1, a2)
+, m_ringLoop(a2.isValid() && a1.frame > a2.frame)
+, m_orphaned(!a2.isValid())
{
m_resizable = isResizable();
}
-
/* -------------------------------------------------------------------------- */
-
bool gePianoItem::isResizable() const
{
return !(m_ringLoop || m_orphaned);
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoItem::draw()
{
Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1;
Pixel by = y() + 2;
Pixel bh = h() - 3;
- if (m_orphaned) {
+ if (m_orphaned)
+ {
fl_rect(x(), by, w(), bh, color);
fl_line(x(), by, x() + w(), by + bh);
}
- else {
+ else
+ {
Pixel vh = calcVelocityH();
- if (m_ringLoop) {
+ if (m_ringLoop)
+ {
fl_rect(x(), by, MIN_WIDTH, bh, color);
- fl_line(x() + MIN_WIDTH, by + bh/2, x() + w(), by + bh/2);
+ fl_line(x() + MIN_WIDTH, by + bh / 2, x() + w(), by + bh / 2);
fl_rectf(x(), by + (bh - vh), MIN_WIDTH, vh, color);
}
- else {
+ else
+ {
fl_rect(x(), by, w(), bh, color);
fl_rectf(x(), by + (bh - vh), w(), vh, color);
}
}
}
-
/* -------------------------------------------------------------------------- */
-
Pixel gePianoItem::calcVelocityH() const
{
int v = a1.event.getVelocity();
return u::math::map<int, Pixel>(v, 0, G_MAX_VELOCITY, 0, h() - 3);
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PIANO_ITEM_H
#define GE_PIANO_ITEM_H
-
#include "baseAction.h"
-
-namespace giada {
-namespace m
+namespace giada
+{
+namespace m
{
struct Action;
}
class gePianoItem : public geBaseAction
{
public:
-
gePianoItem(int x, int y, int w, int h, m::Action a1, m::Action a2);
-
+
void draw() override;
bool isResizable() const;
-private:
+ private:
+ bool m_ringLoop;
+ bool m_orphaned;
- bool m_ringLoop;
- bool m_orphaned;
-
- Pixel calcVelocityH() const;
+ Pixel calcVelocityH() const;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
+#include "pianoRoll.h"
+#include "core/action.h"
+#include "core/clock.h"
#include "core/conf.h"
#include "core/const.h"
-#include "core/clock.h"
-#include "core/action.h"
#include "core/midiEvent.h"
-#include "utils/log.h"
-#include "utils/string.h"
-#include "utils/math.h"
-#include "glue/channel.h"
#include "glue/actionEditor.h"
+#include "glue/channel.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
-#include "pianoItem.h"
#include "noteEditor.h"
-#include "pianoRoll.h"
-
+#include "pianoItem.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, gdBaseActionEditor* b)
: geBaseActionEditor(X, Y, W, 40, b)
-, pick (0)
+, pick(0)
{
- position(x(), m::conf::conf.pianoRollY == -1 ? y()-(h()/2) : m::conf::conf.pianoRollY);
+ position(x(), m::conf::conf.pianoRollY == -1 ? y() - (h() / 2) : m::conf::conf.pianoRollY);
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::drawSurface1()
{
surface1 = fl_create_offscreen(CELL_W, h());
int octave = MAX_OCTAVES;
- for (int i = 1; i <= MAX_KEYS+1; i++) {
+ for (int i = 1; i <= MAX_KEYS + 1; i++)
+ {
/* print key note label. C C# D D# E F F# G G# A A# B */
std::string note = u::string::iToString(octave);
- switch (i % KEYS) {
- case (int) Notes::G:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- note += " G";
- break;
- case (int) Notes::FS:
- note += " F#";
- break;
- case (int) Notes::F:
- note += " F";
- break;
- case (int) Notes::E:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- note += " E";
- break;
- case (int) Notes::DS:
- note += " D#";
- break;
- case (int) Notes::D:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- note += " D";
- break;
- case (int) Notes::CS:
- note += " C#";
- break;
- case (int) Notes::C:
- note += " C";
- octave--;
- break;
- case (int) Notes::B:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- note += " B";
- break;
- case (int) Notes::AS:
- note += " A#";
- break;
- case (int) Notes::A:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- note += " A";
- break;
- case (int) Notes::GS:
- note += " G#";
- break;
+ switch (i % KEYS)
+ {
+ case (int)Notes::G:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ note += " G";
+ break;
+ case (int)Notes::FS:
+ note += " F#";
+ break;
+ case (int)Notes::F:
+ note += " F";
+ break;
+ case (int)Notes::E:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ note += " E";
+ break;
+ case (int)Notes::DS:
+ note += " D#";
+ break;
+ case (int)Notes::D:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ note += " D";
+ break;
+ case (int)Notes::CS:
+ note += " C#";
+ break;
+ case (int)Notes::C:
+ note += " C";
+ octave--;
+ break;
+ case (int)Notes::B:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ note += " B";
+ break;
+ case (int)Notes::AS:
+ note += " A#";
+ break;
+ case (int)Notes::A:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ note += " A";
+ break;
+ case (int)Notes::GS:
+ note += " G#";
+ break;
}
/* Print note name */
fl_color(G_COLOR_GREY_3);
- fl_draw(note.c_str(), 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
- (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ fl_draw(note.c_str(), 4, ((i - 1) * CELL_H) + 1, CELL_W, CELL_H,
+ (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CENTER));
/* Print horizontal line */
- if (i < MAX_KEYS+1)
- fl_line(0, i*CELL_H, CELL_W, +i*CELL_H);
+ if (i < MAX_KEYS + 1)
+ fl_line(0, i * CELL_H, CELL_W, +i * CELL_H);
}
fl_line_style(0);
fl_end_offscreen();
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::drawSurface2()
{
surface2 = fl_create_offscreen(CELL_W, h());
fl_color(G_COLOR_GREY_3);
fl_line_style(FL_DASH, 0, nullptr);
- for (int i=1; i<=MAX_KEYS+1; i++) {
- switch (i % KEYS) {
- case (int) Notes::G:
- case (int) Notes::E:
- case (int) Notes::D:
- case (int) Notes::B:
- case (int) Notes::A:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- break;
+ for (int i = 1; i <= MAX_KEYS + 1; i++)
+ {
+ switch (i % KEYS)
+ {
+ case (int)Notes::G:
+ case (int)Notes::E:
+ case (int)Notes::D:
+ case (int)Notes::B:
+ case (int)Notes::A:
+ fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
+ break;
}
- if (i < MAX_KEYS+1) {
+ if (i < MAX_KEYS + 1)
+ {
fl_color(G_COLOR_GREY_3);
- fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
+ fl_line(0, i * CELL_H, CELL_W, i * CELL_H);
}
}
fl_end_offscreen();
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::draw()
{
fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
// TODO - is this APPLE thing still useful?
#if defined(__APPLE__)
for (Pixel i = 36; i < m_base->fullWidth; i += 36) /// TODO: i < m_base->loopWidth is faster
- fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
+ fl_copy_offscreen(x() + i, y(), CELL_W, h(), surface2, 1, 0);
#else
for (Pixel i = CELL_W; i < m_base->loopWidth; i += CELL_W)
- fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
+ fl_copy_offscreen(x() + i, y(), CELL_W, h(), surface2, 0, 0);
#endif
baseDraw(false);
draw_children();
}
-
/* -------------------------------------------------------------------------- */
-
int gePianoRoll::handle(int e)
{
- if (e == FL_PUSH && Fl::event_button3()) {
+ if (e == FL_PUSH && Fl::event_button3())
+ {
pick = Fl::event_y() - y();
return geBaseActionEditor::handle(e);
}
- if (e == FL_DRAG && Fl::event_button3()) {
+ if (e == FL_DRAG && Fl::event_button3())
+ {
static_cast<geNoteEditor*>(parent())->scroll();
return 1;
}
return geBaseActionEditor::handle(e);
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::onAddAction()
{
Frame frame = m_base->pixelToFrame(Fl::event_x() - x());
int note = yToNote(Fl::event_y() - y());
- c::actionEditor::recordMidiAction(m_data->channelId, note, G_MAX_VELOCITY,
- frame);
+ c::actionEditor::recordMidiAction(m_data->channelId, note, G_MAX_VELOCITY,
+ frame);
- m_base->rebuild(); // Rebuild velocityEditor as well
+ m_base->rebuild(); // Rebuild velocityEditor as well
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::onDeleteAction()
{
- c::actionEditor::deleteMidiAction(m_data->channelId, m_action->a1);
-
- m_base->rebuild(); // Rebuild velocityEditor as well
-}
+ c::actionEditor::deleteMidiAction(m_data->channelId, m_action->a1);
+ m_base->rebuild(); // Rebuild velocityEditor as well
+}
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::onMoveAction()
{
/* Y computation: - (CELL_H/2) is wrong: we should need the y pick value as
done with x. Let's change this when vertical piano zoom will be available. */
Pixel ex = Fl::event_x() - m_action->pick;
- Pixel ey = snapToY(Fl::event_y() - y() - (CELL_H/2)) + y();
+ Pixel ey = snapToY(Fl::event_y() - y() - (CELL_H / 2)) + y();
Pixel x1 = x();
Pixel x2 = (m_base->loopWidth + x()) - m_action->w();
Pixel y1 = y();
Pixel y2 = y() + h();
- if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
- if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
+ if (ex < x1)
+ ex = x1;
+ else if (ex > x2)
+ ex = x2;
+ if (ey < y1)
+ ey = y1;
+ else if (ey > y2)
+ ey = y2;
m_action->position(ex, ey);
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::onResizeAction()
{
if (!static_cast<gePianoItem*>(m_action)->isResizable())
Pixel x1 = x();
Pixel x2 = m_base->loopWidth + x();
-
- if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
- if (m_action->onRightEdge)
+ if (ex < x1)
+ ex = x1;
+ else if (ex > x2)
+ ex = x2;
+
+ if (m_action->onRightEdge)
m_action->setRightEdge(ex - m_action->x());
else
m_action->setLeftEdge(ex);
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::onRefreshAction()
{
namespace ca = c::actionEditor;
Frame f1 = 0;
Frame f2 = 0;
- if (!m_action->isOnEdges()) {
+ if (!m_action->isOnEdges())
+ {
f1 = m_base->pixelToFrame(p1);
f2 = m_base->pixelToFrame(p2, /*snap=*/false) - (m_base->pixelToFrame(p1, /*snap=*/false) - f1);
- }
- else if (m_action->onLeftEdge) {
+ }
+ else if (m_action->onLeftEdge)
+ {
f1 = m_base->pixelToFrame(p1);
f2 = m_action->a2.frame;
if (f1 == f2) // If snapping makes an action fall onto the other
f1 -= G_DEFAULT_ACTION_SIZE;
}
- else if (m_action->onRightEdge) {
+ else if (m_action->onRightEdge)
+ {
f1 = m_action->a1.frame;
f2 = m_base->pixelToFrame(p2);
if (f1 == f2) // If snapping makes an action fall onto the other
ca::updateMidiAction(m_data->channelId, m_action->a1, note, velocity, f1, f2);
- m_base->rebuild(); // Rebuild velocityEditor as well
+ m_base->rebuild(); // Rebuild velocityEditor as well
}
-
/* -------------------------------------------------------------------------- */
-
int gePianoRoll::yToNote(Pixel p) const
{
return gePianoRoll::MAX_KEYS - (p / gePianoRoll::CELL_H);
}
-
Pixel gePianoRoll::noteToY(int n) const
{
return (MAX_KEYS * CELL_H) - (n * gePianoRoll::CELL_H);
}
-
Pixel gePianoRoll::snapToY(Pixel p) const
{
return u::math::quantize(p, CELL_H);
}
-
Pixel gePianoRoll::getPianoItemW(Pixel px, const m::Action& a1, const m::Action& a2) const
{
- if (a2.isValid()) { // Regular
- if (a1.frame > a2.frame) // Ring-loop
+ if (a2.isValid())
+ { // Regular
+ if (a1.frame > a2.frame) // Ring-loop
return m_base->loopWidth - (px - x());
return m_base->frameToPixel(a2.frame - a1.frame);
}
- return geBaseAction::MIN_WIDTH; // Orphaned
+ return geBaseAction::MIN_WIDTH; // Orphaned
}
-
/* -------------------------------------------------------------------------- */
-
void gePianoRoll::rebuild(c::actionEditor::Data& d)
{
m_data = &d;
if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
continue;
- assert(a1.isValid()); // a2 might be null if orphaned
+ assert(a1.isValid()); // a2 might be null if orphaned
const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{};
redraw();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PIANO_ROLL_H
#define GE_PIANO_ROLL_H
-
-#include <FL/fl_draw.H>
#include "baseActionEditor.h"
+#include <FL/fl_draw.H>
-
-namespace giada {
-namespace m
+namespace giada::m
{
class MidiChannel;
-}
-namespace v
+class Action;
+} // namespace giada::m
+namespace giada::v
{
class gePianoRoll : public geBaseActionEditor
{
public:
-
- static const int MAX_KEYS = 127;
- static const int MAX_OCTAVES = 9;
- static const int KEYS = 12;
- static const Pixel CELL_H = 20;
- static const Pixel CELL_W = 40;
+ static const int MAX_KEYS = 127;
+ static const int MAX_OCTAVES = 9;
+ static const int KEYS = 12;
+ static const Pixel CELL_H = 20;
+ static const Pixel CELL_W = 40;
gePianoRoll(Pixel x, Pixel y, Pixel w, gdBaseActionEditor*);
Pixel pick;
-private:
-
+ private:
enum class Notes
{
- G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10,
- A = 11, GS = 0
+ G = 1,
+ FS = 2,
+ F = 3,
+ E = 4,
+ DS = 5,
+ D = 6,
+ CS = 7,
+ C = 8,
+ B = 9,
+ AS = 10,
+ A = 11,
+ GS = 0
};
- Fl_Offscreen surface1; // notes, no repeat
- Fl_Offscreen surface2; // lines, x-repeat
+ Fl_Offscreen surface1; // notes, no repeat
+ Fl_Offscreen surface2; // lines, x-repeat
- void onAddAction() override;
- void onDeleteAction() override;
- void onMoveAction() override;
- void onResizeAction() override;
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override;
void onRefreshAction() override;
/* drawSurface*
Pixel noteToY(int n) const;
Pixel getPianoItemW(Pixel x, const m::Action& a1, const m::Action& a2) const;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
-#include "core/action.h"
#include "sampleAction.h"
+#include "core/action.h"
+#include "core/const.h"
+#include <FL/fl_draw.H>
-
-namespace giada {
+namespace giada
+{
namespace v
{
-geSampleAction::geSampleAction(Pixel X, Pixel Y, Pixel W, Pixel H,
- bool singlePress, m::Action a1, m::Action a2)
-: geBaseAction (X, Y, W, H, singlePress, a1, a2),
- m_singlePress(singlePress)
+geSampleAction::geSampleAction(Pixel X, Pixel Y, Pixel W, Pixel H,
+ bool singlePress, m::Action a1, m::Action a2)
+: geBaseAction(X, Y, W, H, singlePress, a1, a2)
+, m_singlePress(singlePress)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleAction::draw()
{
- Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1;
+ Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1;
- if (m_singlePress) {
+ if (m_singlePress)
+ {
fl_rectf(x(), y(), w(), h(), color);
}
- else {
+ else
+ {
if (a1.event.getStatus() == m::MidiEvent::NOTE_KILL)
fl_rect(x(), y(), MIN_WIDTH, h(), color);
- else {
+ else
+ {
fl_rectf(x(), y(), MIN_WIDTH, h(), color);
if (a1.event.getStatus() == m::MidiEvent::NOTE_ON)
- fl_rectf(x()+3, y()+h()-11, w()-6, 8, G_COLOR_GREY_4);
- else
- if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
- fl_rectf(x()+3, y()+3, w()-6, 8, G_COLOR_GREY_4);
+ fl_rectf(x() + 3, y() + h() - 11, w() - 6, 8, G_COLOR_GREY_4);
+ else if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
+ fl_rectf(x() + 3, y() + 3, w() - 6, 8, G_COLOR_GREY_4);
}
}
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SAMPLE_ACTION_H
#define GE_SAMPLE_ACTION_H
-
-#include "core/recorder.h"
#include "baseAction.h"
+#include "core/recorder.h"
-
-namespace giada {
+namespace giada
+{
namespace m
{
class SampleChannel;
class geSampleAction : public geBaseAction
{
public:
-
- geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, bool singlePress,
- m::Action a1, m::Action a2);
+ geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, bool singlePress,
+ m::Action a1, m::Action a2);
void draw() override;
-private:
-
+ private:
bool m_singlePress;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "core/recorder.h"
-#include "core/const.h"
-#include "core/conf.h"
+#include "sampleActionEditor.h"
#include "core/action.h"
-#include "utils/log.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/recorder.h"
#include "glue/actionEditor.h"
#include "glue/channel.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
#include "sampleAction.h"
-#include "sampleActionEditor.h"
-
+#include "utils/log.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor* b)
{
}
-
/* -------------------------------------------------------------------------- */
-
geSampleActionEditor::~geSampleActionEditor()
{
m::conf::conf.sampleActionEditorH = h();
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleActionEditor::rebuild(c::actionEditor::Data& d)
{
m_data = &d;
clear();
size(m_base->fullWidth, h());
-
- for (const m::Action& a1 : m_data->actions) {
+
+ for (const m::Action& a1 : m_data->actions)
+ {
if (a1.event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1))
continue;
- const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{};
+ const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{};
Pixel px = x() + m_base->frameToPixel(a1.frame);
Pixel py = y() + 4;
/* If channel is LOOP_ANY, deactivate it: a loop mode channel cannot hold
keypress/keyrelease actions. */
-
+
isAnyLoopMode ? deactivate() : activate();
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleActionEditor::draw()
{
/* Draw basic boundaries (+ beat bars) and hide the unused area. Then draw
fl_color(G_COLOR_GREY_4);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
if (active())
- fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ fl_draw("start/stop", x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CENTER));
else
- fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ fl_draw("start/stop (disabled)", x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CENTER));
draw_children();
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleActionEditor::onAddAction()
+void geSampleActionEditor::onAddAction()
{
Frame f = m_base->pixelToFrame(Fl::event_x() - x());
c::actionEditor::recordSampleAction(m_data->channelId, m_base->getActionType(), f);
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleActionEditor::onDeleteAction()
+void geSampleActionEditor::onDeleteAction()
{
c::actionEditor::deleteSampleAction(m_data->channelId, m_action->a1);
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleActionEditor::onMoveAction()
+void geSampleActionEditor::onMoveAction()
{
Pixel ex = Fl::event_x() - m_action->pick;
Pixel x1 = x();
Pixel x2 = m_base->loopWidth + x() - m_action->w();
- if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+ if (ex < x1)
+ ex = x1;
+ else if (ex > x2)
+ ex = x2;
m_action->setPosition(ex);
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleActionEditor::onResizeAction()
+void geSampleActionEditor::onResizeAction()
{
Pixel ex = Fl::event_x();
Pixel x1 = x();
Pixel x2 = m_base->loopWidth + x();
- if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+ if (ex < x1)
+ ex = x1;
+ else if (ex > x2)
+ ex = x2;
- if (m_action->onRightEdge)
+ if (m_action->onRightEdge)
m_action->setRightEdge(ex - m_action->x());
else
m_action->setLeftEdge(ex);
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleActionEditor::onRefreshAction()
+void geSampleActionEditor::onRefreshAction()
{
namespace ca = c::actionEditor;
Frame f2 = 0;
int type = m_action->a1.event.getStatus();
- if (!m_action->isOnEdges()) {
+ if (!m_action->isOnEdges())
+ {
f1 = m_base->pixelToFrame(p1);
f2 = m_base->pixelToFrame(p2, /*snap=*/false) - (m_base->pixelToFrame(p1, /*snap=*/false) - f1);
- }
- else if (m_action->onLeftEdge) {
+ }
+ else if (m_action->onLeftEdge)
+ {
f1 = m_base->pixelToFrame(p1);
f2 = m_action->a2.frame;
}
- else if (m_action->onRightEdge) {
+ else if (m_action->onRightEdge)
+ {
f1 = m_action->a1.frame;
f2 = m_base->pixelToFrame(p2);
}
ca::updateSampleAction(m_data->channelId, m_action->a1, type, f1, f2);
-
+
m_base->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
bool geSampleActionEditor::isNoteOffSinglePress(const m::Action& a)
{
- return m_data->sample->channelMode == SamplePlayerMode::SINGLE_PRESS &&
+ return m_data->sample->channelMode == SamplePlayerMode::SINGLE_PRESS &&
a.event.getStatus() == m::MidiEvent::NOTE_OFF;
-
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SAMPLE_ACTION_EDITOR_H
#define GE_SAMPLE_ACTION_EDITOR_H
-
#include "baseActionEditor.h"
-
-namespace giada {
+namespace giada
+{
namespace m
{
class SampleChannel;
struct Action;
-}
+} // namespace m
namespace v
{
class geSampleAction;
class geSampleActionEditor : public geBaseActionEditor
{
public:
-
geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor*);
~geSampleActionEditor();
void rebuild(c::actionEditor::Data& d) override;
-private:
-
- void onAddAction() override;
- void onDeleteAction() override;
- void onMoveAction() override;
- void onResizeAction() override;
+ private:
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override;
void onRefreshAction() override;
- bool isNoteOffSinglePress(const m::Action& a);
+ bool isNoteOffSinglePress(const m::Action& a);
};
-}} // giada::v::
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "utils/log.h"
-#include "utils/math.h"
-#include "core/const.h"
-#include "core/conf.h"
+#include "velocityEditor.h"
#include "core/action.h"
#include "core/clock.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "envelopePoint.h"
#include "glue/actionEditor.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
-#include "envelopePoint.h"
-#include "velocityEditor.h"
-
+#include "utils/log.h"
+#include "utils/math.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor* b)
-: geBaseActionEditor(x, y, 200, m::conf::conf.velocityEditorH, b)
+: geBaseActionEditor(x, y, 200, m::conf::conf.velocityEditorH, b)
{
}
-
/* -------------------------------------------------------------------------- */
-
geVelocityEditor::~geVelocityEditor()
{
m::conf::conf.velocityEditorH = h();
}
-
/* -------------------------------------------------------------------------- */
-
-void geVelocityEditor::draw()
+void geVelocityEditor::draw()
{
baseDraw();
fl_color(G_COLOR_GREY_4);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_draw("Velocity", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT));
+ fl_draw("Velocity", x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT));
if (children() == 0)
return;
Pixel side = geEnvelopePoint::SIDE / 2;
- for (int i=0; i<children(); i++) {
+ for (int i = 0; i < children(); i++)
+ {
geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
if (m_action == nullptr)
p->position(p->x(), valueToY(p->a1.event.getVelocity()));
draw_children();
}
-
/* -------------------------------------------------------------------------- */
-
Pixel geVelocityEditor::valueToY(int v) const
{
/* Cast the input type of 'v' to float, to make the mapping more precise. */
return u::math::map<float, Pixel>(v, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y());
}
-
int geVelocityEditor::yToValue(Pixel px) const
{
- return u::math::map<Pixel, int>(px, h() - geEnvelopePoint::SIDE, 0, 0, G_MAX_VELOCITY);
+ return u::math::map<Pixel, int>(px, h() - geEnvelopePoint::SIDE, 0, 0, G_MAX_VELOCITY);
}
-
/* -------------------------------------------------------------------------- */
-
void geVelocityEditor::rebuild(c::actionEditor::Data& d)
{
m_data = &d;
clear();
size(m_base->fullWidth, h());
- for (const m::Action& action : m_data->actions) {
-
+ for (const m::Action& action : m_data->actions)
+ {
+
if (action.event.getStatus() == m::MidiEvent::NOTE_OFF)
continue;
add(new geEnvelopePoint(px, py, action));
}
-
+
resizable(nullptr);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
-void geVelocityEditor::onMoveAction()
+void geVelocityEditor::onMoveAction()
{
Pixel ey = Fl::event_y() - (geEnvelopePoint::SIDE / 2);
Pixel y1 = y();
Pixel y2 = y() + h() - geEnvelopePoint::SIDE;
- if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
+ if (ey < y1)
+ ey = y1;
+ else if (ey > y2)
+ ey = y2;
m_action->position(m_action->x(), ey);
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
-void geVelocityEditor::onRefreshAction()
+void geVelocityEditor::onRefreshAction()
{
c::actionEditor::updateVelocity(m_action->a1, yToValue(m_action->y() - y()));
- m_base->rebuild(); // Rebuild pianoRoll as well
+ m_base->rebuild(); // Rebuild pianoRoll as well
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_VELOCITY_EDITOR_H
#define GE_VELOCITY_EDITOR_H
-
#include "baseActionEditor.h"
-
-namespace giada {
+namespace giada
+{
namespace m
{
class MidiChannel;
{
class geEnvelopePoint;
-
class geVelocityEditor : public geBaseActionEditor
{
public:
-
geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor*);
~geVelocityEditor();
void rebuild(c::actionEditor::Data& d) override;
-private:
-
- void onMoveAction() override;
+ private:
+ void onMoveAction() override;
void onRefreshAction() override;
- void onAddAction() override {};
- void onDeleteAction() override {};
- void onResizeAction() override {};
+ void onAddAction() override{};
+ void onDeleteAction() override{};
+ void onResizeAction() override{};
- Pixel valueToY(int v) const;
+ Pixel valueToY(int v) const;
int yToValue(Pixel y) const;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
+#include "box.h"
#include "core/const.h"
#include "utils/gui.h"
-#include "box.h"
-
+#include <FL/fl_draw.H>
geBox::geBox(int x, int y, int w, int h, const char* l, Fl_Align al)
-: Fl_Box (x, y, w, h)
+: Fl_Box(x, y, w, h)
{
copy_label(l);
box(FL_NO_BOX);
align(al | FL_ALIGN_INSIDE);
}
-
/* -------------------------------------------------------------------------- */
-
void geBox::draw()
{
fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1); // Clear background
fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // Border
if (image() != nullptr)
- draw_label(); // draw_label also paints image, if any
- else
- if (label() != nullptr) {
+ draw_label(); // draw_label also paints image, if any
+ else if (label() != nullptr)
+ {
fl_color(G_COLOR_LIGHT_2);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_draw(giada::u::gui::truncate(label(), w()-8).c_str(), x()+4, y(), w()-4, h(), align());
+ fl_draw(giada::u::gui::truncate(label(), w() - 8).c_str(), x() + 4, y(), w() - 4, h(), align());
}
}
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BOX_H
#define GE_BOX_H
-
#include <FL/Fl_Box.H>
-
class geBox : public Fl_Box
{
public:
-
- geBox(int x, int y, int w, int h, const char* l=nullptr, Fl_Align al=FL_ALIGN_CENTER);
+ geBox(int x, int y, int w, int h, const char* l = nullptr, Fl_Align al = FL_ALIGN_CENTER);
void draw() override;
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "../../../core/const.h"
#include "boxtypes.h"
-
+#include "../../../core/const.h"
+#include <FL/fl_draw.H>
void g_customBorderBox(int x, int y, int w, int h, Fl_Color c)
{
- fl_color(c);
- fl_rectf(x, y, w, h);
- fl_color(G_COLOR_GREY_4);
- fl_rect(x, y, w, h);
+ fl_color(c);
+ fl_rectf(x, y, w, h);
+ fl_color(G_COLOR_GREY_4);
+ fl_rect(x, y, w, h);
}
-
void g_customUpBox(int x, int y, int w, int h, Fl_Color /*c*/)
{
- fl_color(G_COLOR_GREY_2);
- fl_rectf(x, y, w, h);
- fl_color(G_COLOR_GREY_2);
- fl_rect(x, y, w, h);
+ fl_color(G_COLOR_GREY_2);
+ fl_rectf(x, y, w, h);
+ fl_color(G_COLOR_GREY_2);
+ fl_rect(x, y, w, h);
}
-
void g_customDownBox(int x, int y, int w, int h, Fl_Color c)
{
- fl_color(c);
- fl_rectf(x, y, w, h);
- fl_color(G_COLOR_GREY_2);
- fl_rect(x, y, w, h);
+ fl_color(c);
+ fl_rectf(x, y, w, h);
+ fl_color(G_COLOR_GREY_2);
+ fl_rect(x, y, w, h);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BOXTYPES_H
#define GE_BOXTYPES_H
-
#include <FL/Fl.H>
-
constexpr Fl_Boxtype G_CUSTOM_BORDER_BOX = FL_FREE_BOXTYPE;
constexpr Fl_Boxtype G_CUSTOM_UP_BOX = static_cast<Fl_Boxtype>(FL_FREE_BOXTYPE + 1);
constexpr Fl_Boxtype G_CUSTOM_DOWN_BOX = static_cast<Fl_Boxtype>(FL_FREE_BOXTYPE + 3);
-
void g_customBorderBox(int x, int y, int w, int h, Fl_Color c);
-void g_customUpBox (int x, int y, int w, int h, Fl_Color c);
-void g_customDownBox (int x, int y, int w, int h, Fl_Color c);
-
+void g_customUpBox(int x, int y, int w, int h, Fl_Color c);
+void g_customDownBox(int x, int y, int w, int h, Fl_Color c);
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
+#include "button.h"
#include "core/const.h"
#include "utils/gui.h"
-#include "button.h"
-
+#include <FL/fl_draw.H>
-geButton::geButton(int x, int y, int w, int h, const char* l,
- const char** imgOff, const char** imgOn, const char** imgDisabled)
-: Fl_Button (x, y, w, h, l),
- imgOff (imgOff),
- imgOn (imgOn),
- imgDisabled(imgDisabled),
- bgColor0 (G_COLOR_GREY_2),
- bgColor1 (G_COLOR_GREY_4),
- bdColor (G_COLOR_GREY_4),
- txtColor (G_COLOR_LIGHT_2)
+geButton::geButton(int x, int y, int w, int h, const char* l,
+ const char** imgOff, const char** imgOn, const char** imgDisabled)
+: Fl_Button(x, y, w, h, l)
+, imgOff(imgOff)
+, imgOn(imgOn)
+, imgDisabled(imgDisabled)
+, bgColor0(G_COLOR_GREY_2)
+, bgColor1(G_COLOR_GREY_4)
+, bdColor(G_COLOR_GREY_4)
+, txtColor(G_COLOR_LIGHT_2)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geButton::draw()
{
//Fl_Button::draw();
if (active())
- if (value()) draw(imgOn, bgColor1, txtColor);
- else draw(imgOff, bgColor0, txtColor);
+ if (value())
+ draw(imgOn, bgColor1, txtColor);
+ else
+ draw(imgOff, bgColor0, txtColor);
else
draw(imgDisabled, bgColor0, bdColor);
}
-
/* -------------------------------------------------------------------------- */
-
void geButton::draw(const char** img, Fl_Color bgColor, Fl_Color textColor)
{
- fl_rect(x(), y(), w(), h(), bdColor); // draw border
+ fl_rect(x(), y(), w(), h(), bdColor); // draw border
- if (img != nullptr) {
- fl_draw_pixmap(img, x()+1, y()+1);
+ if (img != nullptr)
+ {
+ fl_draw_pixmap(img, x() + 1, y() + 1);
return;
}
- fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor); // draw background
+ fl_rectf(x() + 1, y() + 1, w() - 2, h() - 2, bgColor); // draw background
fl_color(textColor);
- if (label() != nullptr) {
+ if (label() != nullptr)
+ {
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_draw(giada::u::gui::truncate(label(), w()-16).c_str(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
+ fl_draw(giada::u::gui::truncate(label(), w() - 16).c_str(), x() + 2, y(), w() - 2, h(), FL_ALIGN_CENTER);
}
}
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BUTTON_H
#define GE_BUTTON_H
-
#include <FL/Fl_Button.H>
-
class geButton : public Fl_Button
{
public:
-
- geButton(int x, int y, int w, int h, const char* l=nullptr,
- const char** imgOff=nullptr, const char** imgOn=nullptr,
- const char** imgDisabled=nullptr);
+ geButton(int x, int y, int w, int h, const char* l = nullptr,
+ const char** imgOff = nullptr, const char** imgOn = nullptr,
+ const char** imgDisabled = nullptr);
void draw() override;
-protected:
-
+ protected:
void draw(const char** img, Fl_Color bgColor, Fl_Color textColor);
const char** imgOff;
const char** imgOn;
const char** imgDisabled;
- Fl_Color bgColor0; // background not clicked
- Fl_Color bgColor1; // background clicked
- Fl_Color bdColor; // border
- Fl_Color txtColor; // text
+ Fl_Color bgColor0; // background not clicked
+ Fl_Color bgColor1; // background clicked
+ Fl_Color bdColor; // border
+ Fl_Color txtColor; // text
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cstring>
-#include <FL/fl_draw.H>
-#include "core/const.h"
#include "check.h"
-
+#include "core/const.h"
+#include <FL/fl_draw.H>
+#include <cstring>
geCheck::geCheck(int x, int y, int w, int h, const char* l)
: Fl_Check_Button(x, y, w, h, l)
{
+ callback(cb_onChange, this);
}
+/* -------------------------------------------------------------------------- */
+
+void geCheck::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geCheck*>(p))->cb_onChange(); }
/* -------------------------------------------------------------------------- */
+void geCheck::cb_onChange()
+{
+ if (onChange != nullptr)
+ onChange(value());
+}
+
+/* -------------------------------------------------------------------------- */
void geCheck::draw()
{
- fl_rectf(x(), y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_rectf(x(), y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
- const int boxColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
- const int textColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2;
- const int textAlign = hasMultilineText() ? FL_ALIGN_LEFT | FL_ALIGN_TOP : FL_ALIGN_LEFT | FL_ALIGN_CENTER;
+ const Fl_Color boxColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
+ const int textColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2;
+ const Fl_Align textAlign = hasMultilineText() ? FL_ALIGN_LEFT | FL_ALIGN_TOP : FL_ALIGN_LEFT | FL_ALIGN_CENTER;
if (value())
- fl_rectf(x(), y(), 12, h(), (Fl_Color) boxColor);
+ fl_rectf(x(), y(), 12, h(), boxColor);
else
- fl_rect(x(), y(), 12, h(), (Fl_Color) boxColor);
+ fl_rect(x(), y(), 12, h(), boxColor);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
fl_color(textColor);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) textAlign);
+ fl_draw(label(), x() + 20, y(), w(), h(), textAlign);
}
-
/* -------------------------------------------------------------------------- */
-
bool geCheck::hasMultilineText() const
{
return label() == nullptr ? false : std::strchr(label(), '\n') != nullptr;
}
-
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHECK_H
#define GE_CHECK_H
-
#include <FL/Fl_Check_Button.H>
-
+#include <functional>
class geCheck : public Fl_Check_Button
{
public:
-
- geCheck(int x, int y, int w, int h, const char *l=0);
+ geCheck(int x, int y, int w, int h, const char* l = 0);
void draw() override;
+ std::function<void(bool)> onChange = nullptr;
+
private:
+ static void cb_onChange(Fl_Widget* w, void* p);
+ void cb_onChange();
bool hasMultilineText() const;
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <string>
-#include <FL/fl_draw.H>
+#include "choice.h"
#include "core/const.h"
#include "utils/gui.h"
#include "utils/vector.h"
-#include "choice.h"
-
+#include <FL/fl_draw.H>
+#include <cassert>
+#include <string>
-namespace giada {
-namespace v
+namespace giada::v
{
geChoice::geChoice(int x, int y, int w, int h, const char* l, bool ang)
: Fl_Choice(x, y, w, h, l)
-, angle (ang)
+, m_angle(ang)
{
labelsize(G_GUI_FONT_SIZE_BASE);
labelcolor(G_COLOR_LIGHT_2);
color(G_COLOR_GREY_2);
}
-
/* -------------------------------------------------------------------------- */
-
void geChoice::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geChoice*>(p))->cb_onChange(); }
-
/* -------------------------------------------------------------------------- */
-
void geChoice::cb_onChange()
{
- if (onChange != nullptr) onChange(getSelectedId());
+ if (onChange != nullptr)
+ onChange(getSelectedId());
}
-
/* -------------------------------------------------------------------------- */
-
void geChoice::draw()
{
- fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg
- fl_rect(x(), y(), w(), h(), (Fl_Color) G_COLOR_GREY_4); // border
- if (angle)
- fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
+ fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg
+ fl_rect(x(), y(), w(), h(), static_cast<Fl_Color>(G_COLOR_GREY_4)); // border
+ if (m_angle)
+ fl_polygon(x() + w() - 8, y() + h() - 1, x() + w() - 1, y() + h() - 8, x() + w() - 1, y() + h() - 1);
/* pick up the text() from the selected item (value()) and print it in
* the box and avoid overflows */
fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2);
- if (value() != -1)
- fl_draw(u::gui::truncate(text(value()), w()-16).c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+ if (value() != -1)
+ fl_draw(u::gui::truncate(text(value()), w() - 16).c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
}
-
/* -------------------------------------------------------------------------- */
-
ID geChoice::getSelectedId() const
{
- return value() == -1 ? 0 : ids.at(value());
+ return value() == -1 ? -1 : m_ids.at(value());
}
-
/* -------------------------------------------------------------------------- */
-
void geChoice::addItem(const std::string& label, ID id)
{
+ assert(id >= 0);
+
Fl_Choice::add(label.c_str(), 0, cb_onChange, static_cast<void*>(this));
- ids.push_back(id);
+ m_ids.push_back(id);
}
-
/* -------------------------------------------------------------------------- */
+void geChoice::showItem(const std::string& label)
+{
+ value(find_index(label.c_str()));
+}
-void geChoice::showItem(const char* c)
+void geChoice::showItem(ID id)
{
- value(find_index(c));
+ value(u::vector::indexOf(m_ids, id));
}
+/* -------------------------------------------------------------------------- */
-void geChoice::showItem(ID id)
+void geChoice::clear()
{
- value(u::vector::indexOf(ids, id));
+ Fl_Choice::clear();
+ m_ids.clear();
}
-}}
\ No newline at end of file
+
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHOICE_H
#define GE_CHOICE_H
-
+#include "core/types.h"
+#include <FL/Fl_Choice.H>
#include <functional>
#include <string>
#include <vector>
-#include <FL/Fl_Choice.H>
-#include "core/types.h"
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geChoice : public Fl_Choice
{
public:
-
- geChoice(int x, int y, int w, int h, const char* l=0, bool angle=true);
+ geChoice(int x, int y, int w, int h, const char* l = 0, bool angle = true);
void draw() override;
ID getSelectedId() const;
void addItem(const std::string& label, ID id);
- void showItem(const char* c);
+ void showItem(const std::string& label);
void showItem(ID id);
+ void clear();
std::function<void(ID)> onChange = nullptr;
private:
+ static void cb_onChange(Fl_Widget* w, void* p);
+ void cb_onChange();
- static void cb_onChange(Fl_Widget* /*w*/, void* p);
- void cb_onChange();
-
- bool angle;
- std::vector<ID> ids;
+ bool m_angle;
+ std::vector<ID> m_ids;
};
-}}
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "../../../core/const.h"
#include "dial.h"
+#include "../../../core/const.h"
+#include <FL/fl_draw.H>
-
-geDial::geDial(int x, int y, int w, int h, const char *l)
+geDial::geDial(int x, int y, int w, int h, const char* l)
: Fl_Dial(x, y, w, h, l)
{
- labelsize(G_GUI_FONT_SIZE_BASE);
- labelcolor(G_COLOR_LIGHT_2);
- align(FL_ALIGN_LEFT);
- type(FL_FILL_DIAL);
- angles(0, 360);
- color(G_COLOR_GREY_2); // background
- selection_color(G_COLOR_GREY_4); // selection
+ labelsize(G_GUI_FONT_SIZE_BASE);
+ labelcolor(G_COLOR_LIGHT_2);
+ align(FL_ALIGN_LEFT);
+ type(FL_FILL_DIAL);
+ angles(0, 360);
+ color(G_COLOR_GREY_2); // background
+ selection_color(G_COLOR_GREY_4); // selection
}
-
/* -------------------------------------------------------------------------- */
-
void geDial::draw()
{
- double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
+ double angle = (angle2() - angle1()) * (value() - minimum()) / (maximum() - minimum()) + angle1();
- fl_color(G_COLOR_GREY_2);
- fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
+ fl_color(G_COLOR_GREY_2);
+ fl_pie(x(), y(), w(), h(), 270 - angle1(), angle > angle1() ? 360 + 270 - angle : 270 - 360 - angle);
- fl_color(G_COLOR_GREY_4);
- fl_arc(x(), y(), w(), h(), 0, 360);
- fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
+ fl_color(G_COLOR_GREY_4);
+ fl_arc(x(), y(), w(), h(), 0, 360);
+ fl_pie(x(), y(), w(), h(), 270 - angle, 270 - angle1());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_DIAL_H
#define GE_DIAL_H
-
#include <FL/Fl_Dial.H>
-
class geDial : public Fl_Dial
{
public:
-
- geDial(int x, int y, int w, int h, const char *l=0);
+ geDial(int x, int y, int w, int h, const char* l = 0);
void draw();
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <algorithm>
#include "group.h"
+#include <algorithm>
-
-namespace giada {
-namespace v
+namespace giada
{
-geGroup::geGroup(int x, int y) : Fl_Group(x, y, 0, 0)
+namespace v
{
- end();
+geGroup::geGroup(int x, int y)
+: Fl_Group(x, y, 0, 0)
+{
+ end();
}
-
/* -------------------------------------------------------------------------- */
-
std::size_t geGroup::countChildren() const
{
- return m_widgets.size();
+ return m_widgets.size();
}
-
/* -------------------------------------------------------------------------- */
-
void geGroup::add(Fl_Widget* widget)
{
- widget->position(widget->x() + x(), widget->y() + y());
-
- Fl_Group::add(widget);
- m_widgets.push_back(widget);
+ widget->position(widget->x() + x(), widget->y() + y());
- int newW = 0;
- int newH = 0;
+ Fl_Group::add(widget);
+ m_widgets.push_back(widget);
- for (const Fl_Widget* wg : m_widgets) {
- newW = std::max(newW, (wg->x() + wg->w()) - x());
- newH = std::max(newH, (wg->y() + wg->h()) - y());
- }
+ int newW = 0;
+ int newH = 0;
- /* Don't call size(newW, newH) as it changes widgets position. Adjust width
+ for (const Fl_Widget* wg : m_widgets)
+ {
+ newW = std::max(newW, (wg->x() + wg->w()) - x());
+ newH = std::max(newH, (wg->y() + wg->h()) - y());
+ }
+
+ /* Don't call size(newW, newH) as it changes widgets position. Adjust width
and height manually instead. */
- w(newW); h(newH);
+ w(newW);
+ h(newH);
}
-
/* -------------------------------------------------------------------------- */
-
Fl_Widget* geGroup::getChild(std::size_t i)
{
- return m_widgets.at(i); // Throws std::out_of_range in case
+ return m_widgets.at(i); // Throws std::out_of_range in case
}
-
/* -------------------------------------------------------------------------- */
-
Fl_Widget* geGroup::getLastChild()
{
- return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
+ return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
}
-}}
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_GROUP_H
#define GE_GROUP_H
-
-#include <vector>
#include <FL/Fl_Group.H>
+#include <vector>
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
/* geGroup
A group that resizes itself accoring to the content. */
class geGroup : public Fl_Group
{
public:
-
geGroup(int x, int y);
- /* countChildren
+ /* countChildren
Returns the number of widgets contained in this group. */
-
- std::size_t countChildren() const;
- /* add
+ std::size_t countChildren() const;
+
+ /* add
Adds a Fl_Widget 'w' to this group. Coordinates are relative to the group,
so origin starts at (0, 0). */
- void add(Fl_Widget* w);
+ void add(Fl_Widget* w);
- Fl_Widget* getChild(std::size_t i);
- Fl_Widget* getLastChild();
+ Fl_Widget* getChild(std::size_t i);
+ Fl_Widget* getLastChild();
-private:
-
- /* m_widgets
+ private:
+ /* m_widgets
The internal Fl_Scroll::array_ is unreliable when inspected with the child()
method. Let's keep track of widgets that belong to this group manually. */
- std::vector<Fl_Widget*> m_widgets;
+ std::vector<Fl_Widget*> m_widgets;
};
-}}
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "../../../core/const.h"
-#include "boxtypes.h"
#include "input.h"
+#include "boxtypes.h"
+#include "core/const.h"
-
-geInput::geInput(int x, int y, int w, int h, const char *l)
- : Fl_Input(x, y, w, h, l)
+geInput::geInput(int x, int y, int w, int h, const char* l)
+: Fl_Input(x, y, w, h, l)
{
- //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
- box(G_CUSTOM_BORDER_BOX);
- labelsize(G_GUI_FONT_SIZE_BASE);
- labelcolor(G_COLOR_LIGHT_2);
- color(G_COLOR_BLACK);
- textcolor(G_COLOR_LIGHT_2);
- cursor_color(G_COLOR_LIGHT_2);
- selection_color(G_COLOR_GREY_4);
- textsize(G_GUI_FONT_SIZE_BASE);
+ //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
+ box(G_CUSTOM_BORDER_BOX);
+ labelsize(G_GUI_FONT_SIZE_BASE);
+ labelcolor(G_COLOR_LIGHT_2);
+ color(G_COLOR_BLACK);
+ textcolor(G_COLOR_LIGHT_2);
+ cursor_color(G_COLOR_LIGHT_2);
+ selection_color(G_COLOR_GREY_4);
+ textsize(G_GUI_FONT_SIZE_BASE);
+
+ when(FL_WHEN_CHANGED);
+ callback(cb_onChange, this);
}
+
+/* -------------------------------------------------------------------------- */
+
+void geInput::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geInput*>(p))->cb_onChange(); }
+
+/* -------------------------------------------------------------------------- */
+
+void geInput::cb_onChange()
+{
+ if (onChange != nullptr)
+ onChange(value());
+}
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_INPUT_H
#define GE_INPUT_H
-
#include <FL/Fl_Input.H>
-
+#include <functional>
+#include <string>
class geInput : public Fl_Input
{
public:
+ geInput(int x, int y, int w, int h, const char* l = 0);
+
+ std::function<void(const std::string&)> onChange = nullptr;
- geInput(int x, int y, int w, int h, const char *l=0);
+private:
+ static void cb_onChange(Fl_Widget* w, void* p);
+ void cb_onChange();
};
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/const.h"
-#include "boxtypes.h"
#include "liquidScroll.h"
-
+#include "boxtypes.h"
+#include "core/const.h"
geLiquidScroll::geLiquidScroll(int x, int y, int w, int h)
: geScroll(x, y, w, h, Fl_Scroll::VERTICAL_ALWAYS)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geLiquidScroll::resize(int X, int Y, int W, int H)
{
- int nc = children()-2; // skip hscrollbar and vscrollbar
- for (int t=0; t<nc; t++) { // tell children to resize to our new width
+ int nc = children() - 2; // skip hscrollbar and vscrollbar
+ for (int t = 0; t < nc; t++)
+ { // tell children to resize to our new width
Fl_Widget* c = child(t);
- c->resize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar
+ c->resize(c->x(), c->y(), W - 24, c->h()); // W-24: leave room for scrollbar
}
- init_sizes(); // tell scroll children changed in size
- Fl_Scroll::resize(X,Y,W,H);
+ init_sizes(); // tell scroll children changed in size
+ Fl_Scroll::resize(X, Y, W, H);
}
-
-
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_LIQUID_SCROLL_H
#define GE_LIQUID_SCROLL_H
-
+#include "core/const.h"
#include "scroll.h"
-
class geLiquidScroll : public geScroll
{
public:
-
geLiquidScroll(int x, int y, int w, int h);
void resize(int x, int y, int w, int h) override;
- /* addWidget
+ /* addWidget
Adds a new widget to the bottom, with proper spacing. */
-
- template<typename T>
- T* addWidget(T* wg)
- {
- int numChildren = countChildren();
- int wx = x();
- int wy = y() - yposition() + (numChildren * (wg->h() + G_GUI_INNER_MARGIN));
- int ww = w() - 24;
- int wh = wg->h();
+ template <typename T>
+ T* addWidget(T* wg)
+ {
+ int numChildren = countChildren();
- wg->resize(wx, wy, ww, wh);
- add(wg);
- redraw();
+ int wx = x();
+ int wy = y() - yposition() + (numChildren * (wg->h() + G_GUI_INNER_MARGIN));
+ int ww = w() - 24;
+ int wh = wg->h();
- return wg;
- }
-};
+ wg->resize(wx, wy, ww, wh);
+ add(wg);
+ redraw();
+ return wg;
+ }
+};
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/const.h"
#include "pack.h"
+#include "core/const.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gePack::gePack(int x, int y, Direction d, int gutter)
-: geGroup (x, y)
+: geGroup(x, y)
, m_direction(d)
-, m_gutter (gutter)
+, m_gutter(gutter)
{
- end();
+ end();
}
-
/* -------------------------------------------------------------------------- */
-
void gePack::add(Fl_Widget* widget)
{
- if (countChildren() == 0)
- widget->position(0, 0);
- else
- if (m_direction == Direction::HORIZONTAL)
- widget->position((getLastChild()->x() + getLastChild()->w() + m_gutter) - x(), 0);
- else
- widget->position(0, (getLastChild()->y() + getLastChild()->h() + m_gutter) - y());
-
- geGroup::add(widget);
+ if (countChildren() == 0)
+ widget->position(0, 0);
+ else if (m_direction == Direction::HORIZONTAL)
+ widget->position((getLastChild()->x() + getLastChild()->w() + m_gutter) - x(), 0);
+ else
+ widget->position(0, (getLastChild()->y() + getLastChild()->h() + m_gutter) - y());
+
+ geGroup::add(widget);
}
-}}
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PACK_H
#define GE_PACK_H
-
#include "core/const.h"
#include "gui/elems/basics/group.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
+{
+enum class Direction
{
-enum class Direction { HORIZONTAL, VERTICAL };
+ HORIZONTAL,
+ VERTICAL
+};
/* gePack
A stack of widgets that resize itself according to its content. */
class gePack : public geGroup
{
public:
+ gePack(int x, int y, Direction d, int gutter = G_GUI_INNER_MARGIN);
- gePack(int x, int y, Direction d, int gutter=G_GUI_INNER_MARGIN);
-
- /* add
+ /* add
Adds a Fl_Widget 'w' to this pack. Coordinates are relative to the group,
so origin starts at (0, 0). */
- void add(Fl_Widget* w);
+ void add(Fl_Widget* w);
-private:
-
- Direction m_direction;
- int m_gutter;
+ private:
+ Direction m_direction;
+ int m_gutter;
};
-}}
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "progress.h"
#include "../../../core/const.h"
#include "boxtypes.h"
-#include "progress.h"
-
-geProgress::geProgress(int x, int y, int w, int h, const char *l)
+geProgress::geProgress(int x, int y, int w, int h, const char* l)
: Fl_Progress(x, y, w, h, l)
{
- color(G_COLOR_GREY_2, G_COLOR_GREY_4);
- box(G_CUSTOM_BORDER_BOX);
+ color(G_COLOR_GREY_2, G_COLOR_GREY_4);
+ box(G_CUSTOM_BORDER_BOX);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PROGRESS_H
#define GE_PROGRESS_H
-
#include <FL/Fl_Progress.H>
-
class geProgress : public Fl_Progress
{
public:
-
- geProgress(int x, int y, int w, int h, const char *l=0);
+ geProgress(int x, int y, int w, int h, const char* l = 0);
};
-
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
-#include "radio.h"
-
-
-geRadio::geRadio(int x, int y, int w, int h, const char *l)
-: Fl_Radio_Button(x, y, w, h, l)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geRadio::draw()
-{
- int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
-
- if (value()) {
- fl_rect(x(), y(), 12, h(), (Fl_Color) color);
- fl_rectf(x(), y(), 12, h(), (Fl_Color) color);
- }
- else {
- fl_rectf(x(), y(), 12, h(), FL_BACKGROUND_COLOR);
- fl_rect(x(), y(), 12, h(), (Fl_Color) color);
- }
-
- fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
- fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_color(G_COLOR_LIGHT_2);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_RADIO_H
-#define GE_RADIO_H
-
-
-#include <FL/Fl_Radio_Button.H>
-
-
-class geRadio : public Fl_Radio_Button
-{
-public:
-
- geRadio(int x, int y, int w, int h, const char *l=0);
-
- void draw() override;
-};
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "resizerBar.h"
+#include "core/const.h"
#include <FL/Fl.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_draw.H>
-#include "core/const.h"
-#include "resizerBar.h"
-
geResizerBar::geResizerBar(int X, int Y, int W, int H, int minSize, bool type, Fl_Widget* target)
-: Fl_Box (X, Y, W, H),
- m_type (type),
- m_minSize (minSize),
- m_lastPos (0),
- m_initialPos(0),
- m_hover (false),
- m_target (target)
+: Fl_Box(X, Y, W, H)
+, m_type(type)
+, m_minSize(minSize)
+, m_lastPos(0)
+, m_initialPos(0)
+, m_hover(false)
+, m_target(target)
{
- if (m_type == VERTICAL) {
+ if (m_type == VERTICAL)
+ {
m_origSize = H;
labelsize(H);
}
- else {
+ else
+ {
m_origSize = W;
labelsize(W);
}
visible_focus(0);
}
-
/* -------------------------------------------------------------------------- */
-
void geResizerBar::handleDrag(int diff)
{
Fl_Scroll* group = static_cast<Fl_Scroll*>(parent());
// First pass: find widget directly above us with common edge
// Possibly clamp 'diff' if widget would get too small..
- for (int t = 0; t < group->children(); t++) {
+ for (int t = 0; t < group->children(); t++)
+ {
Fl_Widget* wd = group->child(t);
- if (m_type == VERTICAL) {
- if ((wd->y() + wd->h()) == top) { // found widget directly above?
+ if (m_type == VERTICAL)
+ {
+ if ((wd->y() + wd->h()) == top)
+ { // found widget directly above?
if ((wd->h() + diff) < m_minSize)
- diff = wd->h() - m_minSize; // clamp
- wd->resize(wd->x(), wd->y(), wd->w(), wd->h() + diff); // change height
- break; // done with first pass
+ diff = wd->h() - m_minSize; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w(), wd->h() + diff); // change height
+ break; // done with first pass
}
}
- else {
- if ((wd->x() + wd->w()) == top) { // found widget directly above?
+ else
+ {
+ if ((wd->x() + wd->w()) == top)
+ { // found widget directly above?
if ((wd->w() + diff) < m_minSize)
- diff = wd->w() - m_minSize; // clamp
- wd->resize(wd->x(), wd->y(), wd->w() + diff, wd->h()); // change width
- break; // done with first pass
+ diff = wd->w() - m_minSize; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w() + diff, wd->h()); // change width
+ break; // done with first pass
}
}
}
// Second pass: find widgets below us, move based on clamped diff
- for (int t = 0; t < group->children(); t++) {
+ for (int t = 0; t < group->children(); t++)
+ {
Fl_Widget* wd = group->child(t);
- if (m_type == VERTICAL) {
- if (wd->y() >= bot) // found widget below us?
- wd->resize(wd->x(), wd->y() + diff, wd->w(), wd->h()); // change position
+ if (m_type == VERTICAL)
+ {
+ if (wd->y() >= bot) // found widget below us?
+ wd->resize(wd->x(), wd->y() + diff, wd->w(), wd->h()); // change position
}
- else {
+ else
+ {
if (wd->x() >= bot)
wd->resize(wd->x() + diff, wd->y(), wd->w(), wd->h());
}
group->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geResizerBar::draw()
{
Fl_Box::draw();
fl_rectf(x(), y(), w(), h(), m_hover ? G_COLOR_GREY_2 : G_COLOR_GREY_1);
}
-
/* -------------------------------------------------------------------------- */
-
int geResizerBar::handle(int e)
{
- int ret = 0;
+ int ret = 0;
int currentPos = m_type == VERTICAL ? Fl::event_y_root() : Fl::event_x_root();
- switch (e) {
- case FL_FOCUS:
- ret = 1;
- break;
- case FL_ENTER:
- ret = 1;
- fl_cursor(m_type == VERTICAL ? FL_CURSOR_NS : FL_CURSOR_WE);
- m_hover = true;
- redraw();
- break;
- case FL_LEAVE:
- ret = 1;
- fl_cursor(FL_CURSOR_DEFAULT);
- m_hover = false;
- redraw();
- break;
- case FL_PUSH:
- ret = 1;
- m_lastPos = currentPos;
- m_initialPos = currentPos;
- break;
- case FL_DRAG:
- handleDrag(currentPos - m_lastPos);
- m_lastPos = currentPos;
- ret = 1;
- if (onDrag != nullptr)
- onDrag(m_target);
- break;
- case FL_RELEASE:
- if (m_initialPos != currentPos && onRelease != nullptr)
- onRelease(m_target);
- break;
- default: break;
+ switch (e)
+ {
+ case FL_FOCUS:
+ ret = 1;
+ break;
+ case FL_ENTER:
+ ret = 1;
+ fl_cursor(m_type == VERTICAL ? FL_CURSOR_NS : FL_CURSOR_WE);
+ m_hover = true;
+ redraw();
+ break;
+ case FL_LEAVE:
+ ret = 1;
+ fl_cursor(FL_CURSOR_DEFAULT);
+ m_hover = false;
+ redraw();
+ break;
+ case FL_PUSH:
+ ret = 1;
+ m_lastPos = currentPos;
+ m_initialPos = currentPos;
+ break;
+ case FL_DRAG:
+ handleDrag(currentPos - m_lastPos);
+ m_lastPos = currentPos;
+ ret = 1;
+ if (onDrag != nullptr)
+ onDrag(m_target);
+ break;
+ case FL_RELEASE:
+ if (m_initialPos != currentPos && onRelease != nullptr)
+ onRelease(m_target);
+ break;
+ default:
+ break;
}
- return(Fl_Box::handle(e) | ret);
+ return (Fl_Box::handle(e) | ret);
}
-
/* -------------------------------------------------------------------------- */
-
int geResizerBar::getMinSize() const
-{
- return m_minSize;
-}
-
+{
+ return m_minSize;
+}
/* -------------------------------------------------------------------------- */
-
void geResizerBar::resize(int x, int y, int w, int h)
{
if (m_type == VERTICAL)
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_RESIZER_BAR_H
#define GE_RESIZER_BAR_H
-
-#include <functional>
#include <FL/Fl_Box.H>
-
+#include <functional>
class geResizerBar : public Fl_Box
{
public:
-
static const int HORIZONTAL = 0;
static const int VERTICAL = 1;
- geResizerBar(int x, int y, int w, int h, int minSize, bool type, Fl_Widget* target=nullptr);
+ geResizerBar(int x, int y, int w, int h, int minSize, bool type, Fl_Widget* target = nullptr);
- int handle(int e) override;
+ int handle(int e) override;
void draw() override;
void resize(int x, int y, int w, int h) override;
int getMinSize() const;
- std::function<void(const Fl_Widget*)> onDrag = nullptr;
+ std::function<void(const Fl_Widget*)> onDrag = nullptr;
std::function<void(const Fl_Widget*)> onRelease = nullptr;
-private:
-
+ private:
void handleDrag(int diff);
bool m_type;
Fl_Widget* m_target;
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/const.h"
-#include "boxtypes.h"
#include "scroll.h"
-
+#include "boxtypes.h"
+#include "core/const.h"
+#include <cassert>
geScroll::geScroll(int x, int y, int w, int h, int t)
: Fl_Scroll(x, y, w, h)
hscrollbar.slider(G_CUSTOM_BORDER_BOX);
}
-
/* -------------------------------------------------------------------------- */
-
int geScroll::countChildren() const
{
return children() - 2; // Exclude scrollbars
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SCROLL_H
#define GE_SCROLL_H
-
#include <FL/Fl_Scroll.H>
-
class geScroll : public Fl_Scroll
{
public:
+ geScroll(int x, int y, int w, int h, int type = Fl_Scroll::BOTH);
- geScroll(int x, int y, int w, int h, int type=Fl_Scroll::BOTH);
-
- int countChildren() const;
+ int countChildren() const;
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "core/const.h"
-#include "boxtypes.h"
#include "scrollPack.h"
+#include "boxtypes.h"
+#include "core/const.h"
+#include <cassert>
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geScrollPack::geScrollPack(int x, int y, int w, int h, int type, Direction dir,
int gutter)
-: geScroll (x, y, w, h, type)
+: geScroll(x, y, w, h, type)
, m_direction(dir)
-, m_gutter (gutter)
+, m_gutter(gutter)
{
- end();
+ end();
}
-
/* -------------------------------------------------------------------------- */
-
std::size_t geScrollPack::countChildren() const
{
- return m_widgets.size();
+ return m_widgets.size();
}
-
/* -------------------------------------------------------------------------- */
-
void geScrollPack::add(Fl_Widget* w)
{
- if (countChildren() == 0)
- w->position(x(), y());
- else
- if (m_direction == Direction::HORIZONTAL)
- w->position((getLastChild()->x() + getLastChild()->w() + m_gutter), y());
- else
- w->position(x(), (getLastChild()->y() + getLastChild()->h() + m_gutter));
-
- geScroll::add(w);
- m_widgets.push_back(w);
+ if (countChildren() == 0)
+ w->position(x(), y());
+ else if (m_direction == Direction::HORIZONTAL)
+ w->position((getLastChild()->x() + getLastChild()->w() + m_gutter), y());
+ else
+ w->position(x(), (getLastChild()->y() + getLastChild()->h() + m_gutter));
+
+ geScroll::add(w);
+ m_widgets.push_back(w);
}
-
/* -------------------------------------------------------------------------- */
-
Fl_Widget* geScrollPack::getChild(std::size_t i)
{
- return m_widgets.at(i); // Throws std::out_of_range in case
+ return m_widgets.at(i); // Throws std::out_of_range in case
}
/* -------------------------------------------------------------------------- */
-
Fl_Widget* geScrollPack::getLastChild()
{
- return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
+ return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
}
-}}
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SCROLL_PACK_H
#define GE_SCROLL_PACK_H
-
-#include "gui/elems/basics/scroll.h"
#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/scroll.h"
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
/* geScrollPack
A scrollable viewport that contains packed widgets. */
class geScrollPack : public geScroll
{
public:
+ geScrollPack(int x, int y, int w, int h, int type = Fl_Scroll::BOTH,
+ Direction d = Direction::HORIZONTAL, int gutter = G_GUI_INNER_MARGIN);
- geScrollPack(int x, int y, int w, int h, int type=Fl_Scroll::BOTH,
- Direction d=Direction::HORIZONTAL, int gutter=G_GUI_INNER_MARGIN);
-
- /* countChildren
+ /* countChildren
Returns the number of widgets contained in this group. */
-
- std::size_t countChildren() const;
-
- void add(Fl_Widget* w);
- Fl_Widget* getChild(std::size_t i);
- Fl_Widget* getLastChild();
+ std::size_t countChildren() const;
-private:
+ void add(Fl_Widget* w);
- /* m_widgets
+ Fl_Widget* getChild(std::size_t i);
+ Fl_Widget* getLastChild();
+
+ private:
+ /* m_widgets
The internal Fl_Scroll::array_ is unreliable when inspected with the child()
method. Let's keep track of widgets that belong to this group manually. */
- std::vector<Fl_Widget*> m_widgets;
+ std::vector<Fl_Widget*> m_widgets;
- Direction m_direction;
- int m_gutter;
+ Direction m_direction;
+ int m_gutter;
};
-}}
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "slider.h"
#include "../../../core/const.h"
#include "boxtypes.h"
-#include "slider.h"
-
-geSlider::geSlider(int x, int y, int w, int h, const char *l)
- : Fl_Slider(x, y, w, h, l)
+geSlider::geSlider(int x, int y, int w, int h, const char* l)
+: Fl_Slider(x, y, w, h, l)
{
- type(FL_HOR_FILL_SLIDER);
+ type(FL_HOR_FILL_SLIDER);
- labelsize(G_GUI_FONT_SIZE_BASE);
- align(FL_ALIGN_LEFT);
- labelcolor(G_COLOR_LIGHT_2);
+ labelsize(G_GUI_FONT_SIZE_BASE);
+ align(FL_ALIGN_LEFT);
+ labelcolor(G_COLOR_LIGHT_2);
- box(G_CUSTOM_BORDER_BOX);
- color(G_COLOR_GREY_2);
- selection_color(G_COLOR_GREY_4);
+ box(G_CUSTOM_BORDER_BOX);
+ color(G_COLOR_GREY_2);
+ selection_color(G_COLOR_GREY_4);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SLIDER_H
#define GE_SLIDER_H
-
#include <FL/Fl_Slider.H>
-
class geSlider : public Fl_Slider
{
public:
+ geSlider(int x, int y, int w, int h, const char* l = 0);
- geSlider(int x, int y, int w, int h, const char *l=0);
-
- int id;
+ int id;
};
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
#include "statusButton.h"
+#include "core/const.h"
+#include <FL/fl_draw.H>
-
+namespace giada::v
+{
geStatusButton::geStatusButton(int x, int y, int w, int h, const char** imgOff,
- const char** imgOn, const char** imgDisabled)
-: geButton(x, y, w, h, "", imgOff, imgOn, imgDisabled),
- m_status(false)
+ const char** imgOn, const char** imgDisabled)
+: geButton(x, y, w, h, "", imgOff, imgOn, imgDisabled)
+, m_status(false)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geStatusButton::draw()
{
if (active())
- if (m_status) geButton::draw(imgOn, bgColor1, txtColor);
- else geButton::draw(imgOff, bgColor0, txtColor);
+ if (m_status)
+ geButton::draw(imgOn, bgColor1, txtColor);
+ else
+ geButton::draw(imgOff, bgColor0, txtColor);
else
geButton::draw(imgDisabled, bgColor0, bdColor);
}
-
/* -------------------------------------------------------------------------- */
-
void geStatusButton::setStatus(bool s)
{
- m_status = s;
- redraw();
+ m_status = s;
+ redraw();
}
-
bool geStatusButton::getStatus() const
{
- return m_status;
+ return m_status;
}
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_STATUS_BUTTON_H
#define GE_STATUS_BUTTON_H
-
#include "button.h"
-
+namespace giada::v
+{
class geStatusButton : public geButton
{
-public:
-
- geStatusButton(int x, int y, int w, int h, const char** imgOff=nullptr,
- const char** imgOn=nullptr, const char** imgDisabled=nullptr);
+ public:
+ geStatusButton(int x, int y, int w, int h, const char** imgOff = nullptr,
+ const char** imgOn = nullptr, const char** imgDisabled = nullptr);
void draw() override;
- bool getStatus() const;
-
- void setStatus(bool s);
+ bool getStatus() const;
-private:
+ void setStatus(bool s);
+ private:
bool m_status;
};
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "browser.h"
+#include "basics/boxtypes.h"
#include "core/const.h"
-#include "utils/string.h"
-#include "utils/fs.h"
#include "gui/dialogs/browser/browserBase.h"
-#include "basics/boxtypes.h"
-#include "browser.h"
-
+#include "utils/fs.h"
+#include "utils/string.h"
-namespace giada {
+namespace giada
+{
namespace v
{
geBrowser::geBrowser(int x, int y, int w, int h)
-: Fl_File_Browser (x, y, w, h),
- m_showHiddenFiles(false)
+: Fl_File_Browser(x, y, w, h)
+, m_showHiddenFiles(false)
{
box(G_CUSTOM_BORDER_BOX);
textsize(G_GUI_FONT_SIZE_BASE);
this->hscrollbar.labelcolor(G_COLOR_LIGHT_1);
this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
- take_focus(); // let it have focus on startup
+ take_focus(); // let it have focus on startup
}
-
/* -------------------------------------------------------------------------- */
-
void geBrowser::toggleHiddenFiles()
{
m_showHiddenFiles = !m_showHiddenFiles;
loadDir(m_currentDir);
}
-
/* -------------------------------------------------------------------------- */
-
void geBrowser::loadDir(const std::string& dir)
{
m_currentDir = dir;
/* Clean up unwanted elements. Hide "../" first, it just screws up things.
Also remove hidden files, if requested. */
- for (int i=size(); i>=0; i--) {
+ for (int i = size(); i >= 0; i--)
+ {
if (text(i) == nullptr)
continue;
if (strcmp(text(i), "../") == 0 || (!m_showHiddenFiles && strncmp(text(i), ".", 1) == 0))
}
}
-
/* -------------------------------------------------------------------------- */
int geBrowser::handle(int e)
{
int ret = Fl_File_Browser::handle(e);
- switch (e) {
- case FL_FOCUS:
- case FL_UNFOCUS:
- ret = 1; // enables receiving Keyboard events
- break;
- case FL_KEYDOWN: // keyboard
- if (Fl::event_key(FL_Down))
- select(value() + 1);
- else
- if (Fl::event_key(FL_Up))
- select(value() - 1);
- else
- if (Fl::event_key(FL_Enter))
- static_cast<v::gdBrowserBase*>(parent())->fireCallback();
- ret = 1;
- break;
- case FL_PUSH: // mouse
- if (Fl::event_clicks() > 0) // double click
- static_cast<v::gdBrowserBase*>(parent())->fireCallback();
- ret = 1;
- break;
- case FL_RELEASE: // mouse
- /* nasty trick to keep the selection on mouse release */
- if (value() > 1) {
- select(value() - 1);
- select(value() + 1);
- }
- else {
- select(value() + 1);
- select(value() - 1);
- }
- ret = 1;
- break;
+ switch (e)
+ {
+ case FL_FOCUS:
+ case FL_UNFOCUS:
+ ret = 1; // enables receiving Keyboard events
+ break;
+ case FL_KEYDOWN: // keyboard
+ if (Fl::event_key(FL_Down))
+ select(value() + 1);
+ else if (Fl::event_key(FL_Up))
+ select(value() - 1);
+ else if (Fl::event_key(FL_Enter))
+ static_cast<v::gdBrowserBase*>(parent())->fireCallback();
+ ret = 1;
+ break;
+ case FL_PUSH: // mouse
+ if (Fl::event_clicks() > 0) // double click
+ static_cast<v::gdBrowserBase*>(parent())->fireCallback();
+ ret = 1;
+ break;
+ case FL_RELEASE: // mouse
+ /* nasty trick to keep the selection on mouse release */
+ if (value() > 1)
+ {
+ select(value() - 1);
+ select(value() + 1);
+ }
+ else
+ {
+ select(value() + 1);
+ select(value() - 1);
+ }
+ ret = 1;
+ break;
}
return ret;
}
/* -------------------------------------------------------------------------- */
-
std::string geBrowser::getCurrentDir()
{
return normalize(u::fs::getRealPath(m_currentDir));
}
-
/* -------------------------------------------------------------------------- */
-
std::string geBrowser::getSelectedItem(bool fullPath)
{
- if (!fullPath) // no full path requested? return the selected text
+ if (!fullPath) // no full path requested? return the selected text
return normalize(text(value()));
- else
- if (value() == 0) // no rows selected? return current directory
+ else if (value() == 0) // no rows selected? return current directory
return normalize(m_currentDir);
- else {
+ else
+ {
#ifdef G_OS_WINDOWS
std::string sep = m_currentDir != "" ? G_SLASH_STR : "";
#else
}
}
-
/* -------------------------------------------------------------------------- */
-
void geBrowser::preselect(int pos, int line)
{
position(pos);
select(line);
}
-
/* -------------------------------------------------------------------------- */
-
std::string geBrowser::normalize(const std::string& s)
{
std::string out = s;
//if (out.back() == G_SLASH && out.length() > 1)
if (out.back() == G_SLASH && !u::fs::isRootDir(s))
- out = out.substr(0, out.size()-1);
+ out = out.substr(0, out.size() - 1);
return out;
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BROWSER_H
#define GE_BROWSER_H
-
-#include <string>
#include <FL/Fl_File_Browser.H>
+#include <string>
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geBrowser : public Fl_File_Browser
{
public:
-
geBrowser(int x, int y, int w, int h);
void toggleHiddenFiles();
Returns the full path or just the displayed name of the i-th selected item.
Always with the trailing slash! */
- std::string getSelectedItem(bool fullPath=true);
+ std::string getSelectedItem(bool fullPath = true);
std::string getCurrentDir();
int handle(int e);
-private:
-
+ private:
/* normalize
Makes sure the std::string never ends with a trailing slash. */
std::string normalize(const std::string& s);
std::string m_currentDir;
- bool m_showHiddenFiles;
+ bool m_showHiddenFiles;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <string>
-#include "deps/rtaudio/RtAudio.h"
+#include "tabAudio.h"
#include "core/const.h"
-#include "core/conf.h"
#include "core/kernelAudio.h"
-#include "utils/string.h"
-#include "gui/dialogs/devInfo.h"
+#include "deps/rtaudio/RtAudio.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/button.h"
-#include "tabAudio.h"
-
+#include "utils/string.h"
+#include <string>
-namespace giada {
-namespace v
+namespace giada::v
{
-geTabAudio::geTabAudio(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "Sound System")
+geTabAudio::geDeviceMenu::geDeviceMenu(int x, int y, int w, int h, const char* l, const std::vector<c::config::AudioDeviceData>& devices)
+: geChoice(x, y, w, h, l)
{
- begin();
- soundsys = new geChoice(x()+114, y()+9, 250, 20, "System");
- buffersize = new geChoice(x()+114, y()+37, 55, 20, "Buffer size");
- samplerate = new geChoice(x()+304, y()+37, 60, 20, "Sample rate");
- sounddevOut = new geChoice(x()+114, y()+65, 222, 20, "Output device");
- devOutInfo = new geButton(x()+344, y()+65, 20, 20, "?");
- channelsOut = new geChoice(x()+114, y()+93, 55, 20, "Output channels");
- limitOutput = new geCheck (x()+177, y()+93, 55, 20, "Limit output");
- sounddevIn = new geChoice(x()+114, y()+121, 222, 20, "Input device");
- devInInfo = new geButton(x()+344, y()+121, 20, 20, "?");
- channelsIn = new geChoice(x()+114, y()+149, 55, 20, "Input channels");
- recTriggerLevel = new geInput (x()+309, y()+149, 55, 20, "Rec threshold (dB)");
- rsmpQuality = new geChoice(x()+114, y()+177, 250, 20, "Resampling");
- new geBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92, "Restart Giada for the changes to take effect.");
- end();
-
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
-
- soundsys->add("(none)");
-
-#if defined(__linux__)
-
- if (m::kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
- soundsys->add("ALSA");
- if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
- soundsys->add("Jack");
- if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
- soundsys->add("PulseAudio");
-
- switch (m::conf::conf.soundSystem) {
- case G_SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case G_SYS_API_ALSA:
- soundsys->showItem("ALSA");
- break;
- case G_SYS_API_JACK:
- soundsys->showItem("Jack");
- buffersize->deactivate();
- samplerate->deactivate();
- break;
- case G_SYS_API_PULSE:
- soundsys->showItem("PulseAudio");
- break;
- }
-
-#elif defined(__FreeBSD__)
-
- if (m::kernelAudio::hasAPI(RtAudio::UNIX_JACK))
- soundsys->add("Jack");
- if (m::kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
- soundsys->add("PulseAudio");
-
- switch (m::conf::conf.soundSystem) {
- case G_SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case G_SYS_API_JACK:
- soundsys->showItem("Jack");
- buffersize->deactivate();
- samplerate->deactivate();
- break;
- case G_SYS_API_PULSE:
- soundsys->showItem("PulseAudio");
- break;
- }
-
-#elif defined(_WIN32)
-
- if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
- soundsys->add("DirectSound");
- if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
- soundsys->add("ASIO");
- if (m::kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
- soundsys->add("WASAPI");
-
- switch (m::conf::conf.soundSystem) {
- case G_SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case G_SYS_API_DS:
- soundsys->showItem("DirectSound");
- break;
- case G_SYS_API_ASIO:
- soundsys->showItem("ASIO");
- break;
- case G_SYS_API_WASAPI:
- soundsys->showItem("WASAPI");
- break;
- }
-
-#elif defined(__APPLE__)
-
- if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
- soundsys->add("CoreAudio");
-
- switch (m::conf::conf.soundSystem) {
- case G_SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case G_SYS_API_CORE:
- soundsys->showItem("CoreAudio");
- break;
- }
-
-#endif
-
- soundsysInitValue = soundsys->value();
-
- soundsys->callback(cb_deactivate_sounddev, (void*)this);
-
- sounddevIn->callback(cb_fetchInChans, this);
- sounddevOut->callback(cb_fetchOutChans, this);
-
- devOutInfo->callback(cb_showOutputInfo, this);
- devInInfo->callback(cb_showInputInfo, this);
-
- if (m::conf::conf.soundSystem != G_SYS_API_NONE) {
- fetchSoundDevs();
- fetchOutChans();
- fetchInChans(sounddevIn->value());
-
- /* fill frequency dropdown menu */
- /* TODO - add fetchFrequencies() */
-
- int nfreq = m::kernelAudio::getTotalFreqs(sounddevOut->value());
- for (int i=0; i<nfreq; i++) {
- int freq = m::kernelAudio::getFreq(sounddevOut->value(), i);
- samplerate->add(u::string::iToString(freq).c_str());
- if (freq == m::conf::conf.samplerate)
- samplerate->value(i);
- }
- }
- else {
- sounddevIn->deactivate();
- sounddevOut->deactivate();
- channelsIn->deactivate();
- channelsOut->deactivate();
- devOutInfo->deactivate();
- devInInfo->deactivate();
- samplerate->deactivate();
+ if (devices.size() == 0)
+ {
+ addItem("-- no devices found --", 0);
+ showItem(0);
+ return;
}
- buffersize->add("8");
- buffersize->add("16");
- buffersize->add("32");
- buffersize->add("64");
- buffersize->add("128");
- buffersize->add("256");
- buffersize->add("512");
- buffersize->add("1024");
- buffersize->add("2048");
- buffersize->add("4096");
- buffersize->showItem(std::to_string(m::conf::conf.buffersize).c_str());
-
- rsmpQuality->add("Sinc best quality (very slow)");
- rsmpQuality->add("Sinc medium quality (slow)");
- rsmpQuality->add("Sinc basic quality (medium)");
- rsmpQuality->add("Zero Order Hold (fast)");
- rsmpQuality->add("Linear (very fast)");
- rsmpQuality->value(m::conf::conf.rsmpQuality);
-
- recTriggerLevel->value(u::string::fToString(m::conf::conf.recTriggerLevel, 1).c_str());
-
- limitOutput->value(m::conf::conf.limitOutput);
+ for (const c::config::AudioDeviceData& device : devices)
+ addItem(device.name, device.index);
}
-
/* -------------------------------------------------------------------------- */
-
-
-void geTabAudio::cb_deactivate_sounddev(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_deactivate_sounddev(); }
-void geTabAudio::cb_fetchInChans(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_fetchInChans(); }
-void geTabAudio::cb_fetchOutChans(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_fetchOutChans(); }
-void geTabAudio::cb_showInputInfo(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_showInputInfo(); }
-void geTabAudio::cb_showOutputInfo(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_showOutputInfo(); }
-
-
/* -------------------------------------------------------------------------- */
-
-
-void geTabAudio::cb_fetchInChans()
-{
- fetchInChans(sounddevIn->value());
- channelsIn->value(0);
-}
-
-
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::cb_fetchOutChans()
+geTabAudio::geChannelMenu::geChannelMenu(int x, int y, int w, int h, const char* l, c::config::AudioDeviceData& data)
+: geChoice(x, y, w, h, l)
+, m_data(data)
{
- fetchOutChans();
- channelsOut->value(0);
}
-
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::cb_showInputInfo()
+int geTabAudio::geChannelMenu::getChannelsCount() const
{
- unsigned dev = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
- new v::gdDevInfo(dev);
+ return getSelectedId() < STEREO_OFFSET ? 1 : 2;
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void geTabAudio::cb_showOutputInfo()
+int geTabAudio::geChannelMenu::getChannelsStart() const
{
- unsigned dev = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- new v::gdDevInfo(dev);
+ if (m_data.channelsCount == 1)
+ return getSelectedId();
+ return getSelectedId() < STEREO_OFFSET ? getSelectedId() : getSelectedId() - STEREO_OFFSET;
}
-
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::cb_deactivate_sounddev()
+void geTabAudio::geChannelMenu::rebuild(c::config::AudioDeviceData& data)
{
- /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
- * If it returns to the original sound system, we re-fill the list by
- * querying m::kernelAudio. Watch out if soundsysInitValue == 0: you don't want
- * to query m::kernelAudio for '(none)' soundsystem! */
+ m_data = data;
- if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
- sounddevOut->clear();
- sounddevIn->clear();
+ clear();
- fetchSoundDevs();
-
- /* the '?' button is added by fetchSoundDevs */
+ if (m_data.index == -1)
+ {
+ addItem("none", 0);
+ showItem(0);
+ return;
+ }
- fetchOutChans();
- sounddevOut->activate();
- channelsOut->activate();
+ if (m_data.type == c::config::DeviceType::INPUT)
+ for (int i = 0; i < m_data.channelsMax; i++)
+ addItem(std::to_string(i + 1), i);
- /* chan menus and '?' button are activated by fetchInChans(...) */
+ /* Dirty trick for stereo channels: they start at STEREO_OFFSET. */
- fetchInChans(sounddevIn->value());
- sounddevIn->activate();
- samplerate->activate();
- }
- else {
- sounddevOut->deactivate();
- sounddevOut->clear();
- sounddevOut->add("-- restart to fetch device(s) --");
- sounddevOut->value(0);
- channelsOut->deactivate();
- devOutInfo->deactivate();
- samplerate->deactivate();
+ for (int i = 0; i < m_data.channelsMax; i += 2)
+ addItem(std::to_string(i + 1) + "-" + std::to_string(i + 2), i + STEREO_OFFSET);
- sounddevIn->deactivate();
- sounddevIn->clear();
- sounddevIn->add("-- restart to fetch device(s) --");
- sounddevIn->value(0);
- channelsIn->deactivate();
- devInInfo->deactivate();
- }
+ if (m_data.channelsCount == 1)
+ showItem(m_data.channelsStart);
+ else
+ showItem(m_data.channelsStart + STEREO_OFFSET);
}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::fetchInChans(int menuItem)
+geTabAudio::geTabAudio(int X, int Y, int W, int H)
+: Fl_Group(X, Y, W, H, "Sound System")
+, m_data(c::config::getAudioData())
+, m_initialApi(m_data.api)
{
- /* if menuItem==0 device in input is disabled. */
-
- if (menuItem == 0) {
- devInInfo->deactivate();
- channelsIn->deactivate();
- recTriggerLevel->deactivate();
- return;
- }
-
- devInInfo->activate();
- channelsIn->activate();
- recTriggerLevel->activate();
-
- channelsIn->clear();
+ begin();
+ soundsys = new geChoice(x() + 114, y() + 9, 250, 20, "System");
+ buffersize = new geChoice(x() + 114, y() + 37, 55, 20, "Buffer size");
+ samplerate = new geChoice(x() + 304, y() + 37, 60, 20, "Sample rate");
+ sounddevOut = new geDeviceMenu(x() + 114, y() + 65, 250, 20, "Output device", m_data.outputDevices);
+ channelsOut = new geChannelMenu(x() + 114, y() + 93, 55, 20, "Output channels", m_data.outputDevice);
+ limitOutput = new geCheck(x() + 177, y() + 93, 100, 20, "Limit output");
+ sounddevIn = new geDeviceMenu(x() + 114, y() + 121, 234, 20, "Input device", m_data.inputDevices);
+ enableIn = new geCheck(sounddevIn->x() + sounddevIn->w() + 4, sounddevIn->y(), 12, 20);
+ channelsIn = new geChannelMenu(x() + 114, y() + 149, 55, 20, "Input channels", m_data.inputDevice);
+ recTriggerLevel = new geInput(x() + 309, y() + 149, 55, 20, "Rec threshold (dB)");
+ rsmpQuality = new geChoice(x() + 114, y() + 177, 250, 20, "Resampling");
+ new geBox(x(), rsmpQuality->y() + rsmpQuality->h() + 8, w(), 92, "Restart Giada for the changes to take effect.");
+ end();
- unsigned dev = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
- unsigned chs = m::kernelAudio::getMaxInChans(dev);
+ labelsize(G_GUI_FONT_SIZE_BASE);
+ selection_color(G_COLOR_GREY_4);
- if (chs == 0) {
- channelsIn->add("none");
- channelsIn->value(0);
- return;
- }
+ for (const auto& [key, value] : m_data.apis)
+ soundsys->addItem(value.c_str(), key);
+ soundsys->showItem(m_data.api);
+ soundsys->onChange = [this](ID id) { m_data.api = id; invalidate(); };
+
+ samplerate->onChange = [this](ID id) { m_data.sampleRate = id; };
+
+ sounddevOut->showItem(m_data.outputDevice.index);
+ sounddevOut->onChange = [this](ID id) { m_data.setOutputDevice(id); fetch(); };
+
+ sounddevIn->showItem(m_data.inputDevice.index);
+ sounddevIn->onChange = [this](ID id) { m_data.setInputDevice(id); fetch(); };
+
+ enableIn->copy_tooltip("Enable Input");
+ enableIn->value(m_data.inputDevice.index != -1);
+ enableIn->onChange = [this](bool b) { m_data.setInputDevice(b ? 0 : -1); fetch(); };
+
+ channelsOut->onChange = [this](ID) {
+ m_data.outputDevice.channelsCount = channelsOut->getChannelsCount();
+ m_data.outputDevice.channelsStart = channelsOut->getChannelsStart();
+ };
+
+ channelsIn->onChange = [this](ID) {
+ m_data.inputDevice.channelsCount = channelsIn->getChannelsCount();
+ m_data.inputDevice.channelsStart = channelsIn->getChannelsStart();
+ };
+
+ limitOutput->value(m_data.limitOutput);
+ limitOutput->onChange = [this](bool v) { m_data.limitOutput = v; };
+
+ buffersize->addItem("8", 8);
+ buffersize->addItem("16", 16);
+ buffersize->addItem("32", 32);
+ buffersize->addItem("64", 64);
+ buffersize->addItem("128", 128);
+ buffersize->addItem("256", 256);
+ buffersize->addItem("512", 512);
+ buffersize->addItem("1024", 1024);
+ buffersize->addItem("2048", 2048);
+ buffersize->addItem("4096", 4096);
+ buffersize->showItem(m_data.bufferSize);
+ buffersize->onChange = [this](ID id) { m_data.bufferSize = id; };
+
+ rsmpQuality->addItem("Sinc best quality (very slow)", 0);
+ rsmpQuality->addItem("Sinc medium quality (slow)", 1);
+ rsmpQuality->addItem("Sinc basic quality (medium)", 2);
+ rsmpQuality->addItem("Zero Order Hold (fast)", 3);
+ rsmpQuality->addItem("Linear (very fast)", 4);
+ rsmpQuality->showItem(m_data.resampleQuality);
+ rsmpQuality->onChange = [this](ID id) { m_data.resampleQuality = id; };
+
+ recTriggerLevel->value(u::string::fToString(m_data.recTriggerLevel, 1).c_str());
+ recTriggerLevel->onChange = [this](const std::string& s) { m_data.recTriggerLevel = std::stof(s); };
+
+ if (m_data.api == G_SYS_API_NONE)
+ deactivateAll();
+ else
+ fetch();
+}
- /* Dirty trick for stereo inputs: indexes start from 1000. */
+/* -------------------------------------------------------------------------- */
- for (unsigned i = 0; i < chs; i++)
- channelsIn->addItem(std::to_string(i + 1).c_str(), i + 1);
- for (unsigned i = 0; i < chs; i += 2)
- channelsIn->addItem((std::to_string(i + 1) + "-" + std::to_string(i + 2)).c_str(), i + 1001);
+void geTabAudio::invalidate()
+{
+ /* If the user changes sound system (e.g. ALSA->JACK), deactivate all widgets. */
- if (m::conf::conf.channelsInCount == 1)
- channelsIn->showItem(m::conf::conf.channelsInStart + 1);
+ if (m_initialApi == m_data.api && m_initialApi != -1 && m_data.api != G_SYS_API_NONE)
+ activateAll();
else
- channelsIn->showItem(m::conf::conf.channelsInStart + 1001);
+ deactivateAll();
}
-
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::fetchOutChans()
+void geTabAudio::fetch()
{
- channelsOut->clear();
+ for (int sampleRate : m_data.outputDevice.sampleRates)
+ samplerate->addItem(std::to_string(sampleRate), sampleRate);
+ samplerate->showItem(m_data.sampleRate);
- unsigned dev = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- unsigned chs = m::kernelAudio::getMaxOutChans(dev);
+ channelsOut->rebuild(m_data.outputDevice);
+ m_data.outputDevice.channelsCount = channelsOut->getChannelsCount();
+ m_data.outputDevice.channelsStart = channelsOut->getChannelsStart();
- if (chs == 0) {
- channelsOut->add("none");
- channelsOut->value(0);
- return;
+ if (m_data.api == G_SYS_API_JACK)
+ buffersize->deactivate();
+ else
+ buffersize->activate();
+
+ if (m_data.inputDevice.index != -1)
+ {
+ channelsIn->rebuild(m_data.inputDevice);
+ m_data.inputDevice.channelsCount = channelsIn->getChannelsCount();
+ m_data.inputDevice.channelsStart = channelsIn->getChannelsStart();
+ sounddevIn->activate();
+ channelsIn->activate();
+ recTriggerLevel->activate();
}
- for (unsigned i=0; i<chs; i+=2) {
- std::string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
- channelsOut->add(tmp.c_str());
+ else
+ {
+ sounddevIn->deactivate();
+ channelsIn->deactivate();
+ recTriggerLevel->deactivate();
}
- channelsOut->value(m::conf::conf.channelsOut);
}
-
/* -------------------------------------------------------------------------- */
-
-int geTabAudio::findMenuDevice(geChoice *m, int device)
+void geTabAudio::deactivateAll()
{
- if (device == -1 || !m::kernelAudio::isReady())
- return 0;
-
- for (int i=0; i<m->size(); i++) {
- if (m::kernelAudio::getDeviceName(device) == "")
- continue;
- if (m->text(i) == nullptr)
- continue;
- if (m->text(i) == m::kernelAudio::getDeviceName(device))
- return i;
- }
-
- return 0;
+ buffersize->deactivate();
+ limitOutput->deactivate();
+ sounddevOut->deactivate();
+ channelsOut->deactivate();
+ samplerate->deactivate();
+ sounddevIn->deactivate();
+ channelsIn->deactivate();
+ recTriggerLevel->deactivate();
+ rsmpQuality->deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
-void geTabAudio::fetchSoundDevs()
+void geTabAudio::activateAll()
{
- if (m::kernelAudio::countDevices() == 0) {
- sounddevOut->add("-- no devices found --");
- sounddevOut->value(0);
- sounddevIn->add("-- no devices found --");
- sounddevIn->value(0);
- devInInfo ->deactivate();
- devOutInfo->deactivate();
- }
- else {
-
- devInInfo ->activate();
- devOutInfo->activate();
-
- /* input device may be disabled: now device number -1 is the disabled
- * one. KernelAudio knows how to handle it. */
-
- sounddevIn->add("(disabled)");
-
- for (unsigned i=0; i<m::kernelAudio::countDevices(); i++) {
-
- /* escaping '/', very dangerous in FLTK (it creates a submenu) */
-
- std::string tmp = m::kernelAudio::getDeviceName(i);
- for (unsigned k=0; k<tmp.size(); k++)
- if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
- tmp[k] = '-';
-
- /* add to list devices with at least 1 channel available. In this
- * way we can filter devices only for input or output, e.g. an input
- * devices has 0 output channels. */
-
- if (m::kernelAudio::getMaxOutChans(i) > 0)
- sounddevOut->add(tmp.c_str());
-
- if (m::kernelAudio::getMaxInChans(i) > 0)
- sounddevIn->add(tmp.c_str());
- }
-
- /* we show the device saved in the configuration file. */
-
- if (sounddevOut->size() == 0) {
- sounddevOut->add("-- no devices found --");
- sounddevOut->value(0);
- devOutInfo->deactivate();
- }
- else {
- int outMenuValue = findMenuDevice(sounddevOut, m::conf::conf.soundDeviceOut);
- sounddevOut->value(outMenuValue);
- }
-
- if (sounddevIn->size() == 0) {
- sounddevIn->add("-- no devices found --");
- sounddevIn->value(0);
- devInInfo->deactivate();
- }
- else {
- int inMenuValue = findMenuDevice(sounddevIn, m::conf::conf.soundDeviceIn);
- sounddevIn->value(inMenuValue);
- }
+ buffersize->activate();
+ limitOutput->activate();
+ sounddevOut->activate();
+ channelsOut->activate();
+ samplerate->activate();
+ rsmpQuality->activate();
+ if (m_data.inputDevice.index != -1)
+ {
+ sounddevIn->activate();
+ channelsIn->activate();
+ recTriggerLevel->activate();
}
}
-
/* -------------------------------------------------------------------------- */
-
void geTabAudio::save()
{
- std::string text = soundsys->text(soundsys->value());
-
- if (text == "(none)") {
- m::conf::conf.soundSystem = G_SYS_API_NONE;
- return;
- }
-
-#if defined(__linux__)
-
- else if (text == "ALSA")
- m::conf::conf.soundSystem = G_SYS_API_ALSA;
- else if (text == "Jack")
- m::conf::conf.soundSystem = G_SYS_API_JACK;
- else if (text == "PulseAudio")
- m::conf::conf.soundSystem = G_SYS_API_PULSE;
-
-#elif defined(__FreeBSD__)
-
- else if (text == "Jack")
- m::conf::conf.soundSystem = G_SYS_API_JACK;
- else if (text == "PulseAudio")
- m::conf::conf.soundSystem = G_SYS_API_PULSE;
-
-#elif defined(_WIN32)
-
- else if (text == "DirectSound")
- m::conf::conf.soundSystem = G_SYS_API_DS;
- else if (text == "ASIO")
- m::conf::conf.soundSystem = G_SYS_API_ASIO;
- else if (text == "WASAPI")
- m::conf::conf.soundSystem = G_SYS_API_WASAPI;
-
-#elif defined(__APPLE__)
-
- else if (text == "CoreAudio")
- m::conf::conf.soundSystem = G_SYS_API_CORE;
-
-#endif
-
- /* use the device name to search into the drop down menu's */
-
- m::conf::conf.soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- m::conf::conf.soundDeviceIn = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
- m::conf::conf.channelsOut = channelsOut->value();
- m::conf::conf.channelsInCount = channelsIn->getSelectedId() < 1000 ? 1 : 2;
- m::conf::conf.channelsInStart = channelsIn->getSelectedId() - (m::conf::conf.channelsInCount == 1 ? 1 : 1001);
- m::conf::conf.limitOutput = limitOutput->value();
- m::conf::conf.rsmpQuality = rsmpQuality->value();
-
- /* If sounddevOut is disabled because of system change e.g. alsa -> jack,
- soundDeviceOut and channelsOut are == -1. Change them! */
-
- if (m::conf::conf.soundDeviceOut == -1) {
- m::conf::conf.soundDeviceOut = 0;
- m::conf::conf.channelsOut = 0;
- }
-
- m::conf::conf.buffersize = std::atoi(buffersize->text());
- m::conf::conf.recTriggerLevel = std::atof(recTriggerLevel->value());
-
- const Fl_Menu_Item* i = nullptr;
- i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
- if (i != nullptr)
- m::conf::conf.samplerate = std::atoi(i->label());
+ c::config::save(m_data);
}
-}} // giada::v::
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_TAB_AUDIO_H
#define GE_TAB_AUDIO_H
-
+#include "glue/config.h"
+#include "gui/elems/basics/choice.h"
#include <FL/Fl_Group.H>
-
class geCheck;
class geButton;
class geInput;
-
-namespace giada {
-namespace v
+namespace giada::v
{
-class geChoice;
class geTabAudio : public Fl_Group
{
public:
+ struct geDeviceMenu : public geChoice
+ {
+ geDeviceMenu(int x, int y, int w, int h, const char* l, const std::vector<c::config::AudioDeviceData>&);
+ };
+
+ struct geChannelMenu : public geChoice
+ {
+ geChannelMenu(int x, int y, int w, int h, const char* l, c::config::AudioDeviceData&);
+
+ int getChannelsCount() const;
+ int getChannelsStart() const;
+
+ void rebuild(c::config::AudioDeviceData&);
+
+ private:
+ static constexpr int STEREO_OFFSET = 1000;
+
+ c::config::AudioDeviceData& m_data;
+ };
geTabAudio(int x, int y, int w, int h);
void save();
- geChoice* soundsys;
- geChoice* buffersize;
- geChoice* samplerate;
- geChoice* sounddevOut;
- geButton* devOutInfo;
- geChoice* channelsOut;
- geCheck* limitOutput;
- geChoice* sounddevIn;
- geButton* devInInfo;
- geChoice* channelsIn;
- geInput* recTriggerLevel;
- geChoice* rsmpQuality;
+ geChoice* soundsys;
+ geChoice* buffersize;
+ geChoice* samplerate;
+ geDeviceMenu* sounddevOut;
+ geChannelMenu* channelsOut;
+ geCheck* limitOutput;
+ geDeviceMenu* sounddevIn;
+ geCheck* enableIn;
+ geChannelMenu* channelsIn;
+ geInput* recTriggerLevel;
+ geChoice* rsmpQuality;
private:
+ void invalidate();
+ void fetch();
+ void deactivateAll();
+ void activateAll();
- static void cb_deactivate_sounddev(Fl_Widget* /*w*/, void* p);
- static void cb_fetchInChans (Fl_Widget* /*w*/, void* p);
- static void cb_fetchOutChans (Fl_Widget* /*w*/, void* p);
- static void cb_showInputInfo (Fl_Widget* /*w*/, void* p);
- static void cb_showOutputInfo (Fl_Widget* /*w*/, void* p);
- void cb_deactivate_sounddev();
- void cb_fetchInChans();
- void cb_fetchOutChans();
- void cb_showInputInfo();
- void cb_showOutputInfo();
-
- void fetchSoundDevs();
- void fetchInChans(int menuItem);
- void fetchOutChans();
- int findMenuDevice(geChoice* m, int device);
-
- int soundsysInitValue;
-};
-}} // giada::v::
+ c::config::AudioData m_data;
+ int m_initialApi;
+};
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl_Pack.H>
-#include "core/const.h"
+#include "tabBehaviors.h"
#include "core/conf.h"
+#include "core/const.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/check.h"
-#include "tabBehaviors.h"
-
+#include <FL/Fl_Pack.H>
-namespace giada {
+namespace giada
+{
namespace v
{
-geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
-: Fl_Group (X, Y, W, H)
-, m_container (X, Y + G_GUI_OUTER_MARGIN, Direction::VERTICAL, G_GUI_OUTER_MARGIN)
-, m_chansStopOnSeqHalt (0, 0, 280, 30, "Dynamic channels stop immediately when the sequencer\nis halted")
-, m_treatRecsAsLoops (0, 0, 280, 20, "Treat one shot channels with actions as loops")
-, m_inputMonitorDefaultOn (0, 0, 280, 20, "New sample channels have input monitor on by default")
+geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
+: Fl_Group(X, Y, W, H)
+, m_container(X, Y + G_GUI_OUTER_MARGIN, Direction::VERTICAL, G_GUI_OUTER_MARGIN)
+, m_chansStopOnSeqHalt(0, 0, 280, 30, "Dynamic channels stop immediately when the sequencer\nis halted")
+, m_treatRecsAsLoops(0, 0, 280, 20, "Treat one shot channels with actions as loops")
+, m_inputMonitorDefaultOn(0, 0, 280, 20, "New sample channels have input monitor on by default")
, m_overdubProtectionDefaultOn(0, 0, 280, 30, "New sample channels have overdub protection on\nby default")
{
end();
m_overdubProtectionDefaultOn.value(m::conf::conf.overdubProtectionDefaultOn);
}
-
/* -------------------------------------------------------------------------- */
-
void geTabBehaviors::save()
{
- m::conf::conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt.value();
- m::conf::conf.treatRecsAsLoops = m_treatRecsAsLoops.value();
- m::conf::conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn.value();
+ m::conf::conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt.value();
+ m::conf::conf.treatRecsAsLoops = m_treatRecsAsLoops.value();
+ m::conf::conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn.value();
m::conf::conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn.value();
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_TAB_BEHAVIORS_H
#define GE_TAB_BEHAVIORS_H
-
-#include <FL/Fl_Group.H>
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/pack.h"
+#include <FL/Fl_Group.H>
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geTabBehaviors : public Fl_Group
{
public:
-
geTabBehaviors(int x, int y, int w, int h);
void save();
-private:
-
+ private:
gePack m_container;
geCheck m_chansStopOnSeqHalt;
geCheck m_treatRecsAsLoops;
geCheck m_inputMonitorDefaultOn;
geCheck m_overdubProtectionDefaultOn;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <RtMidi.h>
-#include <string>
-#include "core/const.h"
+#include "tabMidi.h"
#include "core/conf.h"
-#include "core/midiMapConf.h"
+#include "core/const.h"
#include "core/kernelMidi.h"
-#include "utils/gui.h"
+#include "core/midiMapConf.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/check.h"
-#include "tabMidi.h"
-
+#include "gui/elems/basics/choice.h"
+#include "utils/gui.h"
+#include <RtMidi.h>
+#include <string>
-namespace giada {
+namespace giada
+{
namespace v
{
geTabMidi::geTabMidi(int X, int Y, int W, int H)
: Fl_Group(X, Y, W, H, "MIDI")
{
begin();
- system = new geChoice(x()+w()-250, y()+9, 250, 20, "System");
- portOut = new geChoice(x()+w()-250, system->y()+system->h()+8, 250, 20, "Output port");
- portIn = new geChoice(x()+w()-250, portOut->y()+portOut->h()+8, 250, 20, "Input port");
- midiMap = new geChoice(x()+w()-250, portIn->y()+portIn->h()+8, 250, 20, "Output Midi Map");
- sync = new geChoice(x()+w()-250, midiMap->y()+midiMap->h()+8, 250, 20, "Sync");
- new geBox(x(), sync->y()+sync->h()+8, w(), h()-150, "Restart Giada for the changes to take effect.");
+ system = new geChoice(x() + w() - 250, y() + 9, 250, 20, "System");
+ portOut = new geChoice(x() + w() - 250, system->y() + system->h() + 8, 250, 20, "Output port");
+ portIn = new geChoice(x() + w() - 250, portOut->y() + portOut->h() + 8, 250, 20, "Input port");
+ midiMap = new geChoice(x() + w() - 250, portIn->y() + portIn->h() + 8, 250, 20, "Output Midi Map");
+ sync = new geChoice(x() + w() - 250, midiMap->y() + midiMap->h() + 8, 250, 20, "Sync");
+ new geBox(x(), sync->y() + sync->h() + 8, w(), h() - 150, "Restart Giada for the changes to take effect.");
end();
labelsize(G_GUI_FONT_SIZE_BASE);
sync->add("(disabled)");
sync->add("MIDI Clock (master)");
sync->add("MTC (master)");
- if (m::conf::conf.midiSync == MIDI_SYNC_NONE)
+ if (m::conf::conf.midiSync == MIDI_SYNC_NONE)
sync->value(0);
else if (m::conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
sync->value(1);
systemInitValue = system->value();
}
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::fetchOutPorts()
{
- if (m::kernelMidi::countOutPorts() == 0) {
+ if (m::kernelMidi::countOutPorts() == 0)
+ {
portOut->add("-- no ports found --");
portOut->value(0);
portOut->deactivate();
}
- else {
+ else
+ {
portOut->add("(disabled)");
- for (unsigned i=0; i<m::kernelMidi::countOutPorts(); i++)
+ for (unsigned i = 0; i < m::kernelMidi::countOutPorts(); i++)
portOut->add(u::gui::removeFltkChars(m::kernelMidi::getOutPortName(i)).c_str());
- portOut->value(m::conf::conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)'
+ portOut->value(m::conf::conf.midiPortOut + 1); // +1 because midiPortOut=-1 is '(disabled)'
}
}
/* -------------------------------------------------------------------------- */
-
void geTabMidi::fetchInPorts()
{
- if (m::kernelMidi::countInPorts() == 0) {
+ if (m::kernelMidi::countInPorts() == 0)
+ {
portIn->add("-- no ports found --");
portIn->value(0);
portIn->deactivate();
}
- else {
+ else
+ {
portIn->add("(disabled)");
- for (unsigned i=0; i<m::kernelMidi::countInPorts(); i++)
+ for (unsigned i = 0; i < m::kernelMidi::countInPorts(); i++)
portIn->add(u::gui::removeFltkChars(m::kernelMidi::getInPortName(i)).c_str());
- portIn->value(m::conf::conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)'
+ portIn->value(m::conf::conf.midiPortIn + 1); // +1 because midiPortIn=-1 is '(disabled)'
}
}
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::fetchMidiMaps()
{
- if (m::midimap::maps.size() == 0) {
+ if (m::midimap::maps.size() == 0)
+ {
midiMap->add("(no MIDI maps available)");
midiMap->value(0);
midiMap->deactivate();
return;
}
- for (unsigned i=0; i<m::midimap::maps.size(); i++) {
- const char *imap = m::midimap::maps.at(i).c_str();
+ for (unsigned i = 0; i < m::midimap::maps.size(); i++)
+ {
+ const char* imap = m::midimap::maps.at(i).c_str();
midiMap->add(imap);
if (m::conf::conf.midiMapPath == imap)
midiMap->value(i);
midiMap->value(0);
}
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::save()
{
std::string text = system->text(system->value());
- if (text == "ALSA")
+ if (text == "ALSA")
m::conf::conf.midiSystem = RtMidi::LINUX_ALSA;
else if (text == "Jack")
m::conf::conf.midiSystem = RtMidi::UNIX_JACK;
else if (text == "OSX Core MIDI")
m::conf::conf.midiSystem = RtMidi::MACOSX_CORE;
- m::conf::conf.midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)'
- m::conf::conf.midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)'
+ m::conf::conf.midiPortOut = portOut->value() - 1; // -1 because midiPortOut=-1 is '(disabled)'
+ m::conf::conf.midiPortIn = portIn->value() - 1; // -1 because midiPortIn=-1 is '(disabled)'
m::conf::conf.midiMapPath = m::midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
- if (sync->value() == 0)
+ if (sync->value() == 0)
m::conf::conf.midiSync = MIDI_SYNC_NONE;
else if (sync->value() == 1)
m::conf::conf.midiSync = MIDI_SYNC_CLOCK_M;
m::conf::conf.midiSync = MIDI_SYNC_MTC_M;
}
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::fetchSystems()
{
#if defined(__linux__)
if (m::kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
system->add("Multimedia MIDI");
-#elif defined (__APPLE__)
+#elif defined(__APPLE__)
system->add("OSX Core MIDI");
#endif
- switch (m::conf::conf.midiSystem) {
- case RtMidi::LINUX_ALSA: system->showItem("ALSA"); break;
- case RtMidi::UNIX_JACK: system->showItem("Jack"); break;
- case RtMidi::WINDOWS_MM: system->showItem("Multimedia MIDI"); break;
- case RtMidi::MACOSX_CORE: system->showItem("OSX Core MIDI"); break;
- default: system->value(0); break;
+ switch (m::conf::conf.midiSystem)
+ {
+ case RtMidi::LINUX_ALSA:
+ system->showItem("ALSA");
+ break;
+ case RtMidi::UNIX_JACK:
+ system->showItem("Jack");
+ break;
+ case RtMidi::WINDOWS_MM:
+ system->showItem("Multimedia MIDI");
+ break;
+ case RtMidi::MACOSX_CORE:
+ system->showItem("OSX Core MIDI");
+ break;
+ default:
+ system->value(0);
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::cb_changeSystem(Fl_Widget* /*w*/, void* p) { ((geTabMidi*)p)->cb_changeSystem(); }
-
/* -------------------------------------------------------------------------- */
-
void geTabMidi::cb_changeSystem()
{
/* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
* If it returns to the original system, we re-fill the list by
* querying m::kernelMidi. */
- if (systemInitValue == system->value()) {
+ if (systemInitValue == system->value())
+ {
portOut->clear();
fetchOutPorts();
portOut->activate();
portIn->activate();
sync->activate();
}
- else {
+ else
+ {
portOut->deactivate();
portOut->clear();
portOut->add("-- restart to fetch device(s) --");
sync->deactivate();
}
}
-}} // giada::v::
\ No newline at end of file
+} // namespace v
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_TAB_MIDI_H
#define GE_TAB_MIDI_H
-
#include <FL/Fl_Group.H>
-
class geCheck;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geChoice;
class geTabMidi : public Fl_Group
{
public:
-
geTabMidi(int x, int y, int w, int h);
void save();
geChoice* midiMap;
geChoice* sync;
-private:
-
+ private:
void fetchSystems();
void fetchOutPorts();
void fetchInPorts();
void fetchMidiMaps();
static void cb_changeSystem(Fl_Widget* /*w*/, void* p);
- void cb_changeSystem();
+ void cb_changeSystem();
int systemInitValue;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/const.h"
-#include "core/conf.h"
-#include "gui/elems/basics/choice.h"
#include "tabMisc.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include <FL/Fl_Tooltip.H>
-
-namespace giada {
-namespace v
+namespace giada::v
{
geTabMisc::geTabMisc(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "Misc")
+: geGroup(X, Y)
+, m_debugMsg(W - 230, 9, 230, 20, "Debug messages")
+, m_tooltips(W - 230, 37, 230, 20, "Tooltips")
{
- begin();
- debugMsg = new geChoice(x()+w()-230, y()+9, 230, 20, "Debug messages");
- end();
+ add(&m_debugMsg);
+ add(&m_tooltips);
- debugMsg->add("(disabled)");
- debugMsg->add("To standard output");
- debugMsg->add("To file");
+ m_debugMsg.add("Disabled");
+ m_debugMsg.add("To standard output");
+ m_debugMsg.add("To file");
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ m_tooltips.add("Disabled");
+ m_tooltips.add("Enabled");
- switch (m::conf::conf.logMode) {
- case LOG_MODE_MUTE:
- debugMsg->value(0);
- break;
- case LOG_MODE_STDOUT:
- debugMsg->value(1);
- break;
- case LOG_MODE_FILE:
- debugMsg->value(2);
- break;
+ switch (m::conf::conf.logMode)
+ {
+ case LOG_MODE_MUTE:
+ m_debugMsg.value(0);
+ break;
+ case LOG_MODE_STDOUT:
+ m_debugMsg.value(1);
+ break;
+ case LOG_MODE_FILE:
+ m_debugMsg.value(2);
+ break;
}
-}
+ m_tooltips.value(m::conf::conf.showTooltips);
-/* -------------------------------------------------------------------------- */
+ copy_label("Misc");
+ labelsize(G_GUI_FONT_SIZE_BASE);
+ selection_color(G_COLOR_GREY_4);
+}
+/* -------------------------------------------------------------------------- */
void geTabMisc::save()
{
- switch(debugMsg->value()) {
- case 0:
- m::conf::conf.logMode = LOG_MODE_MUTE;
- break;
- case 1:
- m::conf::conf.logMode = LOG_MODE_STDOUT;
- break;
- case 2:
- m::conf::conf.logMode = LOG_MODE_FILE;
- break;
+ switch (m_debugMsg.value())
+ {
+ case 0:
+ m::conf::conf.logMode = LOG_MODE_MUTE;
+ break;
+ case 1:
+ m::conf::conf.logMode = LOG_MODE_STDOUT;
+ break;
+ case 2:
+ m::conf::conf.logMode = LOG_MODE_FILE;
+ break;
}
+
+ m::conf::conf.showTooltips = m_tooltips.value();
+ Fl_Tooltip::enable(m_tooltips.value());
}
-}} // giada::v::
\ No newline at end of file
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_TAB_MISC_H
#define GE_TAB_MISC_H
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/group.h"
-#include <FL/Fl_Group.H>
-
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geChoice;
-class geTabMisc : public Fl_Group
+class geTabMisc : public geGroup
{
public:
-
geTabMisc(int x, int y, int w, int h);
void save();
- geChoice* debugMsg;
+ private:
+ geChoice m_debugMsg;
+ geChoice m_tooltips;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <functional>
-#include <FL/Fl.H>
-#include "core/const.h"
+#include "tabPlugins.h"
#include "core/conf.h"
+#include "core/const.h"
#include "core/graphics.h"
#include "core/plugins/pluginManager.h"
#include "glue/plugin.h"
-#include "utils/string.h"
-#include "utils/fs.h"
-#include "utils/gui.h"
-#include "gui/dialogs/window.h"
-#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/browser/browserDir.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/window.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/button.h"
-#include "tabPlugins.h"
-
+#include "utils/fs.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
+#include <functional>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "Plugins")
+: Fl_Group(X, Y, W, H, "Plugins")
{
- m_browse = new geButton(x()+w()-G_GUI_UNIT, y()+9, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
- m_folderPath = new geInput(m_browse->x()-258, y()+9, 250, G_GUI_UNIT);
- m_scanButton = new geButton(x()+w()-150, m_folderPath->y()+m_folderPath->h()+8, 150, G_GUI_UNIT);
- m_info = new geBox(x(), m_scanButton->y()+m_scanButton->h()+8, w(), 240);
+ m_browse = new geButton(x() + w() - G_GUI_UNIT, y() + 9, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+ m_folderPath = new geInput(m_browse->x() - 258, y() + 9, 250, G_GUI_UNIT);
+ m_scanButton = new geButton(x() + w() - 150, m_folderPath->y() + m_folderPath->h() + 8, 150, G_GUI_UNIT);
+ m_info = new geBox(x(), m_scanButton->y() + m_scanButton->h() + 8, w(), 240);
end();
m_folderPath->value(m::conf::conf.pluginPath.c_str());
m_folderPath->label("Plugins folder");
- m_browse->callback(cb_browse, (void*) this);
+ m_browse->callback(cb_browse, (void*)this);
- m_scanButton->callback(cb_scan, (void*) this);
+ m_scanButton->callback(cb_scan, (void*)this);
refreshCount();
}
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::refreshCount()
{
std::string scanLabel = "Scan (" + std::to_string(m::pluginManager::countAvailablePlugins()) + " found)";
m_scanButton->copy_label(scanLabel.c_str());
}
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::cb_scan(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_scan(); }
void geTabPlugins::cb_browse(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_browse(); }
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::cb_browse()
{
- v::gdBrowserDir* browser = new v::gdBrowserDir("Add plug-ins directory",
- m::conf::conf.patchPath, c::plugin::setPluginPathCb);
+ v::gdBrowserDir* browser = new v::gdBrowserDir("Add plug-ins directory",
+ m::conf::conf.patchPath, c::plugin::setPluginPathCb);
static_cast<v::gdWindow*>(top_window())->addSubWindow(browser);
}
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::cb_scan()
{
- std::function<void(float)> callback = [this] (float progress)
- {
- std::string l = "Scan in progress (" + std::to_string((int)(progress*100)) + "%). Please wait...";
+ std::function<void(float)> callback = [this](float progress) {
+ std::string l = "Scan in progress (" + std::to_string((int)(progress * 100)) + "%). Please wait...";
m_info->label(l.c_str());
Fl::wait();
};
refreshCount();
}
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::save()
{
m::conf::conf.pluginPath = m_folderPath->value();
}
-
/* -------------------------------------------------------------------------- */
-
void geTabPlugins::refreshVstPath()
{
m_folderPath->value(m::conf::conf.pluginPath.c_str());
m_folderPath->redraw();
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_TAB_PLUGINS_H
#define GE_TAB_PLUGINS_H
-
#ifdef WITH_VST
-
#include <FL/Fl_Group.H>
-
class geInput;
class geButton;
class geBox;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geTabPlugins : public Fl_Group
{
public:
-
geTabPlugins(int x, int y, int w, int h);
void save();
void refreshVstPath();
-private:
-
+ private:
static void cb_scan(Fl_Widget* /*w*/, void* p);
static void cb_browse(Fl_Widget* /*w*/, void* p);
- void cb_scan();
- void cb_browse();
+ void cb_scan();
+ void cb_browse();
void refreshCount();
geButton* m_scanButton;
geBox* m_info;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // WITH_VST
-
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * beatMeter
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "core/const.h"
-#include "core/recManager.h"
-#include "core/mixer.h"
-#include "core/clock.h"
-#include "utils/gui.h"
-#include "beatMeter.h"
-
-
-namespace giada {
-namespace v
-{
-geBeatMeter::geBeatMeter(int x, int y, int w, int h)
-: Fl_Box(x, y, w, h)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Fl_Color geBeatMeter::getCursorColor()
-{
- if (m::clock::getStatus() == ClockStatus::WAITING && u::gui::shouldBlink())
- return FL_BACKGROUND_COLOR;
- return G_COLOR_LIGHT_1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBeatMeter::refresh()
-{
- redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBeatMeter::draw()
-{
- using namespace giada::m;
-
- int cursorW = w() / G_MAX_BEATS;
- int greyX = clock::getBeats() * cursorW;
-
- /* Border and background. */
-
- fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);
- fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR);
-
- /* Cursor. */
-
- fl_rectf(x() + (clock::getCurrentBeat() * cursorW) + 3, y() + 3, cursorW - 5, h() - 6, getCursorColor());
-
- /* Beat cells. */
-
- fl_color(G_COLOR_GREY_4);
- for (int i=1; i<=clock::getBeats(); i++)
- fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2);
-
- /* Bar line. */
-
- fl_color(G_COLOR_LIGHT_1);
- int delta = clock::getBeats() / clock::getBars();
- for (int i=1; i<clock::getBars(); i++)
- fl_line(x()+cursorW*(i*delta), y()+1, x()+cursorW*(i*delta), y()+h()-2);
-
- /* Unused grey area. */
-
- fl_rectf(x()+greyX+1, y()+1, w()-greyX-1, h()-2, G_COLOR_GREY_4);
-}
-}} // giada::v::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * beatMeter
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_BEAT_METER_H
-#define GE_BEAT_METER_H
-
-
-#include <FL/Fl_Box.H>
-
-
-namespace giada {
-namespace v
-{
-class geBeatMeter : public Fl_Box
-{
-public:
-
- geBeatMeter(int x, int y, int w, int h);
-
- void draw() override;
-
- void refresh();
-
-private:
-
- Fl_Color getCursorColor();
-};
-}} // giada::v::
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include "core/model/model.h"
+#include "glue/channel.h"
+#include "channel.h"
+#include "channelButton.h"
+#include "channelStatus.h"
+#include "column.h"
#include "core/const.h"
#include "core/graphics.h"
+#include "core/model/model.h"
#include "core/plugins/pluginHost.h"
-#include "utils/gui.h"
-#include "glue/channel.h"
#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/pluginList.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/statusButton.h"
-#include "column.h"
-#include "channelStatus.h"
-#include "channelButton.h"
-#include "channel.h"
-
+#include "utils/gui.h"
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace v
+namespace giada::v
{
geChannel::geChannel(int X, int Y, int W, int H, c::channel::Data d)
-: Fl_Group (X, Y, W, H)
+: Fl_Group(X, Y, W, H)
, m_channel(d)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::draw()
{
const int ny = y() + (h() / 2) - (G_GUI_UNIT / 2);
fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1_5);
- Fl_Group::draw();
+ Fl_Group::draw();
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::cb_arm(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_arm(); }
void geChannel::cb_mute(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_mute(); }
void geChannel::cb_solo(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_solo(); }
void geChannel::cb_changeVol(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_changeVol(); }
#ifdef WITH_VST
-void geChannel::cb_openFxWindow(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_openFxWindow(); }
+void geChannel::cb_openFxWindow(Fl_Widget* /*w*/, void* p)
+{
+ ((geChannel*)p)->cb_openFxWindow();
+}
#endif
-
/* -------------------------------------------------------------------------- */
-
void geChannel::refresh()
{
- ChannelStatus playStatus = m_channel.a_getPlayStatus();
- ChannelStatus recStatus = m_channel.a_getRecStatus();
+ ChannelStatus playStatus = m_channel.getPlayStatus();
+ ChannelStatus recStatus = m_channel.getRecStatus();
if (mainButton->visible())
mainButton->refresh();
blink();
playButton->setStatus(playStatus == ChannelStatus::PLAY || playStatus == ChannelStatus::ENDING);
- mute->setStatus(m_channel.a_getMute());
- solo->setStatus(m_channel.a_getSolo());
+ mute->setStatus(m_channel.getMute());
+ solo->setStatus(m_channel.getSolo());
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::cb_arm()
{
c::events::toggleArmChannel(m_channel.id, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::cb_mute()
{
c::events::toggleMuteChannel(m_channel.id, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::cb_solo()
{
c::events::toggleSoloChannel(m_channel.id, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::cb_changeVol()
{
c::events::setChannelVolume(m_channel.id, vol->value(), Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
void geChannel::cb_openFxWindow()
{
}
#endif
-
/* -------------------------------------------------------------------------- */
-
-
int geChannel::getColumnId()
{
return static_cast<geColumn*>(parent())->id;
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::blink()
{
if (u::gui::shouldBlink())
mainButton->setDefaultMode();
}
-
/* -------------------------------------------------------------------------- */
-
void geChannel::packWidgets()
{
/* Count visible widgets and resize mainButton according to how many widgets
are visible. */
int visibles = 0;
- for (int i = 0; i < children(); i++) {
- child(i)->size(MIN_ELEM_W, child(i)->h()); // also normalize widths
+ for (int i = 0; i < children(); i++)
+ {
+ child(i)->size(MIN_ELEM_W, child(i)->h()); // also normalize widths
if (child(i)->visible())
visibles++;
}
- mainButton->size(w() - ((visibles - 1) * (MIN_ELEM_W + G_GUI_INNER_MARGIN)), // -1: exclude itself
- mainButton->h());
+ mainButton->size(w() - ((visibles - 1) * (MIN_ELEM_W + G_GUI_INNER_MARGIN)), // -1: exclude itself
+ mainButton->h());
/* Reposition everything else */
- for (int i = 1, p = 0; i < children(); i++) {
+ for (int i = 1, p = 0; i < children(); i++)
+ {
if (!child(i)->visible())
continue;
for (int k = i - 1; k >= 0; k--) // Get the first visible item prior to i
- if (child(k)->visible()) {
+ if (child(k)->visible())
+ {
p = k;
break;
}
init_sizes(); // Resets the internal array of widget sizes and positions
}
-
/* -------------------------------------------------------------------------- */
-
bool geChannel::handleKey(int e)
{
- if (Fl::event_key() != m_channel.key)
+ if (Fl::event_key() != m_channel.key)
return false;
- if (e == FL_KEYDOWN && !playButton->value()) { // Key not already pressed
- playButton->take_focus(); // Move focus to this playButton
+ if (e == FL_KEYDOWN && !playButton->value())
+ { // Key not already pressed
+ playButton->take_focus(); // Move focus to this playButton
playButton->value(1);
return true;
}
- if (e == FL_KEYUP) {
+ if (e == FL_KEYUP)
+ {
playButton->value(0);
return true;
}
return false;
}
-
/* -------------------------------------------------------------------------- */
-
const c::channel::Data& geChannel::getData() const
{
return m_channel;
}
-}} // giada::v::
+} // namespace giada::v
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHANNEL_H
#define GE_CHANNEL_H
-
-#include <FL/Fl_Group.H>
-#include "glue/channel.h"
#include "core/types.h"
+#include "glue/channel.h"
+#include <FL/Fl_Group.H>
-
-class geChannelStatus;
class geButton;
class geDial;
-class geStatusButton;
-
-namespace giada {
-namespace v
+namespace giada::v
{
+class geChannelStatus;
+class geStatusButton;
class geChannelButton;
class geChannel : public Fl_Group
{
public:
-
geChannel(int x, int y, int w, int h, c::channel::Data d);
void draw() override;
Returns a reference to the internal data. Read-only. */
const c::channel::Data& getData() const;
-
+
geStatusButton* playButton;
geButton* arm;
geChannelStatus* status;
geStatusButton* solo;
geDial* vol;
#ifdef WITH_VST
- geStatusButton* fx;
+ geStatusButton* fx;
#endif
protected:
-
/* Define some breakpoints for dynamic resize. BREAK_DELTA: base amount of
pixels to shrink sampleButton. */
c::channel::Data m_channel;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
+#include "channelButton.h"
#include "core/channels/channel.h"
-#include "core/model/model.h"
#include "core/const.h"
+#include "core/model/model.h"
#include "core/recorder.h"
#include "glue/channel.h"
#include "utils/string.h"
-#include "channelButton.h"
-
+#include <FL/fl_draw.H>
-namespace giada {
+namespace giada
+{
namespace v
{
geChannelButton::geChannelButton(int x, int y, int w, int h, const c::channel::Data& d)
-: geButton (x, y, w, h)
+: geButton(x, y, w, h)
, m_channel(d)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::refresh()
{
- switch (m_channel.a_getPlayStatus()) {
- case ChannelStatus::OFF:
- case ChannelStatus::EMPTY:
- setDefaultMode(); break;
- case ChannelStatus::PLAY:
- setPlayMode(); break;
- case ChannelStatus::ENDING:
- setEndingMode(); break;
- default: break;
- }
- switch (m_channel.a_getRecStatus()) {
- case ChannelStatus::ENDING:
- setEndingMode(); break;
- default: break;
+ switch (m_channel.getPlayStatus())
+ {
+ case ChannelStatus::OFF:
+ case ChannelStatus::EMPTY:
+ setDefaultMode();
+ break;
+ case ChannelStatus::PLAY:
+ setPlayMode();
+ break;
+ case ChannelStatus::ENDING:
+ setEndingMode();
+ break;
+ default:
+ break;
+ }
+ switch (m_channel.getRecStatus())
+ {
+ case ChannelStatus::ENDING:
+ setEndingMode();
+ break;
+ default:
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::draw()
{
geButton::draw();
/* draw background */
- fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0);
+ fl_rectf(x() + 1, y() + 1, 18, h() - 2, bgColor0);
/* draw m_key */
fl_draw(std::string(1, static_cast<wchar_t>(m_channel.key)).c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::setInputRecordMode()
{
bgColor0 = G_COLOR_RED;
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::setActionRecordMode()
{
bgColor0 = G_COLOR_BLUE;
txtColor = G_COLOR_LIGHT_2;
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::setDefaultMode(const char* l)
{
bgColor0 = G_COLOR_GREY_2;
label(l);
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::setPlayMode()
{
bgColor0 = G_COLOR_LIGHT_1;
txtColor = G_COLOR_GREY_1;
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelButton::setEndingMode()
{
bgColor0 = G_COLOR_GREY_4;
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHANNEL_BUTTON_H
#define GE_CHANNEL_BUTTON_H
-
#include "gui/elems/basics/button.h"
-
-namespace giada {
-namespace c {
+namespace giada
+{
+namespace c
+{
namespace channel
{
struct Data;
-}}
+}
+} // namespace c
namespace v
{
class geChannelButton : public geButton
{
public:
-
geChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
virtual void refresh();
void draw() override;
-
+
void setPlayMode();
void setEndingMode();
- void setDefaultMode(const char* l=0);
+ void setDefaultMode(const char* l = 0);
void setInputRecordMode();
void setActionRecordMode();
-protected:
-
- const c::channel::Data& m_channel;
+ protected:
+ const c::channel::Data& m_channel;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
- /* -----------------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/fl_draw.H>
-#include "utils/gui.h"
+#include "channelMode.h"
#include "core/channels/channel.h"
#include "core/channels/samplePlayer.h"
-#include "core/model/model.h"
-#include "core/graphics.h"
#include "core/const.h"
+#include "core/graphics.h"
+#include "core/model/model.h"
#include "glue/channel.h"
#include "gui/elems/basics/boxtypes.h"
-#include "channelMode.h"
-
+#include "utils/gui.h"
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
geChannelMode::geChannelMode(int x, int y, int w, int h, c::channel::Data& d)
-: Fl_Menu_Button(x, y, w, h)
-, m_channel (d)
+: Fl_Menu_Button(x, y, w, h)
+, m_channel(d)
{
box(G_CUSTOM_BORDER_BOX);
textsize(G_GUI_FONT_SIZE_BASE);
textcolor(G_COLOR_LIGHT_2);
color(G_COLOR_GREY_2);
- add("Loop . basic", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_BASIC);
- add("Loop . once", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_ONCE);
- add("Loop . once . bar", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_ONCE_BAR);
- add("Loop . repeat", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_REPEAT);
- add("Oneshot . basic", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_BASIC);
- add("Oneshot . press", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_PRESS);
- add("Oneshot . retrig", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_RETRIG);
- add("Oneshot . endless", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_ENDLESS);
+ add("Loop . basic", 0, cb_changeMode, (void*)SamplePlayerMode::LOOP_BASIC);
+ add("Loop . once", 0, cb_changeMode, (void*)SamplePlayerMode::LOOP_ONCE);
+ add("Loop . once . bar", 0, cb_changeMode, (void*)SamplePlayerMode::LOOP_ONCE_BAR);
+ add("Loop . repeat", 0, cb_changeMode, (void*)SamplePlayerMode::LOOP_REPEAT);
+ add("Oneshot . basic", 0, cb_changeMode, (void*)SamplePlayerMode::SINGLE_BASIC);
+ add("Oneshot . press", 0, cb_changeMode, (void*)SamplePlayerMode::SINGLE_PRESS);
+ add("Oneshot . retrig", 0, cb_changeMode, (void*)SamplePlayerMode::SINGLE_RETRIG);
+ add("Oneshot . endless", 0, cb_changeMode, (void*)SamplePlayerMode::SINGLE_ENDLESS);
value(static_cast<int>(m_channel.sample->mode));
}
-
/* -------------------------------------------------------------------------- */
-
-void geChannelMode::draw()
+void geChannelMode::draw()
{
- fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border
-
- switch (m_channel.sample->mode) {
- case SamplePlayerMode::LOOP_BASIC:
- fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::LOOP_ONCE:
- fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::LOOP_ONCE_BAR:
- fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::LOOP_REPEAT:
- fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::SINGLE_BASIC:
- fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::SINGLE_PRESS:
- fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::SINGLE_RETRIG:
- fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1);
- break;
- case SamplePlayerMode::SINGLE_ENDLESS:
- fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1);
- break;
+ fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border
+
+ switch (m_channel.sample->mode)
+ {
+ case SamplePlayerMode::LOOP_BASIC:
+ fl_draw_pixmap(loopBasic_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::LOOP_ONCE:
+ fl_draw_pixmap(loopOnce_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::LOOP_ONCE_BAR:
+ fl_draw_pixmap(loopOnceBar_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::LOOP_REPEAT:
+ fl_draw_pixmap(loopRepeat_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::SINGLE_BASIC:
+ fl_draw_pixmap(oneshotBasic_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::SINGLE_PRESS:
+ fl_draw_pixmap(oneshotPress_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::SINGLE_RETRIG:
+ fl_draw_pixmap(oneshotRetrig_xpm, x() + 1, y() + 1);
+ break;
+ case SamplePlayerMode::SINGLE_ENDLESS:
+ fl_draw_pixmap(oneshotEndless_xpm, x() + 1, y() + 1);
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
void geChannelMode::cb_changeMode(Fl_Widget* w, void* p) { ((geChannelMode*)w)->cb_changeMode((intptr_t)p); }
-
/* -------------------------------------------------------------------------- */
-
void geChannelMode::cb_changeMode(int mode)
{
c::channel::setSamplePlayerMode(m_channel.id, static_cast<SamplePlayerMode>(mode));
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHANNEL_MODE_H
#define GE_CHANNEL_MODE_H
-
#include <FL/Fl_Menu_Button.H>
-
-namespace giada {
-namespace v
+namespace giada::c::channel
+{
+struct Data;
+}
+namespace giada::v
{
class geChannelMode : public Fl_Menu_Button
{
public:
-
geChannelMode(int x, int y, int w, int h, c::channel::Data& d);
void draw() override;
-private:
-
- static void cb_changeMode(Fl_Widget* /*w*/, void* p);
- void cb_changeMode(int mode);
+ private:
+ static void cb_changeMode(Fl_Widget* /*w*/, void* p);
+ void cb_changeMode(int mode);
- c::channel::Data& m_channel;
+ c::channel::Data& m_channel;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/fl_draw.H>
+#include "channelStatus.h"
#include "core/const.h"
#include "glue/channel.h"
-#include "channelStatus.h"
-
+#include <FL/fl_draw.H>
-namespace giada {
-namespace v
+namespace giada::v
{
geChannelStatus::geChannelStatus(int x, int y, int w, int h, c::channel::Data& d)
-: Fl_Box (x, y, w, h)
+: Fl_Box(x, y, w, h)
, m_channel(d)
{
}
-
/* -------------------------------------------------------------------------- */
-
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
+ 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
- ChannelStatus playStatus = m_channel.a_getPlayStatus();
- ChannelStatus recStatus = m_channel.a_getRecStatus();
+ ChannelStatus playStatus = m_channel.getPlayStatus();
+ ChannelStatus recStatus = m_channel.getRecStatus();
Pixel pos = 0;
- if (playStatus == ChannelStatus::WAIT ||
- playStatus == ChannelStatus::ENDING ||
- recStatus == ChannelStatus::WAIT ||
- recStatus == ChannelStatus::ENDING)
+ if (playStatus == ChannelStatus::WAIT ||
+ playStatus == ChannelStatus::ENDING ||
+ recStatus == ChannelStatus::WAIT ||
+ recStatus == ChannelStatus::ENDING)
{
fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
}
- else
- if (playStatus == ChannelStatus::PLAY) {
+ else if (playStatus == ChannelStatus::PLAY)
+ {
/* Equation for the progress bar:
((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
- Frame tracker = m_channel.sample->a_getTracker();
- Frame begin = m_channel.sample->a_getBegin();
- Frame end = m_channel.sample->a_getEnd();
- pos = ((tracker - begin) * (w() - 1)) / ((end - begin));
+ Frame tracker = m_channel.sample->getTracker();
+ Frame begin = m_channel.sample->getBegin();
+ Frame end = m_channel.sample->getEnd();
+ pos = ((tracker - begin) * (w() - 1)) / ((end - begin));
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
+ fl_rectf(x() + 1, y() + 1, w() - 2, h() - 2, G_COLOR_GREY_2); // status empty
if (pos != 0)
- fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
+ fl_rectf(x() + 1, y() + 1, pos, h() - 2, G_COLOR_LIGHT_1);
}
-
-}} // giada::v::
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_CHANNEL_STATUS_H
#define GE_CHANNEL_STATUS_H
-
#include <FL/Fl_Box.H>
-
-namespace giada {
-namespace c {
-namespace channel
+namespace giada::c::channel
{
struct Data;
-}}
-namespace v
+}
+namespace giada::v
{
class geChannelStatus : public Fl_Box
{
public:
-
geChannelStatus(int x, int y, int w, int h, c::channel::Data& d);
void draw() override;
private:
-
c::channel::Data& m_channel;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Menu_Button.H>
-#include "core/channels/state.h"
+#include "column.h"
#include "core/model/model.h"
#include "glue/channel.h"
-#include "utils/log.h"
-#include "utils/fs.h"
-#include "utils/string.h"
#include "gui/dialogs/warnings.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/resizerBar.h"
#include "keyboard.h"
-#include "sampleChannel.h"
#include "midiChannel.h"
-#include "column.h"
-
+#include "sampleChannel.h"
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include <FL/Fl_Menu_Button.H>
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
-namespace v
+namespace giada::v
{
geColumn::geColumn(int X, int Y, int W, int H, ID id, geResizerBar* b)
-: Fl_Group (X, Y, W, H),
- id (id),
- resizerBar(b)
+: Fl_Group(X, Y, W, H)
+, id(id)
+, resizerBar(b)
{
end();
init();
}
-
/* -------------------------------------------------------------------------- */
-
void geColumn::refresh()
{
for (geChannel* c : m_channels)
c->refresh();
}
-
/* -------------------------------------------------------------------------- */
-
void geColumn::cb_addChannel(Fl_Widget* /*w*/, void* p) { ((geColumn*)p)->cb_addChannel(); }
-
/* -------------------------------------------------------------------------- */
-
geChannel* geColumn::addChannel(c::channel::Data d)
{
geChannel* gch = nullptr;
if (d.type == ChannelType::SAMPLE)
gch = new geSampleChannel(x(), last->y() + last->h() + G_GUI_INNER_MARGIN, w(), d.height, d);
else
- gch = new geMidiChannel (x(), last->y() + last->h() + G_GUI_INNER_MARGIN, w(), d.height, d);
+ gch = new geMidiChannel(x(), last->y() + last->h() + G_GUI_INNER_MARGIN, w(), d.height, d);
- geResizerBar* bar = new geResizerBar(x(), gch->y() + gch->h(), w(),
- G_GUI_INNER_MARGIN, G_GUI_UNIT, geResizerBar::VERTICAL, gch);
+ geResizerBar* bar = new geResizerBar(x(), gch->y() + gch->h(), w(),
+ G_GUI_INNER_MARGIN, G_GUI_UNIT, geResizerBar::VERTICAL, gch);
/* Update the column height while dragging the resizer bar. */
- bar->onDrag = [this](const Fl_Widget* /*w*/)
- {
- resizable(nullptr);
+ bar->onDrag = [this](const Fl_Widget* /*w*/) {
+ resizable(nullptr);
size(this->w(), (child(children() - 1)->y() - y()) + G_GUI_INNER_MARGIN);
-
- };
+ };
/* Store the channel height in model when the resizer bar is released. */
- bar->onRelease = [channelId = d.id, this](const Fl_Widget* w)
- {
+ bar->onRelease = [channelId = d.id, this](const Fl_Widget* w) {
resizable(this);
c::channel::setHeight(channelId, w->h());
};
/* Temporarily disable the resizability, add new stuff, resize the group and
bring the resizability back. This is needed to prevent weird vertical
- stretching on existing content. */
+ stretching on existing content. */
resizable(nullptr);
add(gch);
return gch;
}
-
/* -------------------------------------------------------------------------- */
-
void geColumn::cb_addChannel()
{
u::log::print("[geColumn::cb_addChannel] id = %d\n", id);
Fl_Menu_Item menu[] = {
- {"Add Sample channel"},
- {"Add MIDI channel"},
- {"Remove"},
- {0}
- };
+ {"Add Sample channel"},
+ {"Add MIDI channel"},
+ {"Remove"},
+ {0}};
if (countChannels() > 0)
menu[2].deactivate();
b.color(G_COLOR_GREY_2);
const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
- if (m == nullptr) return;
+ if (m == nullptr)
+ return;
if (strcmp(m->label(), "Add Sample channel") == 0)
c::channel::addChannel(id, ChannelType::SAMPLE);
- else
- if (strcmp(m->label(), "Add MIDI channel") == 0)
+ else if (strcmp(m->label(), "Add MIDI channel") == 0)
c::channel::addChannel(id, ChannelType::MIDI);
else
static_cast<geKeyboard*>(parent())->deleteColumn(id);
-
}
-
/* -------------------------------------------------------------------------- */
-
geChannel* geColumn::getChannel(ID channelId) const
{
for (geChannel* c : m_channels)
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
void geColumn::init()
{
Fl_Group::clear();
add(m_addChannelBtn);
}
-
/* -------------------------------------------------------------------------- */
-
void geColumn::forEachChannel(std::function<void(geChannel& c)> f) const
{
for (geChannel* c : m_channels)
f(*c);
}
-
/* -------------------------------------------------------------------------- */
-
int geColumn::countChannels() const
{
return m_channels.size();
}
-
/* -------------------------------------------------------------------------- */
-
int geColumn::computeHeight() const
{
int out = 0;
out += c->h() + G_GUI_INNER_MARGIN;
return out + m_addChannelBtn->h() + G_GUI_INNER_MARGIN;
}
-}} // giada::v::
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_COLUMN_H
#define GE_COLUMN_H
-
+#include "core/types.h"
+#include "glue/channel.h"
+#include <FL/Fl_Group.H>
#include <functional>
#include <vector>
-#include <FL/Fl_Group.H>
-#include "glue/channel.h"
-#include "core/types.h"
-
class geButton;
class geResizerBar;
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geKeyboard;
class geChannel;
class geColumn : public Fl_Group
{
public:
-
geColumn(int x, int y, int w, int h, ID id, geResizerBar* b);
geChannel* getChannel(ID channelId) const;
-
+
/* addChannel
Adds a new channel in this column. */
void refresh();
- void init();
+ void init();
void forEachChannel(std::function<void(geChannel& c)> f) const;
-
+
ID id;
geResizerBar* resizerBar;
-private:
-
+ private:
static void cb_addChannel(Fl_Widget* /*w*/, void* p);
- void cb_addChannel();
+ void cb_addChannel();
int countChannels() const;
int computeHeight() const;
geButton* m_addChannelBtn;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/fl_draw.H>
-#include "glue/io.h"
+#include "keyboard.h"
+#include "channelButton.h"
+#include "column.h"
#include "glue/channel.h"
-#include "utils/fs.h"
-#include "utils/log.h"
-#include "utils/vector.h"
-#include "utils/string.h"
-#include "gui/dispatcher.h"
+#include "glue/io.h"
#include "gui/dialogs/warnings.h"
+#include "gui/dispatcher.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/resizerBar.h"
-#include "column.h"
#include "sampleChannel.h"
-#include "channelButton.h"
-#include "keyboard.h"
-
+#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
+#include "utils/vector.h"
+#include <FL/fl_draw.H>
+#include <cassert>
-namespace giada {
+namespace giada
+{
namespace v
{
geKeyboard::geKeyboard(int X, int Y, int W, int H)
-: geScroll (X, Y, W, H, Fl_Scroll::BOTH_ALWAYS)
+: geScroll(X, Y, W, H, Fl_Scroll::BOTH_ALWAYS)
, m_addColumnBtn(nullptr)
{
end();
init();
+ rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::init()
{
m_columnId = m::IdManager();
layout.push_back({6, G_DEFAULT_COLUMN_WIDTH});
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::rebuild()
{
/* Wipe out all columns and add them according to the current layout. */
deleteAllColumns();
-
+
for (ColumnLayout c : layout)
addColumn(c.width, c.id);
for (const c::channel::Data& ch : c::channel::getChannels())
getColumn(ch.columnId)->addChannel(ch);
-
+
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::deleteColumn(ID id)
{
u::vector::removeIf(layout, [=](const ColumnLayout& c) { return c.id == id; });
rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::deleteAllColumns()
{
Fl_Scroll::clear();
m_columns.clear();
m_addColumnBtn = new geButton(8, y(), 200, 20, "Add new column");
- m_addColumnBtn->callback(cb_addColumn, (void*) this);
+ m_addColumnBtn->callback(cb_addColumn, (void*)this);
add(m_addColumnBtn);
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::cb_addColumn(Fl_Widget* /*w*/, void* p)
{
((geKeyboard*)p)->cb_addColumn();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::refresh()
{
for (geColumn* c : m_columns)
c->refresh();
}
-
/* -------------------------------------------------------------------------- */
-
int geKeyboard::handle(int e)
{
- switch (e) {
- case FL_FOCUS:
- case FL_UNFOCUS: {
- return 1; // Enables receiving Keyboard events
- }
- case FL_SHORTCUT: // In case widget that isn't ours has focus
- case FL_KEYDOWN: // Keyboard key pushed
- case FL_KEYUP: { // Keyboard key released
- dispatcher::dispatchKey(e);
- return 1;
- }
- case FL_DND_ENTER: // return(1) for these events to 'accept' dnd
- case FL_DND_DRAG:
- case FL_DND_RELEASE: {
- return 1;
- }
- case FL_PASTE: { // handle actual drop (paste) operation
- const geColumn* c = getColumnAtCursor(Fl::event_x());
- if (c != nullptr)
- c::channel::addAndLoadChannels(c->id, getDroppedFilePaths());
- return 1;
- }
+ switch (e)
+ {
+ case FL_FOCUS:
+ case FL_UNFOCUS:
+ {
+ return 1; // Enables receiving Keyboard events
+ }
+ case FL_SHORTCUT: // In case widget that isn't ours has focus
+ case FL_KEYDOWN: // Keyboard key pushed
+ case FL_KEYUP:
+ { // Keyboard key released
+ dispatcher::dispatchKey(e);
+ return 1;
}
- return Fl_Group::handle(e); // Assume the buttons won't handle the Keyboard events
+ case FL_DND_ENTER: // return(1) for these events to 'accept' dnd
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE:
+ {
+ return 1;
+ }
+ case FL_PASTE:
+ { // handle actual drop (paste) operation
+ const geColumn* c = getColumnAtCursor(Fl::event_x());
+ if (c != nullptr)
+ c::channel::addAndLoadChannels(c->id, getDroppedFilePaths());
+ return 1;
+ }
+ }
+ return Fl_Group::handle(e); // Assume the buttons won't handle the Keyboard events
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::draw()
{
Fl_Scroll::draw();
fl_color(G_COLOR_GREY_1_5);
fl_push_clip(
- x(),
- y(),
- w() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2),
- h() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2));
+ x(),
+ y(),
+ w() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2),
+ h() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2));
for (const geColumn* c : m_columns)
fl_rectf(c->x(), c->y() + c->h(), c->w(), h() + yposition());
fl_pop_clip();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::cb_addColumn()
{
addColumn();
storeLayout();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::addColumn(int width, ID id)
{
- int colx = x() - xposition(); // Mind the x-scroll offset with xposition()
+ int colx = x() - xposition(); // Mind the x-scroll offset with xposition()
/* If this is not the first column... */
/* Add a new column + a new resizer bar. */
geResizerBar* bar = new geResizerBar(colx + width, y(), COLUMN_GAP, h(), G_MIN_COLUMN_WIDTH, geResizerBar::HORIZONTAL);
- geColumn* column = new geColumn(colx, y(), width, G_GUI_UNIT, m_columnId.get(id), bar);
+ geColumn* column = new geColumn(colx, y(), width, G_GUI_UNIT, m_columnId.generate(id), bar);
/* Store the column width in layout when the resizer bar is released. */
- bar->onRelease = [=](const Fl_Widget* /*w*/)
- {
+ bar->onRelease = [=](const Fl_Widget* /*w*/) {
storeLayout();
};
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::forEachChannel(std::function<void(geChannel& c)> f) const
{
- for (geColumn* column : m_columns)
+ for (geColumn* column : m_columns)
column->forEachChannel(f);
}
-
void geKeyboard::forEachColumn(std::function<void(const geColumn& c)> f) const
{
- for (geColumn* column : m_columns)
+ for (geColumn* column : m_columns)
f(*column);
}
-
/* -------------------------------------------------------------------------- */
-
geColumn* geKeyboard::getColumn(ID id)
{
- for (geColumn* c : m_columns)
+ for (geColumn* c : m_columns)
if (c->id == id)
return c;
assert(false);
return nullptr;
}
-
geColumn* geKeyboard::getColumnAtCursor(Pixel px)
{
px += xposition();
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
geChannel* geKeyboard::getChannel(ID channelId)
{
- for (geColumn* column : m_columns) {
+ for (geColumn* column : m_columns)
+ {
geChannel* c = column->getChannel(channelId);
- if (c != nullptr)
+ if (c != nullptr)
return c;
}
assert(false);
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
std::vector<std::string> geKeyboard::getDroppedFilePaths() const
-{
+{
std::vector<std::string> paths = u::string::split(Fl::event_text(), "\n");
- for (std::string& p : paths)
- p = u::fs::stripFileUrl(p);
+ for (std::string& p : paths)
+ p = u::fs::stripFileUrl(p);
return paths;
}
-
/* -------------------------------------------------------------------------- */
-
void geKeyboard::storeLayout()
{
layout.clear();
for (const geColumn* c : m_columns)
- layout.push_back({ c->id, c->w() });
+ layout.push_back({c->id, c->w()});
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_KEYBOARD_H
#define GE_KEYBOARD_H
-
-#include <vector>
-#include <functional>
-#include "gui/elems/basics/scroll.h"
-#include "core/idManager.h"
#include "core/const.h"
-
+#include "core/idManager.h"
+#include "gui/elems/basics/scroll.h"
+#include <functional>
+#include <vector>
class geButton;
class geResizerBar;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geColumn;
class geKeyboard : public geScroll
{
public:
-
struct ColumnLayout
{
- ID id;
+ ID id;
int width;
};
geKeyboard(int X, int Y, int W, int H);
- int handle(int e) override;
+ int handle(int e) override;
void draw() override;
/* rebuild
/* deleteColumn
Deletes column by id. */
-
+
void deleteColumn(ID id);
/* deleteAllColumns
std::vector<ColumnLayout> layout;
-private:
-
+ private:
static constexpr int COLUMN_GAP = 20;
static void cb_addColumn(Fl_Widget* /*w*/, void* p);
- void cb_addColumn();
+ void cb_addColumn();
- void addColumn(int width=G_DEFAULT_COLUMN_WIDTH, ID id=0);
+ void addColumn(int width = G_DEFAULT_COLUMN_WIDTH, ID id = 0);
/* getDroppedFilePaths
Returns a vector of audio file paths after a drag-n-drop from desktop
/* storeLayout
Stores the current column layout into the layout vector. */
-
+
void storeLayout();
- m::IdManager m_columnId;
+ m::IdManager m_columnId;
std::vector<geColumn*> m_columns;
geButton* m_addColumnBtn;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl_Menu_Button.H>
+#include "midiChannel.h"
+#include "column.h"
#include "core/const.h"
#include "core/graphics.h"
#include "core/model/model.h"
#include "core/recorder.h"
-#include "utils/gui.h"
-#include "utils/string.h"
#include "glue/channel.h"
#include "glue/io.h"
#include "glue/recorder.h"
-#include "gui/dispatcher.h"
-#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/actionEditor/midiActionEditor.h"
#include "gui/dialogs/channelNameInput.h"
-#include "gui/dialogs/warnings.h"
#include "gui/dialogs/keyGrabber.h"
-#include "gui/dialogs/pluginList.h"
-#include "gui/dialogs/actionEditor/midiActionEditor.h"
+#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/midiIO/midiInputChannel.h"
#include "gui/dialogs/midiIO/midiOutputMidiCh.h"
+#include "gui/dialogs/pluginList.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dispatcher.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/statusButton.h"
#include "gui/elems/basics/dial.h"
-#include "column.h"
+#include "gui/elems/basics/statusButton.h"
#include "midiChannelButton.h"
-#include "midiChannel.h"
-
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <FL/Fl_Menu_Button.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
namespace
DELETE_CHANNEL
};
-
/* -------------------------------------------------------------------------- */
-
void menuCallback(Fl_Widget* w, void* v)
{
const geMidiChannel* gch = static_cast<geMidiChannel*>(w);
const c::channel::Data& data = gch->getData();
- switch ((Menu) (intptr_t) v)
+ switch ((Menu)(intptr_t)v)
{
- case Menu::CLEAR_ACTIONS:
- case Menu::__END_CLEAR_ACTION_SUBMENU__:
- break;
- case Menu::EDIT_ACTIONS:
- u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(data.id), WID_ACTION_EDITOR);
- break;
- case Menu::CLEAR_ACTIONS_ALL:
- c::recorder::clearAllActions(data.id);
- break;
- case Menu::SETUP_KEYBOARD_INPUT:
- u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data), WID_KEY_GRABBER);
- break;
- case Menu::SETUP_MIDI_INPUT:
- u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id), WID_MIDI_INPUT);
- break;
- case Menu::SETUP_MIDI_OUTPUT:
- u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(data.id), WID_MIDI_OUTPUT);
- break;
- case Menu::CLONE_CHANNEL:
- c::channel::cloneChannel(data.id);
- break;
- case Menu::RENAME_CHANNEL:
- u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data), WID_SAMPLE_NAME);
- break;
- case Menu::DELETE_CHANNEL:
- c::channel::deleteChannel(data.id);
- break;
+ case Menu::CLEAR_ACTIONS:
+ case Menu::__END_CLEAR_ACTION_SUBMENU__:
+ break;
+ case Menu::EDIT_ACTIONS:
+ u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(data.id), WID_ACTION_EDITOR);
+ break;
+ case Menu::CLEAR_ACTIONS_ALL:
+ c::recorder::clearAllActions(data.id);
+ break;
+ case Menu::SETUP_KEYBOARD_INPUT:
+ u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data), WID_KEY_GRABBER);
+ break;
+ case Menu::SETUP_MIDI_INPUT:
+ u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id), WID_MIDI_INPUT);
+ break;
+ case Menu::SETUP_MIDI_OUTPUT:
+ u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(data.id), WID_MIDI_OUTPUT);
+ break;
+ case Menu::CLONE_CHANNEL:
+ c::channel::cloneChannel(data.id);
+ break;
+ case Menu::RENAME_CHANNEL:
+ u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data), WID_SAMPLE_NAME);
+ break;
+ case Menu::DELETE_CHANNEL:
+ c::channel::deleteChannel(data.id);
+ break;
}
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
-
geMidiChannel::geMidiChannel(int X, int Y, int W, int H, c::channel::Data d)
: geChannel(X, Y, W, H, d)
-, m_data (d)
+, m_data(d)
{
#if defined(WITH_VST)
constexpr int delta = 6 * (G_GUI_UNIT + G_GUI_INNER_MARGIN);
constexpr int delta = 5 * (G_GUI_UNIT + G_GUI_INNER_MARGIN);
#endif
- playButton = new geStatusButton (x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
- arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
+ playButton = new geStatusButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
+ arm = new geButton(playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
mainButton = new geMidiChannelButton(arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, m_channel);
- mute = new geStatusButton (mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
- solo = new geStatusButton (mute->x() + mute->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
+ mute = new geStatusButton(mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
+ solo = new geStatusButton(mute->x() + mute->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
#if defined(WITH_VST)
- fx = new geStatusButton (solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
- vol = new geDial (fx->x() + fx->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
+ fx = new geStatusButton(solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
+ vol = new geDial(fx->x() + fx->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
#else
- vol = new geDial (solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
+ vol = new geDial(solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
#endif
end();
resizable(mainButton);
+ playButton->copy_tooltip("Play/stop");
+ arm->copy_tooltip("Arm for recording");
+ mute->copy_tooltip("Mute");
+ solo->copy_tooltip("Solo");
+#if defined(WITH_VST)
+ fx->copy_tooltip("Plug-ins");
+#endif
+ vol->copy_tooltip("Volume");
+
#ifdef WITH_VST
- fx->setStatus(m_channel.pluginIds.size() > 0);
+ fx->setStatus(m_channel.plugins.size() > 0);
#endif
playButton->callback(cb_playButton, (void*)this);
- playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease
+ playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease
arm->type(FL_TOGGLE_BUTTON);
- arm->value(m_channel.a_isArmed());
+ arm->value(m_channel.isArmed());
arm->callback(cb_arm, (void*)this);
#ifdef WITH_VST
size(w(), h()); // Force responsiveness
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannel::cb_playButton(Fl_Widget* /*w*/, void* p) { ((geMidiChannel*)p)->cb_playButton(); }
void geMidiChannel::cb_openMenu(Fl_Widget* /*w*/, void* p) { ((geMidiChannel*)p)->cb_openMenu(); }
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannel::cb_playButton()
{
v::dispatcher::dispatchTouch(*this, playButton->value());
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannel::cb_openMenu()
{
Fl_Menu_Item rclick_menu[] = {
- {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
- {"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
- {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
- {0},
- {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
- {"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
- {"Setup MIDI output...", 0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
- {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL},
- {"Clone", 0, menuCallback, (void*) Menu::CLONE_CHANNEL},
- {"Delete", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
- {0}
- };
+ {"Edit actions...", 0, menuCallback, (void*)Menu::EDIT_ACTIONS},
+ {"Clear actions", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS, FL_SUBMENU},
+ {"All", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS_ALL},
+ {0},
+ {"Setup keyboard input...", 0, menuCallback, (void*)Menu::SETUP_KEYBOARD_INPUT},
+ {"Setup MIDI input...", 0, menuCallback, (void*)Menu::SETUP_MIDI_INPUT},
+ {"Setup MIDI output...", 0, menuCallback, (void*)Menu::SETUP_MIDI_OUTPUT},
+ {"Rename", 0, menuCallback, (void*)Menu::RENAME_CHANNEL},
+ {"Clone", 0, menuCallback, (void*)Menu::CLONE_CHANNEL},
+ {"Delete", 0, menuCallback, (void*)Menu::DELETE_CHANNEL},
+ {0}};
/* No 'clear actions' if there are no actions. */
return;
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannel::resize(int X, int Y, int W, int H)
{
geChannel::resize(X, Y, W, H);
packWidgets();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MIDI_CHANNEL_H
#define GE_MIDI_CHANNEL_H
-
#include "channel.h"
#include "channelButton.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geMidiChannel : public geChannel
{
public:
+ geMidiChannel(int x, int y, int w, int h, c::channel::Data d);
- geMidiChannel(int x, int y, int w, int h, c::channel::Data d);
-
- void resize(int x, int y, int w, int h) override;
-
-private:
+ void resize(int x, int y, int w, int h) override;
+ private:
static void cb_playButton(Fl_Widget* /*w*/, void* p);
static void cb_openMenu(Fl_Widget* /*w*/, void* p);
- void cb_playButton();
- void cb_openMenu();
+ void cb_playButton();
+ void cb_openMenu();
c::channel::Data m_data;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "utils/string.h"
-#include "glue/channel.h"
#include "midiChannelButton.h"
+#include "glue/channel.h"
+#include "utils/string.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const c::channel::Data& d)
{
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannelButton::refresh()
{
geChannelButton::refresh();
refreshLabel();
- if (m_channel.a_isRecordingAction() && m_channel.a_isArmed())
+ if (m_channel.isRecordingAction() && m_channel.isArmed())
setActionRecordMode();
-
+
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiChannelButton::refreshLabel()
{
- std::string l = m_channel.name.empty() ? "-- MIDI --" : m_channel.name;
+ std::string l = m_channel.name.empty() ? "-- MIDI --" : m_channel.name;
- if (m_channel.midi->a_isOutputEnabled())
- l += " (ch " + std::to_string(m_channel.midi->a_getFilter() + 1) + " out)";
+ if (m_channel.midi->isOutputEnabled())
+ l += " (ch " + std::to_string(m_channel.midi->getFilter() + 1) + " out)";
copy_label(l.c_str());
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MIDI_CHANNEL_BUTTON_H
#define GE_MIDI_CHANNEL_BUTTON_H
-
#include "channelButton.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geMidiChannelButton : public geChannelButton
{
public:
-
geMidiChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
-
- void refresh() override;
-private:
+ void refresh() override;
+ private:
void refreshLabel();
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
+#include "sampleChannel.h"
+#include "channelMode.h"
+#include "channelStatus.h"
+#include "column.h"
#include "core/channels/channel.h"
#include "core/channels/samplePlayer.h"
-#include "core/model/model.h"
-#include "core/mixer.h"
-#include "core/conf.h"
#include "core/clock.h"
+#include "core/conf.h"
#include "core/graphics.h"
-#include "core/wave.h"
-#include "core/recorder.h"
+#include "core/mixer.h"
+#include "core/model/model.h"
#include "core/recManager.h"
-#include "glue/io.h"
+#include "core/recorder.h"
+#include "core/wave.h"
#include "glue/channel.h"
#include "glue/events.h"
+#include "glue/io.h"
#include "glue/recorder.h"
#include "glue/storage.h"
-#include "utils/gui.h"
-#include "gui/dispatcher.h"
-#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/keyGrabber.h"
-#include "gui/dialogs/sampleEditor.h"
-#include "gui/dialogs/channelNameInput.h"
-#include "gui/dialogs/warnings.h"
#include "gui/dialogs/actionEditor/sampleActionEditor.h"
-#include "gui/dialogs/browser/browserSave.h"
#include "gui/dialogs/browser/browserLoad.h"
-#include "gui/dialogs/midiIO/midiOutputSampleCh.h"
+#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/channelNameInput.h"
+#include "gui/dialogs/keyGrabber.h"
+#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/midiIO/midiInputChannel.h"
+#include "gui/dialogs/midiIO/midiOutputSampleCh.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dispatcher.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/statusButton.h"
#include "gui/elems/basics/dial.h"
-#include "channelStatus.h"
-#include "channelMode.h"
-#include "sampleChannelButton.h"
+#include "gui/elems/basics/statusButton.h"
#include "keyboard.h"
-#include "column.h"
-#include "sampleChannel.h"
-
+#include "sampleChannelButton.h"
+#include "utils/gui.h"
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
namespace
DELETE_CHANNEL
};
-
/* -------------------------------------------------------------------------- */
-
void menuCallback(Fl_Widget* w, void* v)
{
const geSampleChannel* gch = static_cast<geSampleChannel*>(w);
const c::channel::Data& data = gch->getData();
- switch ((Menu) (intptr_t) v) {
- case Menu::INPUT_MONITOR: {
- c::channel::setInputMonitor(data.id, !data.sample->a_getInputMonitor());
- break;
- }
- case Menu::OVERDUB_PROTECTION: {
- c::channel::setOverdubProtection(data.id, !data.sample->a_getOverdubProtection());
- break;
- }
- case Menu::LOAD_SAMPLE: {
- gdWindow* w = new gdBrowserLoad("Browse sample",
- m::conf::conf.samplePath.c_str(), c::storage::loadSample, data.id);
- u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
- break;
- }
- case Menu::EXPORT_SAMPLE: {
- gdWindow* w = new gdBrowserSave("Save sample",
- m::conf::conf.samplePath.c_str(), "", c::storage::saveSample, data.id);
- u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
- break;
- }
- case Menu::SETUP_KEYBOARD_INPUT: {
- u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data),
- WID_KEY_GRABBER);
- break;
- }
- case Menu::SETUP_MIDI_INPUT: {
- u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id),
- WID_MIDI_INPUT);
- break;
- }
- case Menu::SETUP_MIDI_OUTPUT: {
- u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(data.id),
- WID_MIDI_OUTPUT);
- break;
- }
- case Menu::EDIT_SAMPLE: {
- u::gui::openSubWindow(G_MainWin, new gdSampleEditor(data.id),
- WID_SAMPLE_EDITOR);
- break;
- }
- case Menu::EDIT_ACTIONS: {
- u::gui::openSubWindow(G_MainWin, new gdSampleActionEditor(data.id),
- WID_ACTION_EDITOR);
- break;
- }
- case Menu::CLEAR_ACTIONS:
- case Menu::__END_CLEAR_ACTIONS_SUBMENU__:
- break;
- case Menu::CLEAR_ACTIONS_ALL: {
- c::recorder::clearAllActions(data.id);
- break;
- }
- case Menu::CLEAR_ACTIONS_VOLUME: {
- c::recorder::clearVolumeActions(data.id);
- break;
- }
- case Menu::CLEAR_ACTIONS_START_STOP: {
- c::recorder::clearStartStopActions(data.id);
- break;
- }
- case Menu::CLONE_CHANNEL: {
- c::channel::cloneChannel(data.id);
- break;
- }
- case Menu::RENAME_CHANNEL: {
- u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data),
- WID_SAMPLE_NAME);
- break;
- }
- case Menu::FREE_CHANNEL: {
- c::channel::freeChannel(data.id);
- break;
- }
- case Menu::DELETE_CHANNEL: {
- c::channel::deleteChannel(data.id);
- break;
- }
+ switch ((Menu)(intptr_t)v)
+ {
+ case Menu::INPUT_MONITOR:
+ {
+ c::channel::setInputMonitor(data.id, !data.sample->getInputMonitor());
+ break;
+ }
+ case Menu::OVERDUB_PROTECTION:
+ {
+ c::channel::setOverdubProtection(data.id, !data.sample->getOverdubProtection());
+ break;
+ }
+ case Menu::LOAD_SAMPLE:
+ {
+ gdWindow* w = new gdBrowserLoad("Browse sample",
+ m::conf::conf.samplePath.c_str(), c::storage::loadSample, data.id);
+ u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+ break;
+ }
+ case Menu::EXPORT_SAMPLE:
+ {
+ gdWindow* w = new gdBrowserSave("Save sample",
+ m::conf::conf.samplePath.c_str(), "", c::storage::saveSample, data.id);
+ u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+ break;
+ }
+ case Menu::SETUP_KEYBOARD_INPUT:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data),
+ WID_KEY_GRABBER);
+ break;
+ }
+ case Menu::SETUP_MIDI_INPUT:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id),
+ WID_MIDI_INPUT);
+ break;
+ }
+ case Menu::SETUP_MIDI_OUTPUT:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(data.id),
+ WID_MIDI_OUTPUT);
+ break;
+ }
+ case Menu::EDIT_SAMPLE:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdSampleEditor(data.id),
+ WID_SAMPLE_EDITOR);
+ break;
+ }
+ case Menu::EDIT_ACTIONS:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdSampleActionEditor(data.id),
+ WID_ACTION_EDITOR);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS:
+ case Menu::__END_CLEAR_ACTIONS_SUBMENU__:
+ break;
+ case Menu::CLEAR_ACTIONS_ALL:
+ {
+ c::recorder::clearAllActions(data.id);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS_VOLUME:
+ {
+ c::recorder::clearVolumeActions(data.id);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS_START_STOP:
+ {
+ c::recorder::clearStartStopActions(data.id);
+ break;
+ }
+ case Menu::CLONE_CHANNEL:
+ {
+ c::channel::cloneChannel(data.id);
+ break;
+ }
+ case Menu::RENAME_CHANNEL:
+ {
+ u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data),
+ WID_SAMPLE_NAME);
+ break;
+ }
+ case Menu::FREE_CHANNEL:
+ {
+ c::channel::freeChannel(data.id);
+ break;
+ }
+ case Menu::DELETE_CHANNEL:
+ {
+ c::channel::deleteChannel(data.id);
+ break;
+ }
}
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
geSampleChannel::geSampleChannel(int X, int Y, int W, int H, c::channel::Data d)
: geChannel(X, Y, W, H, d)
{
constexpr int delta = 8 * (G_GUI_UNIT + G_GUI_INNER_MARGIN);
#endif
- playButton = new geStatusButton (x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
- arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm, armDisabled_xpm);
- status = new geChannelStatus (arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, H, m_channel);
+ playButton = new geStatusButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
+ arm = new geButton(playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm, armDisabled_xpm);
+ status = new geChannelStatus(arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, H, m_channel);
mainButton = new geSampleChannelButton(status->x() + status->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, m_channel);
- readActions = new geStatusButton (mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, readActionOff_xpm, readActionOn_xpm, readActionDisabled_xpm);
- modeBox = new geChannelMode (readActions->x() + readActions->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, m_channel);
- mute = new geStatusButton (modeBox->x() + modeBox->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
- solo = new geStatusButton (mute->x() + mute->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
+ readActions = new geStatusButton(mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, readActionOff_xpm, readActionOn_xpm, readActionDisabled_xpm);
+ modeBox = new geChannelMode(readActions->x() + readActions->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, m_channel);
+ mute = new geStatusButton(modeBox->x() + modeBox->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
+ solo = new geStatusButton(mute->x() + mute->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
#if defined(WITH_VST)
- fx = new geStatusButton (solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
- vol = new geDial (fx->x() + fx->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
+ fx = new geStatusButton(solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
+ vol = new geDial(fx->x() + fx->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
#else
- vol = new geDial (solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
+ vol = new geDial(solo->x() + solo->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT);
#endif
end();
resizable(mainButton);
+ playButton->copy_tooltip("Play/stop");
+ arm->copy_tooltip("Arm for recording");
+ status->copy_tooltip("Progress bar");
+ readActions->copy_tooltip("Read actions\n\nToggles playback of pre-recorded "
+ "actions (key press, key release, ...).");
+ modeBox->copy_tooltip("Mode");
+ mute->copy_tooltip("Mute");
+ solo->copy_tooltip("Solo");
+#if defined(WITH_VST)
+ fx->copy_tooltip("Plug-ins");
+#endif
+ vol->copy_tooltip("Volume");
+
#ifdef WITH_VST
- fx->setStatus(m_channel.pluginIds.size() > 0);
+ fx->setStatus(m_channel.plugins.size() > 0);
#endif
playButton->callback(cb_playButton, (void*)this);
- playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease
+ playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease
arm->type(FL_TOGGLE_BUTTON);
- arm->value(m_channel.a_isArmed());
+ arm->value(m_channel.isArmed());
arm->callback(cb_arm, (void*)this);
#ifdef WITH_VST
size(w(), h()); // Force responsiveness
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleChannel::cb_playButton (Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_playButton(); }
-void geSampleChannel::cb_openMenu (Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_openMenu(); }
+void geSampleChannel::cb_playButton(Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_playButton(); }
+void geSampleChannel::cb_openMenu(Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_openMenu(); }
void geSampleChannel::cb_readActions(Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_readActions(); }
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannel::cb_playButton()
{
v::dispatcher::dispatchTouch(*this, playButton->value());
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannel::cb_openMenu()
{
/* If you're recording (input or actions) no menu is allowed; you can't do
return;
Fl_Menu_Item rclick_menu[] = {
- {"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR,
- FL_MENU_TOGGLE | (m_channel.sample->a_getInputMonitor() ? FL_MENU_VALUE : 0)},
- {"Overdub protection", 0, menuCallback, (void*) Menu::OVERDUB_PROTECTION,
- FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->a_getOverdubProtection() ? FL_MENU_VALUE : 0)},
- {"Load new sample...", 0, menuCallback, (void*) Menu::LOAD_SAMPLE},
- {"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE},
- {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
- {"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
- {"Setup MIDI output...", 0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
- {"Edit sample...", 0, menuCallback, (void*) Menu::EDIT_SAMPLE},
- {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
- {"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
- {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
- {"Volume", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME},
- {"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP},
- {0},
- {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL},
- {"Clone", 0, menuCallback, (void*) Menu::CLONE_CHANNEL},
- {"Free", 0, menuCallback, (void*) Menu::FREE_CHANNEL},
- {"Delete", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
- {0}
- };
-
- if (m_channel.sample->waveId == 0) {
- rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate();
- rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate();
- rclick_menu[(int) Menu::FREE_CHANNEL].deactivate();
- rclick_menu[(int) Menu::RENAME_CHANNEL].deactivate();
+ {"Input monitor", 0, menuCallback, (void*)Menu::INPUT_MONITOR,
+ FL_MENU_TOGGLE | (m_channel.sample->getInputMonitor() ? FL_MENU_VALUE : 0)},
+ {"Overdub protection", 0, menuCallback, (void*)Menu::OVERDUB_PROTECTION,
+ FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->getOverdubProtection() ? FL_MENU_VALUE : 0)},
+ {"Load new sample...", 0, menuCallback, (void*)Menu::LOAD_SAMPLE},
+ {"Export sample to file...", 0, menuCallback, (void*)Menu::EXPORT_SAMPLE},
+ {"Setup keyboard input...", 0, menuCallback, (void*)Menu::SETUP_KEYBOARD_INPUT},
+ {"Setup MIDI input...", 0, menuCallback, (void*)Menu::SETUP_MIDI_INPUT},
+ {"Setup MIDI output...", 0, menuCallback, (void*)Menu::SETUP_MIDI_OUTPUT},
+ {"Edit sample...", 0, menuCallback, (void*)Menu::EDIT_SAMPLE},
+ {"Edit actions...", 0, menuCallback, (void*)Menu::EDIT_ACTIONS},
+ {"Clear actions", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS, FL_SUBMENU},
+ {"All", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS_ALL},
+ {"Volume", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS_VOLUME},
+ {"Start/Stop", 0, menuCallback, (void*)Menu::CLEAR_ACTIONS_START_STOP},
+ {0},
+ {"Rename", 0, menuCallback, (void*)Menu::RENAME_CHANNEL},
+ {"Clone", 0, menuCallback, (void*)Menu::CLONE_CHANNEL},
+ {"Free", 0, menuCallback, (void*)Menu::FREE_CHANNEL},
+ {"Delete", 0, menuCallback, (void*)Menu::DELETE_CHANNEL},
+ {0}};
+
+ if (m_channel.sample->waveId == 0)
+ {
+ rclick_menu[(int)Menu::EXPORT_SAMPLE].deactivate();
+ rclick_menu[(int)Menu::EDIT_SAMPLE].deactivate();
+ rclick_menu[(int)Menu::FREE_CHANNEL].deactivate();
+ rclick_menu[(int)Menu::RENAME_CHANNEL].deactivate();
}
if (!m_channel.hasActions)
- rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate();
+ rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate();
/* No 'clear start/stop actions' for those channels in loop mode: they cannot
have start/stop actions. */
if (m_channel.sample->isLoop)
- rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate();
+ rclick_menu[(int)Menu::CLEAR_ACTIONS_START_STOP].deactivate();
Fl_Menu_Button b(0, 0, 100, 50);
b.box(G_CUSTOM_BORDER_BOX);
return;
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannel::cb_readActions()
{
if (Fl::event_shift())
c::events::toggleReadActionsChannel(m_channel.id, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannel::refresh()
{
geChannel::refresh();
- if (m_channel.sample->waveId != 0) {
+ if (m_channel.sample->waveId != 0)
+ {
status->redraw();
- if (m_channel.sample->a_getOverdubProtection())
+ if (m_channel.sample->getOverdubProtection())
arm->deactivate();
else
arm->activate();
}
- if (m_channel.hasActions) {
+ if (m_channel.hasActions)
+ {
readActions->activate();
- readActions->setStatus(m_channel.a_getReadActions());
+ readActions->setStatus(m_channel.getReadActions());
}
else
readActions->deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
-void geSampleChannel::draw()
+void geSampleChannel::draw()
{
const int ny = y() + (h() / 2) - (G_GUI_UNIT / 2);
geChannel::draw();
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannel::resize(int X, int Y, int W, int H)
{
geChannel::resize(X, Y, W, H);
#ifdef WITH_VST
fx->hide();
#endif
-
+
if (w() > BREAK_ARM)
arm->show();
#ifdef WITH_VST
packWidgets();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SAMPLE_CHANNEL_H
#define GE_SAMPLE_CHANNEL_H
-
-#include "glue/channel.h"
#include "channel.h"
+#include "glue/channel.h"
-
-class geStatusButton;
-
-
-namespace giada {
-namespace v
+namespace giada::v
{
+class geStatusButton;
class geChannelMode;
class geSampleChannel : public geChannel
{
public:
-
geSampleChannel(int x, int y, int w, int h, c::channel::Data d);
void resize(int x, int y, int w, int h) override;
void draw() override;
-
void refresh() override;
geChannelMode* modeBox;
geStatusButton* readActions;
-private:
-
+ private:
static void cb_playButton(Fl_Widget* /*w*/, void* p);
static void cb_openMenu(Fl_Widget* /*w*/, void* p);
static void cb_readActions(Fl_Widget* /*w*/, void* p);
- void cb_playButton();
- void cb_openMenu();
- void cb_readActions();
+ void cb_playButton();
+ void cb_openMenu();
+ void cb_readActions();
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include "utils/string.h"
-#include "utils/fs.h"
+#include "sampleChannelButton.h"
#include "glue/channel.h"
#include "gui/dialogs/mainWindow.h"
-#include "sampleChannel.h"
#include "keyboard.h"
-#include "sampleChannelButton.h"
-
+#include "sampleChannel.h"
+#include "utils/fs.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const c::channel::Data& d)
: geChannelButton(x, y, w, h, d)
{
- switch (m_channel.a_getPlayStatus()) {
- case ChannelStatus::MISSING:
- case ChannelStatus::WRONG:
- label("* file not found! *");
- break;
- default:
- label(m_channel.sample->waveId == 0 ? "-- no sample --" : m_channel.name.c_str());
- break;
+ switch (m_channel.getPlayStatus())
+ {
+ case ChannelStatus::MISSING:
+ case ChannelStatus::WRONG:
+ label("* file not found! *");
+ break;
+ default:
+ label(m_channel.sample->waveId == 0 ? "-- no sample --" : m_channel.name.c_str());
+ break;
}
}
-
/* -------------------------------------------------------------------------- */
-
void geSampleChannelButton::refresh()
{
geChannelButton::refresh();
- if (m_channel.a_isRecordingInput() && m_channel.a_isArmed())
+ if (m_channel.isRecordingInput() && m_channel.isArmed())
setInputRecordMode();
- else
- if (m_channel.a_isRecordingAction() && m_channel.sample->waveId != 0 && !m_channel.sample->isLoop)
+ else if (m_channel.isRecordingAction() && m_channel.sample->waveId != 0 && !m_channel.sample->isLoop)
setActionRecordMode();
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
int geSampleChannelButton::handle(int e)
{
int ret = geButton::handle(e);
- switch (e) {
- case FL_DND_ENTER:
- case FL_DND_DRAG:
- case FL_DND_RELEASE: {
- ret = 1;
- break;
- }
- case FL_PASTE: {
- c::channel::loadChannel(m_channel.id, u::string::trim(u::fs::stripFileUrl(Fl::event_text())));
- ret = 1;
- break;
- }
+ switch (e)
+ {
+ case FL_DND_ENTER:
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE:
+ {
+ ret = 1;
+ break;
+ }
+ case FL_PASTE:
+ {
+ c::channel::loadChannel(m_channel.id, u::string::trim(u::fs::stripFileUrl(Fl::event_text())));
+ ret = 1;
+ break;
+ }
}
return ret;
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SAMPLE_CHANNEL_BUTTON_H
#define GE_SAMPLE_CHANNEL_BUTTON_H
-
#include "channelButton.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geSampleChannelButton : public geChannelButton
{
public:
-
geSampleChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
-
- int handle(int e) override;
- void refresh() override;
-};
-}} // giada::v::
+ int handle(int e) override;
+ void refresh() override;
+};
+} // namespace v
+} // namespace giada
#endif
*
* ------------------------------------------------------------------------------
*
-* Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* --------------------------------------------------------------------------- */
-
+#include "mainIO.h"
#include "core/const.h"
#include "core/graphics.h"
+#include "glue/channel.h"
#include "glue/events.h"
#include "glue/main.h"
-#include "glue/channel.h"
-#include "utils/gui.h"
-#include "gui/elems/soundMeter.h"
-#include "gui/elems/basics/statusButton.h"
-#include "gui/elems/basics/dial.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/pluginList.h"
-#include "mainIO.h"
-
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/statusButton.h"
+#include "gui/elems/soundMeter.h"
+#include "utils/gui.h"
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
geMainIO::geMainIO(int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, outMeter (0, 0, 140, G_GUI_UNIT)
-, inMeter (0, 0, 140, G_GUI_UNIT)
-, outVol (0, 0, G_GUI_UNIT, G_GUI_UNIT)
-, inVol (0, 0, G_GUI_UNIT, G_GUI_UNIT)
-, inToOut (0, 0, 12, G_GUI_UNIT, "")
+: gePack(x, y, Direction::HORIZONTAL)
+, outMeter(0, 0, 140, G_GUI_UNIT)
+, inMeter(0, 0, 140, G_GUI_UNIT)
+, outVol(0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, inVol(0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, inToOut(0, 0, 12, G_GUI_UNIT, "")
#ifdef WITH_VST
, masterFxOut(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm)
-, masterFxIn (0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm)
+, masterFxIn(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm)
#endif
{
#ifdef WITH_VST
add(&masterFxOut);
#endif
- resizable(nullptr); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
+
+ outMeter.copy_tooltip("Main output meter");
+ inMeter.copy_tooltip("Main input meter");
+ outVol.copy_tooltip("Main output volume");
+ inVol.copy_tooltip("Main input volume");
+ inToOut.copy_tooltip("Stream linker\n\nConnects input to output to enable \"hear what you're playing\" mode.");
+#ifdef WITH_VST
+ masterFxOut.copy_tooltip("Main output plug-ins");
+ masterFxIn.copy_tooltip("Main input plug-ins");
+#endif
outVol.callback(cb_outVol, (void*)this);
inVol.callback(cb_inVol, (void*)this);
#endif
}
-
/* -------------------------------------------------------------------------- */
-
-void geMainIO::cb_outVol (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_outVol(); }
-void geMainIO::cb_inVol (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inVol(); }
-void geMainIO::cb_inToOut (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inToOut(); }
+void geMainIO::cb_outVol(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_outVol(); }
+void geMainIO::cb_inVol(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inVol(); }
+void geMainIO::cb_inToOut(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inToOut(); }
#ifdef WITH_VST
-void geMainIO::cb_masterFxOut(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_masterFxOut(); }
-void geMainIO::cb_masterFxIn (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_masterFxIn(); }
+void geMainIO::cb_masterFxOut(Fl_Widget* /*w*/, void* p)
+{
+ ((geMainIO*)p)->cb_masterFxOut();
+}
+void geMainIO::cb_masterFxIn(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_masterFxIn(); }
#endif
-
/* -------------------------------------------------------------------------- */
-
void geMainIO::cb_outVol()
{
c::events::setMasterOutVolume(outVol.value(), Thread::MAIN);
}
-
void geMainIO::cb_inVol()
{
c::events::setMasterInVolume(inVol.value(), Thread::MAIN);
}
-
void geMainIO::cb_inToOut()
{
c::main::setInToOut(inToOut.value());
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
void geMainIO::cb_masterFxOut()
u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_OUT_CHANNEL_ID), WID_FX_LIST);
}
-
void geMainIO::cb_masterFxIn()
{
u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_IN_CHANNEL_ID), WID_FX_LIST);
#endif
-
/* -------------------------------------------------------------------------- */
-
void geMainIO::setOutVol(float v)
{
outVol.value(v);
}
-
void geMainIO::setInVol(float v)
{
inVol.value(v);
}
-
/* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
void geMainIO::setMasterFxOutFull(bool v)
masterFxOut.setStatus(v);
}
-
void geMainIO::setMasterFxInFull(bool v)
{
masterFxIn.setStatus(v);
#endif
-
/* -------------------------------------------------------------------------- */
-
void geMainIO::refresh()
{
- outMeter.mixerPeak = m_io.a_getMasterOutPeak();
- inMeter.mixerPeak = m_io.a_getMasterInPeak();
+ outMeter.mixerPeak = m_io.getMasterOutPeak();
+ inMeter.mixerPeak = m_io.getMasterInPeak();
outMeter.redraw();
inMeter.redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geMainIO::rebuild()
{
m_io = c::main::getIO();
inToOut.value(m_io.inToOut);
#endif
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MAIN_IO_H
#define GE_MAIN_IO_H
-
-#include "gui/elems/soundMeter.h"
-#include "gui/elems/basics/pack.h"
-#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/soundMeter.h"
#ifdef WITH_VST
#include "gui/elems/basics/statusButton.h"
#endif
#include "glue/main.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geMainIO : public gePack
{
public:
-
geMainIO(int x, int y);
void refresh();
void rebuild();
void setOutVol(float v);
- void setInVol (float v);
+ void setInVol(float v);
#ifdef WITH_VST
void setMasterFxOutFull(bool v);
void setMasterFxInFull(bool v);
#endif
-private:
-
- static void cb_outVol (Fl_Widget* /*w*/, void* p);
- static void cb_inVol (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_outVol(Fl_Widget* /*w*/, void* p);
+ static void cb_inVol(Fl_Widget* /*w*/, void* p);
static void cb_inToOut(Fl_Widget* /*w*/, void* p);
- void cb_outVol();
- void cb_inVol();
- void cb_inToOut();
+ void cb_outVol();
+ void cb_inVol();
+ void cb_inToOut();
#ifdef WITH_VST
static void cb_masterFxOut(Fl_Widget* /*w*/, void* p);
- static void cb_masterFxIn (Fl_Widget* /*w*/, void* p);
- void cb_masterFxOut();
- void cb_masterFxIn();
+ static void cb_masterFxIn(Fl_Widget* /*w*/, void* p);
+ void cb_masterFxOut();
+ void cb_masterFxIn();
#endif
c::main::IO m_io;
- geSoundMeter outMeter;
- geSoundMeter inMeter;
- geDial outVol;
- geDial inVol;
- geButton inToOut;
+ geSoundMeter outMeter;
+ geSoundMeter inMeter;
+ geDial outVol;
+ geDial inVol;
+ geButton inToOut;
#ifdef WITH_VST
geStatusButton masterFxOut;
geStatusButton masterFxIn;
#endif
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl_Menu_Button.H>
-#include "core/model/model.h"
+#include "mainMenu.h"
+#include "core/conf.h"
#include "core/const.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
-#include "core/conf.h"
+#include "core/model/model.h"
#include "core/patch.h"
-#include "utils/gui.h"
-#include "glue/storage.h"
#include "glue/main.h"
-#include "gui/elems/basics/boxtypes.h"
-#include "gui/elems/basics/button.h"
-#include "gui/dialogs/mainWindow.h"
+#include "glue/storage.h"
#include "gui/dialogs/about.h"
-#include "gui/dialogs/config.h"
-#include "gui/dialogs/warnings.h"
#include "gui/dialogs/browser/browserLoad.h"
#include "gui/dialogs/browser/browserSave.h"
+#include "gui/dialogs/config.h"
+#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/midiIO/midiInputMaster.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
#include "keyboard/keyboard.h"
-#include "mainMenu.h"
-
+#include "utils/gui.h"
+#include <FL/Fl_Menu_Button.H>
+#include <cassert>
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
+namespace giada
+{
namespace v
{
geMainMenu::geMainMenu(int x, int y)
add(config);
add(about);
- resizable(nullptr); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
file->callback(cb_file, (void*)this);
edit->callback(cb_edit, (void*)this);
- about->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ about->callback([](Fl_Widget* /*w*/, void* /*v*/) {
u::gui::openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT);
});
- config->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ config->callback([](Fl_Widget* /*w*/, void* /*v*/) {
u::gui::openSubWindow(G_MainWin, new gdConfig(400, 370), WID_CONFIG);
});
}
-
/* -------------------------------------------------------------------------- */
-
void geMainMenu::cb_file(Fl_Widget* /*w*/, void* p) { ((geMainMenu*)p)->cb_file(); }
void geMainMenu::cb_edit(Fl_Widget* /*w*/, void* p) { ((geMainMenu*)p)->cb_edit(); }
-
/* -------------------------------------------------------------------------- */
-
void geMainMenu::cb_file()
{
using namespace giada::m;
/* An Fl_Menu_Button is made of many Fl_Menu_Item */
Fl_Menu_Item menu[] = {
- {"Open project..."},
- {"Save project..."},
- {"Close project"},
+ {"Open project..."},
+ {"Save project..."},
+ {"Close project"},
#ifndef NDEBUG
- {"Debug stats"},
+ {"Debug stats"},
#endif
- {"Quit Giada"},
- {0}
- };
+ {"Quit Giada"},
+ {0}};
Fl_Menu_Button b(0, 0, 100, 50);
b.box(G_CUSTOM_BORDER_BOX);
b.textcolor(G_COLOR_LIGHT_2);
b.color(G_COLOR_GREY_2);
- const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
- if (!m) return;
+ const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
+ if (!m)
+ return;
- if (strcmp(m->label(), "Open project...") == 0) {
- gdWindow* childWin = new gdBrowserLoad("Open project",
- conf::conf.patchPath, c::storage::loadProject, 0);
+ if (strcmp(m->label(), "Open project...") == 0)
+ {
+ gdWindow* childWin = new gdBrowserLoad("Open project",
+ conf::conf.patchPath, c::storage::loadProject, 0);
u::gui::openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
}
- else
- if (strcmp(m->label(), "Save project...") == 0) {
- gdWindow* childWin = new gdBrowserSave("Save project", conf::conf.patchPath,
- patch::patch.name, c::storage::saveProject, 0);
+ else if (strcmp(m->label(), "Save project...") == 0)
+ {
+ gdWindow* childWin = new gdBrowserSave("Save project", conf::conf.patchPath,
+ patch::patch.name, c::storage::saveProject, 0);
u::gui::openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
}
- else
- if (strcmp(m->label(), "Close project") == 0) {
+ else if (strcmp(m->label(), "Close project") == 0)
+ {
c::main::closeProject();
}
#ifndef NDEBUG
- else
- if (strcmp(m->label(), "Debug stats") == 0) {
+ else if (strcmp(m->label(), "Debug stats") == 0)
+ {
m::model::debug();
}
#endif
- else
- if (strcmp(m->label(), "Quit Giada") == 0) {
+ else if (strcmp(m->label(), "Quit Giada") == 0)
+ {
G_MainWin->do_callback();
}
}
-
/* -------------------------------------------------------------------------- */
-
void geMainMenu::cb_edit()
{
Fl_Menu_Item menu[] = {
- {"Free all Sample channels"},
- {"Clear all actions"},
- {"Setup global MIDI input..."},
- {0}
- };
+ {"Free all Sample channels"},
+ {"Clear all actions"},
+ {"Setup global MIDI input..."},
+ {0}};
menu[0].deactivate();
menu[1].deactivate();
- if (m::mh::hasAudioData()) menu[0].activate();
- if (m::mh::hasActions()) menu[1].activate();
+ if (m::mh::hasAudioData())
+ menu[0].activate();
+ if (m::mh::hasActions())
+ menu[1].activate();
Fl_Menu_Button b(0, 0, 100, 50);
b.box(G_CUSTOM_BORDER_BOX);
b.color(G_COLOR_GREY_2);
const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
- if (!m) return;
+ if (!m)
+ return;
- if (strcmp(m->label(), "Free all Sample channels") == 0)
+ if (strcmp(m->label(), "Free all Sample channels") == 0)
c::main::clearAllSamples();
- else
- if (strcmp(m->label(), "Clear all actions") == 0)
+ else if (strcmp(m->label(), "Clear all actions") == 0)
c::main::clearAllActions();
- else
- if (strcmp(m->label(), "Setup global MIDI input...") == 0)
+ else if (strcmp(m->label(), "Setup global MIDI input...") == 0)
u::gui::openSubWindow(G_MainWin, new gdMidiInputMaster(), WID_MIDI_INPUT);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MAIN_MENU_H
#define GE_MAIN_MENU_H
-
#include "gui/elems/basics/pack.h"
-
class geButton;
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geMainMenu : public gePack
{
public:
-
geMainMenu(int x, int y);
-private:
-
+ private:
static void cb_file(Fl_Widget* /*w*/, void* p);
static void cb_edit(Fl_Widget* /*w*/, void* p);
- void cb_file();
- void cb_edit();
+ void cb_file();
+ void cb_edit();
geButton* file;
geButton* edit;
geButton* config;
geButton* about;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "mainTimer.h"
+#include "core/clock.h"
#include "core/const.h"
#include "core/graphics.h"
-#include "core/clock.h"
-#include "glue/main.h"
#include "glue/events.h"
-#include "utils/gui.h"
-#include "utils/string.h"
+#include "glue/main.h"
+#include "gui/dialogs/beatsInput.h"
+#include "gui/dialogs/bpmInput.h"
+#include "gui/dialogs/mainWindow.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
-#include "gui/dialogs/mainWindow.h"
-#include "gui/dialogs/bpmInput.h"
-#include "gui/dialogs/beatsInput.h"
-#include "mainTimer.h"
-
+#include "utils/gui.h"
+#include "utils/string.h"
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace v
+namespace giada::v
{
geMainTimer::geMainTimer(int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_bpm (0, 0, 60, G_GUI_UNIT)
-, m_meter (0, 0, 60, G_GUI_UNIT)
-, m_quantizer (0, 0, 60, G_GUI_UNIT, "", false)
+: gePack(x, y, Direction::HORIZONTAL)
+, m_bpm(0, 0, 60, G_GUI_UNIT)
+, m_meter(0, 0, 60, G_GUI_UNIT)
+, m_quantizer(0, 0, 60, G_GUI_UNIT, "", false)
, m_multiplier(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm)
-, m_divider (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
+, m_divider(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
{
add(&m_quantizer);
add(&m_bpm);
add(&m_multiplier);
add(&m_divider);
- resizable(nullptr); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
+
+ m_bpm.copy_tooltip("Beats per minute (BPM)");
+ m_meter.copy_tooltip("Beats and bars");
+ m_quantizer.copy_tooltip("Live quantizer");
+ m_multiplier.copy_tooltip("Beat multiplier");
+ m_divider.copy_tooltip("Beat divider");
m_bpm.callback(cb_bpm, (void*)this);
m_meter.callback(cb_meter, (void*)this);
- m_multiplier.callback(cb_multiplier, (void*)this);
+ m_multiplier.callback(cb_multiplier, (void*)this);
m_divider.callback(cb_divider, (void*)this);
m_quantizer.add("off", 0, cb_quantizer, (void*)this);
m_quantizer.value(0); // "off" by default
}
-
/* -------------------------------------------------------------------------- */
-
-void geMainTimer::cb_bpm (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_bpm(); }
-void geMainTimer::cb_meter (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_meter(); }
-void geMainTimer::cb_quantizer (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_quantizer(); }
+void geMainTimer::cb_bpm(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_bpm(); }
+void geMainTimer::cb_meter(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_meter(); }
+void geMainTimer::cb_quantizer(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_quantizer(); }
void geMainTimer::cb_multiplier(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_multiplier(); }
-void geMainTimer::cb_divider (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_divider(); }
-
+void geMainTimer::cb_divider(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_divider(); }
/* -------------------------------------------------------------------------- */
-
void geMainTimer::cb_bpm()
{
u::gui::openSubWindow(G_MainWin, new gdBpmInput(m_bpm.label()), WID_BPM);
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::cb_meter()
{
u::gui::openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS);
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::cb_quantizer()
{
c::main::quantize(m_quantizer.value());
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::cb_multiplier()
{
c::events::multiplyBeats();
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::cb_divider()
{
c::events::divideBeats();
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::refresh()
{
m_timer = c::main::getTimer();
- if (m_timer.isRecordingInput) {
+ if (m_timer.isRecordingInput)
+ {
m_bpm.deactivate();
m_meter.deactivate();
m_multiplier.deactivate();
m_divider.deactivate();
}
- else {
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
- /* Don't reactivate m_bpm when using JACK. It must stay disabled. */
- if (m_timer.isUsingJack)
- m_bpm.deactivate();
- else
- m_bpm.activate();
-#else
+ else
+ {
m_bpm.activate();
-#endif
- m_meter.activate();
+ m_meter.activate();
m_multiplier.activate();
- m_divider.activate();
+ m_divider.activate();
}
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::rebuild()
{
m_timer = c::main::getTimer();
setBpm(m_timer.bpm);
setMeter(m_timer.beats, m_timer.bars);
setQuantizer(m_timer.quantize);
-
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
- /* Can't change m_bpm from within Giada when using JACK. */
- if (m_timer.isUsingJack)
- m_bpm.deactivate();
-#endif
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::setBpm(const char* v)
{
m_bpm.copy_label(v);
}
-
void geMainTimer::setBpm(float v)
{
- m_bpm.copy_label(u::string::fToString((float) v, 1).c_str()); // Only 1 decimal place (e.g. 120.0)
+ m_bpm.copy_label(u::string::fToString(v, 1).c_str()); // Only 1 decimal place (e.g. 120.0)
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::setLock(bool v)
{
- if (v) {
- m_bpm.deactivate();
- m_meter.deactivate();
- m_multiplier.deactivate();
- m_divider.deactivate();
- }
- else {
- m_bpm.activate();
- m_meter.activate();
- m_multiplier.activate();
- m_divider.activate();
- }
+ if (v)
+ {
+ m_bpm.deactivate();
+ m_meter.deactivate();
+ m_multiplier.deactivate();
+ m_divider.deactivate();
+ }
+ else
+ {
+ m_bpm.activate();
+ m_meter.activate();
+ m_multiplier.activate();
+ m_divider.activate();
+ }
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::setQuantizer(int q)
{
m_quantizer.value(q);
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTimer::setMeter(int beats, int bars)
{
std::string s = std::to_string(beats) + "/" + std::to_string(bars);
m_meter.copy_label(s.c_str());
}
-
-}} // giada::v::
+} // namespace giada::v
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MAIN_TIMER_H
#define GE_MAIN_TIMER_H
-
#include "glue/main.h"
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
-
+#include "gui/elems/basics/pack.h"
class geButton;
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geChoice;
class geMainTimer : public gePack
{
public:
-
geMainTimer(int x, int y);
-
+
void refresh();
void rebuild();
void setLock(bool v);
-private:
-
- static void cb_bpm (Fl_Widget* /*w*/, void* p);
- static void cb_meter (Fl_Widget* /*w*/, void* p);
- static void cb_quantizer (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_bpm(Fl_Widget* /*w*/, void* p);
+ static void cb_meter(Fl_Widget* /*w*/, void* p);
+ static void cb_quantizer(Fl_Widget* /*w*/, void* p);
static void cb_multiplier(Fl_Widget* /*w*/, void* p);
- static void cb_divider (Fl_Widget* /*w*/, void* p);
- void cb_bpm();
- void cb_meter();
- void cb_quantizer();
- void cb_multiplier();
- void cb_divider();
+ static void cb_divider(Fl_Widget* /*w*/, void* p);
+ void cb_bpm();
+ void cb_meter();
+ void cb_quantizer();
+ void cb_multiplier();
+ void cb_divider();
c::main::Timer m_timer;
geButton m_multiplier;
geButton m_divider;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include "core/graphics.h"
-#include "core/conf.h"
+#include "mainTransport.h"
#include "core/clock.h"
-#include "core/sequencer.h"
+#include "core/conf.h"
+#include "core/const.h"
+#include "core/graphics.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
#include "core/recManager.h"
-#include "core/conf.h"
-#include "core/const.h"
-#include "glue/main.h"
+#include "core/sequencer.h"
#include "glue/events.h"
-#include "gui/elems/basics/button.h"
+#include "glue/main.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/statusButton.h"
-#include "mainTransport.h"
-
-namespace giada {
-namespace v
+namespace giada::v
{
geMainTransport::geMainTransport(int x, int y)
: gePack(x, y, Direction::HORIZONTAL)
+, m_rewind(0, 0, 25, 25, "", rewindOff_xpm, rewindOn_xpm)
+, m_play(0, 0, 25, 25, play_xpm, pause_xpm)
+, m_spacer1(0, 0, 10, 25)
+, m_recTriggerMode(0, 0, 15, 25, recTriggerModeOff_xpm, recTriggerModeOn_xpm)
+, m_recAction(0, 0, 25, 25, recOff_xpm, recOn_xpm)
+, m_recInput(0, 0, 25, 25, inputRecOff_xpm, inputRecOn_xpm)
+, m_inputRecMode(0, 0, 15, 25, freeInputRecOff_xpm, freeInputRecOn_xpm)
+, m_spacer2(0, 0, 10, 25)
+, m_metronome(0, 0, 15, 25, metronomeOff_xpm, metronomeOn_xpm)
{
- rewind = new geButton (0, 0, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
- play = new geStatusButton(0, 0, 25, 25, play_xpm, pause_xpm);
- spacer1 = new geBox (0, 0, 10, 25);
- recTriggerMode = new geStatusButton(0, 0, 15, 25, recTriggerModeOff_xpm, recTriggerModeOn_xpm);
- recAction = new geStatusButton(0, 0, 25, 25, recOff_xpm, recOn_xpm);
- recInput = new geStatusButton(0, 0, 25, 25, inputRecOff_xpm, inputRecOn_xpm);
- spacer2 = new geBox (0, 0, 10, 25);
- metronome = new geStatusButton(0, 0, 15, 25, metronomeOff_xpm, metronomeOn_xpm);
- add(rewind);
- add(play);
- add(spacer1);
- add(recTriggerMode);
- add(recAction);
- add(recInput);
- add(spacer2);
- add(metronome);
-
- rewind->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ add(&m_rewind);
+ add(&m_play);
+ add(&m_spacer1);
+ add(&m_recTriggerMode);
+ add(&m_recAction);
+ add(&m_recInput);
+ add(&m_inputRecMode);
+ add(&m_spacer2);
+ add(&m_metronome);
+
+ m_rewind.copy_tooltip("Rewind");
+ m_play.copy_tooltip("Play/Stop");
+ m_recTriggerMode.copy_tooltip("Record-on-signal mode\n\nIf enabled, action "
+ "and audio recording will start only when a signal (key press or audio) "
+ "is detected.");
+ m_recAction.copy_tooltip("Record actions");
+ m_recInput.copy_tooltip("Record audio");
+ m_inputRecMode.copy_tooltip("Free loop-length mode\n\nIf enabled, the sequencer "
+ "will adjust to the length of your first audio recording. "
+ "Available only if there are no other audio samples in the "
+ "project.");
+ m_metronome.copy_tooltip("Metronome");
+
+ m_rewind.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::events::rewindSequencer(Thread::MAIN);
});
- play->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ m_play.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::events::toggleSequencer(Thread::MAIN);
});
- recAction->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ m_recAction.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::events::toggleActionRecording();
});
- recInput->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ m_recInput.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::events::toggleInputRecording();
});
- recTriggerMode->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ m_recTriggerMode.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::main::toggleRecOnSignal();
});
- metronome->type(FL_TOGGLE_BUTTON);
- metronome->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ m_inputRecMode.callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::main::toggleFreeInputRec();
+ });
+
+ m_metronome.type(FL_TOGGLE_BUTTON);
+ m_metronome.callback([](Fl_Widget* /*w*/, void* /*v*/) {
c::events::toggleMetronome();
});
}
-
/* -------------------------------------------------------------------------- */
-
void geMainTransport::refresh()
{
- play->setStatus(m::clock::isRunning());
- recAction->setStatus(m::recManager::isRecordingAction());
- recInput->setStatus(m::recManager::isRecordingInput());
- metronome->setStatus(m::sequencer::isMetronomeOn());
- recTriggerMode->setStatus(m::conf::conf.recTriggerMode == RecTriggerMode::SIGNAL);
+ m_play.setStatus(m::clock::isRunning());
+ m_recAction.setStatus(m::recManager::isRecordingAction());
+ m_recInput.setStatus(m::recManager::isRecordingInput());
+ m_metronome.setStatus(m::sequencer::isMetronomeOn());
+ m_recTriggerMode.setStatus(m::conf::conf.recTriggerMode == RecTriggerMode::SIGNAL);
+ m_inputRecMode.setStatus(m::conf::conf.inputRecMode == InputRecMode::FREE);
}
-}} // giada::v::
+} // namespace giada::v
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MAIN_TRANSPORT_H
#define GE_MAIN_TRANSPORT_H
-
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/statusButton.h"
-
-class geButton;
-class geBox;
-class geStatusButton;
-
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geMainTransport : public gePack
{
public:
-
geMainTransport(int x, int y);
void refresh();
private:
-
- geButton* rewind;
- geStatusButton* play;
- geBox* spacer1;
- geStatusButton* recTriggerMode;
- geStatusButton* recAction;
- geStatusButton* recInput;
- geBox* spacer2;
- geStatusButton* metronome;
+ geButton m_rewind;
+ geStatusButton m_play;
+ geBox m_spacer1;
+ geStatusButton m_recTriggerMode;
+ geStatusButton m_recAction;
+ geStatusButton m_recInput;
+ geStatusButton m_inputRecMode;
+ geBox m_spacer2;
+ geStatusButton m_metronome;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * beatMeter
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "sequencer.h"
+#include "core/const.h"
+#include "gui/drawing.h"
+#include "utils/math.h"
+#include <FL/fl_draw.H>
+
+namespace giada::v
+{
+geSequencer::geSequencer(int x, int y, int w, int h)
+: Fl_Box(x, y, w, h)
+{
+ copy_tooltip("Main sequencer");
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::refresh()
+{
+ m_data = c::main::getSequencer();
+ redraw();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::draw()
+{
+ m_background = geompp::Rect(x(), y(), w(), h());
+ m_cell = geompp::Rect(x(), y(), w() / G_MAX_BEATS, h()).reduced(0, REC_BARS_H);
+
+ /* Cleanup */
+ drawRectf(m_background, FL_BACKGROUND_COLOR);
+
+ if (m_data.isFreeModeInputRec)
+ drawRecBars();
+
+ drawBody();
+ drawCursor();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::drawBody() const
+{
+ const geompp::Rect body = m_background.reduced(0, REC_BARS_H);
+ const geompp::Line line = m_cell.getHeightAsLine();
+
+ /* Background and borders. */
+
+ drawRectf(body, FL_BACKGROUND_COLOR);
+ drawRect(body, G_COLOR_GREY_4);
+
+ /* Beat lines. */
+
+ for (int i = 1; i <= m_data.beats; i++)
+ drawLine(line.withShiftedX(m_cell.w * i), G_COLOR_GREY_4);
+
+ /* Bar lines. */
+
+ const int delta = m_data.beats / m_data.bars;
+ for (int i = 1; i < m_data.bars; i++)
+ drawLine(line.withShiftedX(m_cell.w * i * delta), G_COLOR_LIGHT_1);
+
+ /* Unused grey area. */
+
+ drawRectf(body.withTrimmedLeft(m_data.beats * m_cell.w), G_COLOR_GREY_4);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::drawRecBars() const
+{
+ int length = u::math::map(m_data.recPosition, m_data.recMaxLength, w());
+
+ drawRectf(geompp::Rect(x(), y(), length, h()), G_COLOR_LIGHT_1);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::drawCursor(int beat, Fl_Color color) const
+{
+ // TODO withW(...): FLTK glitch?
+ drawRectf(m_cell.withShiftedX(beat * m_cell.w).reduced(CURSOR_PAD).withW(m_cell.w - CURSOR_PAD - 2), color);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geSequencer::drawCursor() const
+{
+ Fl_Color color = m_data.shouldBlink ? FL_BACKGROUND_COLOR : G_COLOR_LIGHT_1;
+
+ if (m_data.isFreeModeInputRec)
+ {
+ for (int i = 0; i < m_data.beats; i++)
+ drawCursor(i, color);
+ }
+ else
+ drawCursor(m_data.currentBeat, color);
+}
+} // namespace giada::v
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * beatMeter
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef GE_SEQUENCER_H
+#define GE_SEQUENCER_H
+
+#include "core/types.h"
+#include "deps/geompp/src/rect.hpp"
+#include "glue/main.h"
+#include <FL/Fl_Box.H>
+
+namespace giada::v
+{
+class geSequencer : public Fl_Box
+{
+public:
+ geSequencer(int x, int y, int w, int h);
+
+ void draw() override;
+
+ void refresh();
+
+private:
+ static constexpr int REC_BARS_H = 3;
+ static constexpr int CURSOR_PAD = 3;
+
+ void drawBody() const;
+ void drawCursor() const;
+ void drawCursor(int beat, Fl_Color col) const;
+ void drawRecBars() const;
+
+ c::main::Sequencer m_data;
+
+ geompp::Rect<int> m_background;
+ geompp::Rect<int> m_cell;
+};
+} // namespace giada::v
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include "utils/string.h"
-#include "gui/elems/basics/boxtypes.h"
+#include "midiLearner.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
-#include "midiLearner.h"
-
+#include "utils/string.h"
+#include <cassert>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geMidiLearner::geMidiLearner(int x, int y, std::string l, int param)
-: gePack (x, y, Direction::HORIZONTAL)
+: gePack(x, y, Direction::HORIZONTAL)
, onStartLearn(nullptr)
-, onStopLearn (nullptr)
+, onStopLearn(nullptr)
, onClearLearn(nullptr)
-, m_param (param)
-, m_text (0, 0, 146, 20, l.c_str())
-, m_valueBtn (0, 0, 80, 20)
-, m_button (0, 0, 50, 20, "learn")
+, m_param(param)
+, m_text(0, 0, 146, 20, l.c_str())
+, m_valueBtn(0, 0, 80, 20)
+, m_button(0, 0, 50, 20, "learn")
{
add(&m_text);
add(&m_valueBtn);
m_button.callback(cb_button, (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearner::cb_button(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onLearn(); }
-void geMidiLearner::cb_value(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onReset(); }
-
+void geMidiLearner::cb_value(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onReset(); }
/* -------------------------------------------------------------------------- */
-
void geMidiLearner::update(uint32_t value)
{
std::string tmp = "(not set)";
-
- if (value != 0x0) {
+
+ if (value != 0x0)
+ {
tmp = "0x" + u::string::iToString(value, /*hex=*/true);
- tmp.pop_back(); // Remove last two digits, useless in MIDI messages
- tmp.pop_back(); // Remove last two digits, useless in MIDI messages
+ tmp.pop_back(); // Remove last two digits, useless in MIDI messages
+ tmp.pop_back(); // Remove last two digits, useless in MIDI messages
}
m_valueBtn.copy_label(tmp.c_str());
- m_button.value(0);
+ m_button.value(0);
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearner::activate()
{
Fl_Group::activate();
m_button.activate();
}
-
void geMidiLearner::deactivate()
{
Fl_Group::deactivate();
m_button.deactivate();
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearner::onLearn() const
{
assert(onStartLearn != nullptr);
onStopLearn();
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearner::onReset() const
{
assert(onClearLearn != nullptr);
if (Fl::event_button() == FL_RIGHT_MOUSE)
- onClearLearn(m_param);
+ onClearLearn(m_param);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_MIDI_LEARNER_H
#define GE_MIDI_LEARNER_H
-
-#include <functional>
-#include <string>
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
-
+#include "gui/elems/basics/pack.h"
+#include <functional>
+#include <string>
class geBox;
class geButton;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geMidiLearner : public gePack
{
public:
-
geMidiLearner(int x, int y, std::string l, int param);
/* update
std::function<void()> onStopLearn;
std::function<void(uint32_t)> onClearLearn;
-protected:
-
+ protected:
/* m_param
Parameter index to be learnt. */
geButton m_valueBtn;
geButton m_button;
-private:
-
+ private:
static void cb_button(Fl_Widget* /*w*/, void* p);
- static void cb_value (Fl_Widget* /*w*/, void* p);
+ static void cb_value(Fl_Widget* /*w*/, void* p);
void onLearn() const;
void onReset() const;
-
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
+#include "midiLearnerPack.h"
#include "core/const.h"
#include "glue/io.h"
#include "gui/elems/basics/box.h"
-#include "midiLearnerPack.h"
-
+#include <cassert>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
constexpr int LEARNER_WIDTH = 284;
-
/* -------------------------------------------------------------------------- */
-
geMidiLearnerPack::geMidiLearnerPack(int X, int Y, std::string title)
: gePack(X, Y, Direction::VERTICAL)
{
end();
- if (title != "") {
+ if (title != "")
+ {
geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, title.c_str());
header->box(FL_BORDER_BOX);
add(header);
}
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearnerPack::setCallbacks(std::function<void(uint32_t)> s, std::function<void(uint32_t)> c)
{
- m_onStartLearn = s;
- m_onClearLearn = c;
+ m_onStartLearn = s;
+ m_onClearLearn = c;
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearnerPack::addMidiLearner(std::string label, int param, bool visible)
{
geMidiLearner* l = new geMidiLearner(0, 0, label, param);
-
+
l->onStartLearn = m_onStartLearn;
l->onClearLearn = m_onClearLearn;
- l->onStopLearn = [] () { c::io::stopMidiLearn(); };
+ l->onStopLearn = []() { c::io::stopMidiLearn(); };
add(l);
- if (!visible) l->hide();
+ if (!visible)
+ l->hide();
learners.push_back(l);
}
-
/* -------------------------------------------------------------------------- */
-
void geMidiLearnerPack::setEnabled(bool v)
{
- if (v) for (auto* l : learners) l->activate();
- else for (auto* l : learners) l->deactivate();
+ if (v)
+ for (auto* l : learners)
+ l->activate();
+ else
+ for (auto* l : learners)
+ l->deactivate();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_LEARNER_PACK_H
#define GE_LEARNER_PACK_H
-
-#include <string>
-#include <vector>
#include "gui/elems/basics/pack.h"
#include "gui/elems/midiIO/midiLearner.h"
+#include <string>
+#include <vector>
-
-namespace giada {
+namespace giada
+{
namespace v
{
class geMidiLearnerPack : public gePack
{
public:
+ geMidiLearnerPack(int x, int y, std::string title = "");
- geMidiLearnerPack(int x, int y, std::string title="");
-
- void setCallbacks(std::function<void(uint32_t)>, std::function<void(uint32_t)>);
- void addMidiLearner(std::string label, int param, bool visible=true);
- void setEnabled(bool v);
+ void setCallbacks(std::function<void(uint32_t)>, std::function<void(uint32_t)>);
+ void addMidiLearner(std::string label, int param, bool visible = true);
+ void setEnabled(bool v);
- std::vector<geMidiLearner*> learners;
+ std::vector<geMidiLearner*> learners;
-private:
-
- std::function<void(uint32_t)> m_onStartLearn;
- std::function<void(uint32_t)> m_onClearLearn;
+ private:
+ std::function<void(uint32_t)> m_onStartLearn;
+ std::function<void(uint32_t)> m_onClearLearn;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <FL/fl_draw.H>
-#include "core/plugins/plugin.h"
+#include "pluginBrowser.h"
#include "core/const.h"
-#include "core/plugins/pluginManager.h"
+#include "core/plugins/plugin.h"
#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
#include "gui/elems/basics/boxtypes.h"
-#include "pluginBrowser.h"
-
+#include <FL/fl_draw.H>
-namespace giada {
+namespace giada
+{
namespace v
{
gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
- : Fl_Browser(x, y, w, h)
+: Fl_Browser(x, y, w, h)
{
box(G_CUSTOM_BORDER_BOX);
textsize(G_GUI_FONT_SIZE_BASE);
computeWidths();
column_widths(widths);
- column_char('\t'); // tabs as column delimiters
+ column_char('\t'); // tabs as column delimiters
refresh();
end();
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginBrowser::refresh()
{
clear();
add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
add("---\t---\t---\t---\t---");
- for (int i=0; i<m::pluginManager::countAvailablePlugins(); i++) {
+ for (int i = 0; i < m::pluginManager::countAvailablePlugins(); i++)
+ {
m::pluginManager::PluginInfo pi = m::pluginManager::getAvailablePluginInfo(i);
- std::string m = m::pluginManager::doesPluginExist(pi.uid) ? "" : "@-";
- std::string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
- pi.category + "\t" + m + pi.format + "\t" + m + pi.uid;
+ std::string m = m::pluginManager::doesPluginExist(pi.uid) ? "" : "@-";
+ std::string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
+ pi.category + "\t" + m + pi.format + "\t" + m + pi.uid;
add(s.c_str());
}
- for (int i = 0; i < m::pluginManager::countUnknownPlugins(); i++) {
+ for (int i = 0; i < m::pluginManager::countUnknownPlugins(); i++)
+ {
std::string s = "?\t?\t?\t?\t? " + m::pluginManager::getUnknownPluginInfo(i) + " ?";
add(s.c_str());
}
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginBrowser::computeWidths()
{
int w0, w1, w3;
- for (int i = 0; i < m::pluginManager::countAvailablePlugins(); i++) {
+ for (int i = 0; i < m::pluginManager::countAvailablePlugins(); i++)
+ {
m::pluginManager::PluginInfo pi = m::pluginManager::getAvailablePluginInfo(i);
- w0 = (int) fl_width(pi.name.c_str());
- w1 = (int) fl_width(pi.manufacturerName.c_str());
- w3 = (int) fl_width(pi.format.c_str());
- if (w0 > widths[0]) widths[0] = w0;
- if (w1 > widths[1]) widths[1] = w1;
- if (w3 > widths[3]) widths[3] = w3;
+ w0 = (int)fl_width(pi.name.c_str());
+ w1 = (int)fl_width(pi.manufacturerName.c_str());
+ w3 = (int)fl_width(pi.format.c_str());
+ if (w0 > widths[0])
+ widths[0] = w0;
+ if (w1 > widths[1])
+ widths[1] = w1;
+ if (w3 > widths[3])
+ widths[3] = w3;
}
widths[0] += 60;
widths[1] += 60;
widths[3] += 60;
widths[4] = 0;
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GE_PLUGIN_BROWSER_H
#define GE_PLUGIN_BROWSER_H
-
#include <FL/Fl_Browser.H>
-
-namespace giada {
+namespace giada
+{
namespace v
{
class gePluginBrowser : public Fl_Browser
{
public:
-
gePluginBrowser(int x, int y, int w, int h);
void refresh();
-private:
+ private:
+ void computeWidths();
- void computeWidths();
-
- int widths[5] = {0};
+ int widths[5] = {0};
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
-#include <cassert>
-#include <string>
+#include "pluginElement.h"
#include "core/graphics.h"
-#include "core/plugins/pluginHost.h"
#include "core/plugins/plugin.h"
-#include "utils/gui.h"
-#include "utils/log.h"
+#include "core/plugins/pluginHost.h"
#include "glue/plugin.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/choice.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/pluginList.h"
-#include "gui/dialogs/pluginWindowGUI.h"
#include "gui/dialogs/pluginWindow.h"
-#include "pluginElement.h"
-
+#include "gui/dialogs/pluginWindowGUI.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "utils/gui.h"
+#include "utils/log.h"
+#include <cassert>
+#include <string>
-namespace giada {
+namespace giada
+{
namespace v
{
gePluginElement::gePluginElement(int x, int y, c::plugin::Plugin data)
-: gePack (x, y, Direction::HORIZONTAL)
-, button (0, 0, 196, G_GUI_UNIT)
-, program (0, 0, 132, G_GUI_UNIT)
-, bypass (0, 0, G_GUI_UNIT, G_GUI_UNIT)
-, shiftUp (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm)
+: gePack(x, y, Direction::HORIZONTAL)
+, button(0, 0, 196, G_GUI_UNIT)
+, program(0, 0, 132, G_GUI_UNIT)
+, bypass(0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, shiftUp(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm)
, shiftDown(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm)
-, remove (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxRemoveOff_xpm, fxRemoveOn_xpm)
-, m_plugin (data)
+, remove(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxRemoveOff_xpm, fxRemoveOn_xpm)
+, m_plugin(data)
{
add(&button);
add(&program);
remove.callback(cb_removePlugin, (void*)this);
- if (!m_plugin.valid) {
+ if (!m_plugin.valid)
+ {
button.copy_label(m_plugin.uniqueId.c_str());
button.deactivate();
bypass.deactivate();
for (const auto& p : m_plugin.programs)
program.add(u::gui::removeFltkChars(p.name).c_str());
- if (program.size() == 0) {
+ if (program.size() == 0)
+ {
program.add("-- no programs --\0");
program.deactivate();
}
shiftDown.callback(cb_shiftDown, (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
ID gePluginElement::getPluginId() const
{
return m_plugin.id;
}
+const m::Plugin& gePluginElement::getPluginRef() const
+{
+ return m_plugin.getPluginRef();
+}
/* -------------------------------------------------------------------------- */
-
-void gePluginElement::cb_removePlugin (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_removePlugin(); }
+void gePluginElement::cb_removePlugin(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_removePlugin(); }
void gePluginElement::cb_openPluginWindow(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_openPluginWindow(); }
-void gePluginElement::cb_setBypass (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
-void gePluginElement::cb_shiftUp (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
-void gePluginElement::cb_shiftDown (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
-void gePluginElement::cb_setProgram (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
-
+void gePluginElement::cb_setBypass(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
+void gePluginElement::cb_shiftUp(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
+void gePluginElement::cb_shiftDown(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
+void gePluginElement::cb_setProgram(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
/* -------------------------------------------------------------------------- */
-
void gePluginElement::cb_shiftUp()
{
const gdPluginList* parent = static_cast<const gdPluginList*>(window());
- c::plugin::swapPlugins(m_plugin.id, parent->getPrevElement(*this).getPluginId(), m_plugin.channelId);
+ c::plugin::swapPlugins(m_plugin.getPluginRef(), parent->getPrevElement(*this).getPluginRef(), m_plugin.channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginElement::cb_shiftDown()
{
const gdPluginList* parent = static_cast<const gdPluginList*>(window());
- c::plugin::swapPlugins(m_plugin.id, parent->getNextElement(*this).getPluginId(), m_plugin.channelId);
+ c::plugin::swapPlugins(m_plugin.getPluginRef(), parent->getNextElement(*this).getPluginRef(), m_plugin.channelId);
}
-
/* -------------------------------------------------------------------------- */
-
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'.*/
-
+
static_cast<gdWindow*>(window())->delSubWindow(m_plugin.id + 1);
- c::plugin::freePlugin(m_plugin.id, m_plugin.channelId);
+ c::plugin::freePlugin(m_plugin.getPluginRef(), m_plugin.channelId);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginElement::cb_openPluginWindow()
{
/* The new pluginWindow has id = id_plugin + 1, because id=0 is reserved for
gdWindow* parent = static_cast<gdWindow*>(window());
gdWindow* child = parent->getChild(pwid);
- if (child != nullptr) {
- child->show(); // Raise it to top
+ if (child != nullptr)
+ {
+ child->show(); // Raise it to top
}
- else {
+ else
+ {
if (m_plugin.hasEditor)
child = new gdPluginWindowGUI(m_plugin);
- else
+ else
child = new gdPluginWindow(m_plugin);
child->setId(pwid);
parent->addSubWindow(child);
}
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginElement::cb_setBypass()
{
c::plugin::toggleBypass(m_plugin.id);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginElement::cb_setProgram()
{
c::plugin::setProgram(m_plugin.id, program.value());
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GE_PLUGIN_ELEMENT_H
#define GE_PLUGIN_ELEMENT_H
-
-#include "gui/elems/basics/pack.h"
+#include "glue/plugin.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
-#include "glue/plugin.h"
-
+#include "gui/elems/basics/pack.h"
-namespace giada {
+namespace giada
+{
namespace v
{
class gePluginElement : public gePack
{
public:
-
gePluginElement(int x, int y, c::plugin::Plugin);
- ID getPluginId() const;
+ ID getPluginId() const;
+ const m::Plugin& getPluginRef() const;
geButton button;
geChoice program;
geButton shiftDown;
geButton remove;
-private:
-
+ private:
static void cb_removePlugin(Fl_Widget* /*w*/, void* p);
static void cb_openPluginWindow(Fl_Widget* /*w*/, void* p);
static void cb_setBypass(Fl_Widget* /*w*/, void* p);
static void cb_shiftUp(Fl_Widget* /*w*/, void* p);
static void cb_shiftDown(Fl_Widget* /*w*/, void* p);
static void cb_setProgram(Fl_Widget* /*w*/, void* p);
- void cb_removePlugin();
- void cb_openPluginWindow();
- void cb_setBypass();
- void cb_shiftUp();
- void cb_shiftDown();
- void cb_setProgram();
+ void cb_removePlugin();
+ void cb_openPluginWindow();
+ void cb_setBypass();
+ void cb_shiftUp();
+ void cb_shiftDown();
+ void cb_setProgram();
c::plugin::Plugin m_plugin;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
+#include "pluginParameter.h"
#include "core/const.h"
-#include "glue/plugin.h"
#include "glue/events.h"
-#include "gui/elems/basics/boxtypes.h"
+#include "glue/plugin.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/slider.h"
-#include "pluginParameter.h"
-
-namespace giada {
+namespace giada
+{
namespace v
{
gePluginParameter::gePluginParameter(int X, int Y, int W, int labelWidth, const c::plugin::Param p)
-: Fl_Group (X, Y, W, G_GUI_UNIT)
-, m_param (p)
+: Fl_Group(X, Y, W, G_GUI_UNIT)
+, m_param(p)
{
begin();
- const int VALUE_WIDTH = 100;
+ const int VALUE_WIDTH = 100;
+
+ m_label = new geBox(x(), y(), labelWidth, G_GUI_UNIT);
+ m_label->copy_label(m_param.name.c_str());
- m_label = new geBox(x(), y(), labelWidth, G_GUI_UNIT);
- m_label->copy_label(m_param.name.c_str());
+ m_slider = new geSlider(m_label->x() + m_label->w() + G_GUI_OUTER_MARGIN, y(),
+ w() - (m_label->x() + m_label->w() + G_GUI_OUTER_MARGIN) - VALUE_WIDTH, G_GUI_UNIT);
+ m_slider->value(m_param.value);
+ m_slider->callback(cb_setValue, (void*)this);
- m_slider = new geSlider(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN, y(),
- w()-(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN)-VALUE_WIDTH, G_GUI_UNIT);
- m_slider->value(m_param.value);
- m_slider->callback(cb_setValue, (void*)this);
+ m_value = new geBox(m_slider->x() + m_slider->w() + G_GUI_OUTER_MARGIN, y(), VALUE_WIDTH, G_GUI_UNIT);
+ m_value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+ m_value->box(G_CUSTOM_BORDER_BOX);
- m_value = new geBox(m_slider->x()+m_slider->w()+G_GUI_OUTER_MARGIN, y(), VALUE_WIDTH, G_GUI_UNIT);
- m_value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
- m_value->box(G_CUSTOM_BORDER_BOX);
-
end();
resizable(m_slider);
update(m_param, false);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginParameter::cb_setValue(Fl_Widget* /*w*/, void* p) { ((gePluginParameter*)p)->cb_setValue(); }
-
/* -------------------------------------------------------------------------- */
-
void gePluginParameter::cb_setValue()
{
- c::events::setPluginParameter(m_param.pluginId, m_param.index, m_slider->value(),
- /*gui=*/true);
+ c::events::setPluginParameter(m_param.pluginId, m_param.index,
+ m_slider->value(), /*gui=*/true);
}
-
/* -------------------------------------------------------------------------- */
-
void gePluginParameter::update(const c::plugin::Param& p, bool changeSlider)
{
m_value->copy_label(std::string(p.text + " " + p.label).c_str());
if (changeSlider)
m_slider->value(p.value);
}
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif // #ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifdef WITH_VST
-
#ifndef GE_PLUGIN_PARAMETER_H
#define GE_PLUGIN_PARAMETER_H
-
-#include <FL/Fl_Group.H>
#include "core/types.h"
-
+#include "glue/plugin.h"
+#include <FL/Fl_Group.H>
class geBox;
class geSlider;
-
-namespace giada {
-namespace c {
-namespace plugin
-{
-struct Param;
-}}
-namespace v
+namespace giada::v
{
class gePluginParameter : public Fl_Group
{
public:
-
gePluginParameter(int x, int y, int w, int labelWidth, const c::plugin::Param);
void update(const c::plugin::Param& p, bool changeSlider);
-private:
-
+ private:
static void cb_setValue(Fl_Widget* /*w*/, void* p);
- void cb_setValue();
+ void cb_setValue();
- const c::plugin::Param m_param;
+ const c::plugin::Param m_param;
geBox* m_label;
geSlider* m_slider;
geBox* m_value;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
+#include "boostTool.h"
#include "core/const.h"
-#include "core/waveFx.h"
+#include "core/waveFx.h"
#include "glue/channel.h"
-#include "utils/gui.h"
-#include "utils/string.h"
-#include "utils/math.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/dial.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "utils/gui.h"
+#include "utils/math.h"
+#include "utils/string.h"
#include "waveTools.h"
-#include "boostTool.h"
-
+#include <FL/Fl.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geBoostTool::geBoostTool(int X, int Y)
: Fl_Pack(X, Y, 220, G_GUI_UNIT)
spacing(G_GUI_INNER_MARGIN);
begin();
- label = new geBox (0, 0, u::gui::getStringWidth("Boost"), G_GUI_UNIT, "Boost", FL_ALIGN_RIGHT);
- dial = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- input = new geInput (0, 0, 70, G_GUI_UNIT);
- normalize = new geButton(0, 0, 70, G_GUI_UNIT, "Normalize");
+ label = new geBox(0, 0, u::gui::getStringWidth("Boost"), G_GUI_UNIT, "Boost", FL_ALIGN_RIGHT);
+ dial = new geDial(0, 0, G_GUI_UNIT, G_GUI_UNIT);
+ input = new geInput(0, 0, 70, G_GUI_UNIT);
+ normalize = new geButton(0, 0, 70, G_GUI_UNIT, "Normalize");
end();
dial->range(1.0f, 10.0f);
normalize->callback(cb_normalize, (void*)this);
}
-
/* -------------------------------------------------------------------------- */
-
void geBoostTool::rebuild()
{
/*
dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);*/
}
-
/* -------------------------------------------------------------------------- */
-
-void geBoostTool::cb_setBoost (Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_setBoost(); }
+void geBoostTool::cb_setBoost(Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_setBoost(); }
void geBoostTool::cb_setBoostNum(Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_setBoostNum(); }
-void geBoostTool::cb_normalize (Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_normalize(); }
-
+void geBoostTool::cb_normalize(Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_normalize(); }
/* -------------------------------------------------------------------------- */
-
void geBoostTool::cb_setBoost()
{
/*const m::SampleChannel* ch = static_cast<gdSampleEditor*>(window())->ch;
c::channel::setBoost(ch->id, dial->value());*/
}
-
/* -------------------------------------------------------------------------- */
-
void geBoostTool::cb_setBoostNum()
{
/*const m::SampleChannel* ch = static_cast<gdSampleEditor*>(window())->ch;
c::channel::setBoost(ch->id, u::math::dBtoLinear(atof(input->value())));*/
}
-
/* -------------------------------------------------------------------------- */
-
void geBoostTool::cb_normalize()
{
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_BOOST_TOOL_H
#define GE_BOOST_TOOL_H
-
#include <FL/Fl_Pack.H>
-
class geDial;
class geInput;
class geButton;
class geBox;
-
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
class geBoostTool : public Fl_Pack
{
public:
-
geBoostTool(int x, int y);
void rebuild();
-private:
-
+ private:
static void cb_setBoost(Fl_Widget* /*w*/, void* p);
static void cb_setBoostNum(Fl_Widget* /*w*/, void* p);
static void cb_normalize(Fl_Widget* /*w*/, void* p);
- void cb_setBoost();
- void cb_setBoostNum();
- void cb_normalize();
+ void cb_setBoost();
+ void cb_setBoostNum();
+ void cb_normalize();
geBox* label;
geDial* dial;
geInput* input;
geButton* normalize;
};
-}} // giada::v::
-
+} // namespace v
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include "core/model/model.h"
+#include "panTool.h"
#include "core/const.h"
-#include "core/waveFx.h"
+#include "core/model/model.h"
+#include "core/waveFx.h"
#include "glue/events.h"
+#include "gui/dialogs/sampleEditor.h"
#include "utils/gui.h"
#include "utils/math.h"
#include "utils/string.h"
-#include "gui/dialogs/sampleEditor.h"
#include "waveTools.h"
-#include "panTool.h"
-
+#include <FL/Fl.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gePanTool::gePanTool(const c::sampleEditor::Data& d, int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_data (nullptr)
+: gePack(x, y, Direction::HORIZONTAL)
+, m_data(nullptr)
, m_label(0, 0, 60, G_GUI_UNIT, "Pan", FL_ALIGN_LEFT)
-, m_dial (0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, m_dial(0, 0, G_GUI_UNIT, G_GUI_UNIT)
, m_input(0, 0, 70, G_GUI_UNIT)
, m_reset(0, 0, 70, G_GUI_UNIT, "Reset")
{
add(&m_label);
- add(&m_dial);
+ add(&m_dial);
add(&m_input);
add(&m_reset);
rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void gePanTool::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
update(m_data->pan);
-
}
-
/* -------------------------------------------------------------------------- */
-
void gePanTool::update(float v)
{
m_dial.value(v);
- if (v < 0.5f) {
- std::string tmp = u::string::iToString((int) ((-v * 200.0f) + 100.0f)) + " L";
+ if (v < 0.5f)
+ {
+ std::string tmp = u::string::iToString((int)((-v * 200.0f) + 100.0f)) + " L";
m_input.value(tmp.c_str());
}
- else
- if (v == 0.5)
+ else if (v == 0.5)
m_input.value("C");
- else {
- std::string tmp = u::string::iToString((int) ((v * 200.0f) - 100.0f)) + " R";
+ else
+ {
+ std::string tmp = u::string::iToString((int)((v * 200.0f) - 100.0f)) + " R";
m_input.value(tmp.c_str());
}
}
-
/* -------------------------------------------------------------------------- */
-
-void gePanTool::cb_panning (Fl_Widget* /*w*/, void* p) { ((gePanTool*)p)->cb_panning(); }
+void gePanTool::cb_panning(Fl_Widget* /*w*/, void* p) { ((gePanTool*)p)->cb_panning(); }
void gePanTool::cb_panReset(Fl_Widget* /*w*/, void* p) { ((gePanTool*)p)->cb_panReset(); }
-
-
/* -------------------------------------------------------------------------- */
-
void gePanTool::cb_panning()
{
c::events::sendChannelPan(m_data->channelId, m_dial.value());
}
-
/* -------------------------------------------------------------------------- */
-
void gePanTool::cb_panReset()
{
c::events::sendChannelPan(m_data->channelId, 0.5f);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PAN_TOOL_H
#define GE_PAN_TOOL_H
-
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/button.h"
-
+#include "gui/elems/basics/pack.h"
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class gePanTool : public gePack
{
public:
-
gePanTool(const c::sampleEditor::Data& d, int x, int y);
void rebuild(const c::sampleEditor::Data& d);
void update(float v);
-private:
-
- static void cb_panning (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_panning(Fl_Widget* /*w*/, void* p);
static void cb_panReset(Fl_Widget* /*w*/, void* p);
- void cb_panning();
- void cb_panReset();
+ void cb_panning();
+ void cb_panReset();
const c::sampleEditor::Data* m_data;
geInput m_input;
geButton m_reset;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "core/const.h"
-#include "core/graphics.h"
+#include "pitchTool.h"
#include "core/clock.h"
+#include "core/const.h"
+#include "core/graphics.h"
+#include "core/model/model.h"
#include "glue/events.h"
-#include "utils/gui.h"
-#include "utils/string.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/dial.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
-#include "pitchTool.h"
-
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <FL/Fl.H>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
gePitchTool::gePitchTool(const c::sampleEditor::Data& d, int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_data (nullptr)
-, m_label (0, 0, 60, G_GUI_UNIT, "Pitch", FL_ALIGN_LEFT)
-, m_dial (0, 0, G_GUI_UNIT, G_GUI_UNIT)
-, m_input (0, 0, 70, G_GUI_UNIT)
-, m_pitchToBar (0, 0, 70, G_GUI_UNIT, "To bar")
+: gePack(x, y, Direction::HORIZONTAL)
+, m_data(nullptr)
+, m_label(0, 0, 60, G_GUI_UNIT, "Pitch", FL_ALIGN_LEFT)
+, m_dial(0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, m_input(0, 0, 70, G_GUI_UNIT)
+, m_pitchToBar(0, 0, 70, G_GUI_UNIT, "To bar")
, m_pitchToSong(0, 0, 70, G_GUI_UNIT, "To song")
-, m_pitchHalf (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
+, m_pitchHalf(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
, m_pitchDouble(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm)
-, m_pitchReset (0, 0, 70, G_GUI_UNIT, "Reset")
+, m_pitchReset(0, 0, 70, G_GUI_UNIT, "Reset")
{
add(&m_label);
add(&m_dial);
rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
update(m_data->pitch, /*isDial=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::update(float v, bool isDial)
{
m_input.value(u::string::fToString(v, 4).c_str()); // 4 digits
m_dial.value(v);
}
-
/* -------------------------------------------------------------------------- */
-
-void gePitchTool::cb_setPitch (Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitch(); }
-void gePitchTool::cb_setPitchToBar (Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchToBar(); }
+void gePitchTool::cb_setPitch(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitch(); }
+void gePitchTool::cb_setPitchToBar(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchToBar(); }
void gePitchTool::cb_setPitchToSong(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchToSong(); }
-void gePitchTool::cb_setPitchHalf (Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchHalf(); }
+void gePitchTool::cb_setPitchHalf(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchHalf(); }
void gePitchTool::cb_setPitchDouble(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchDouble(); }
-void gePitchTool::cb_resetPitch (Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_resetPitch(); }
-void gePitchTool::cb_setPitchNum (Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchNum(); }
-
+void gePitchTool::cb_resetPitch(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_resetPitch(); }
+void gePitchTool::cb_setPitchNum(Fl_Widget* /*w*/, void* p) { ((gePitchTool*)p)->cb_setPitchNum(); }
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitch()
{
c::events::setChannelPitch(m_data->channelId, m_dial.value(), Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitchNum()
{
- c::events::setChannelPitch(m_data->channelId, atof(m_input.value()), Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, atof(m_input.value()), Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitchHalf()
{
- c::events::setChannelPitch(m_data->channelId, m_dial.value() / 2, Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, m_dial.value() / 2, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitchDouble()
{
- c::events::setChannelPitch(m_data->channelId, m_dial.value() * 2, Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, m_dial.value() * 2, Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitchToBar()
{
- c::events::setChannelPitch(m_data->channelId, m_data->end / (float) m::clock::getFramesInBar(),
- Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, m_data->end / (float)m::clock::getFramesInBar(),
+ Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_setPitchToSong()
{
- c::events::setChannelPitch(m_data->channelId, m_data->end / (float) m::clock::getFramesInLoop(),
- Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, m_data->end / (float)m::clock::getFramesInLoop(),
+ Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void gePitchTool::cb_resetPitch()
{
- c::events::setChannelPitch(m_data->channelId, G_DEFAULT_PITCH, Thread::MAIN);
+ c::events::setChannelPitch(m_data->channelId, G_DEFAULT_PITCH, Thread::MAIN);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_PITCH_TOOL_H
#define GE_PITCH_TOOL_H
-
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/button.h"
-
+#include "gui/elems/basics/pack.h"
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class gePitchTool : public gePack
{
public:
-
gePitchTool(const c::sampleEditor::Data& d, int x, int y);
void rebuild(const c::sampleEditor::Data& d);
- void update(float v, bool isDial=false);
+ void update(float v, bool isDial = false);
-private:
-
- static void cb_setPitch (Fl_Widget* /*w*/, void* p);
- static void cb_setPitchToBar (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_setPitch(Fl_Widget* /*w*/, void* p);
+ static void cb_setPitchToBar(Fl_Widget* /*w*/, void* p);
static void cb_setPitchToSong(Fl_Widget* /*w*/, void* p);
- static void cb_setPitchHalf (Fl_Widget* /*w*/, void* p);
+ static void cb_setPitchHalf(Fl_Widget* /*w*/, void* p);
static void cb_setPitchDouble(Fl_Widget* /*w*/, void* p);
- static void cb_resetPitch (Fl_Widget* /*w*/, void* p);
- static void cb_setPitchNum (Fl_Widget* /*w*/, void* p);
- void cb_setPitch();
- void cb_setPitchToBar();
- void cb_setPitchToSong();
- void cb_setPitchHalf();
- void cb_setPitchDouble();
- void cb_resetPitch();
- void cb_setPitchNum();
+ static void cb_resetPitch(Fl_Widget* /*w*/, void* p);
+ static void cb_setPitchNum(Fl_Widget* /*w*/, void* p);
+ void cb_setPitch();
+ void cb_setPitchToBar();
+ void cb_setPitchToSong();
+ void cb_setPitchHalf();
+ void cb_setPitchDouble();
+ void cb_resetPitch();
+ void cb_setPitchNum();
const c::sampleEditor::Data* m_data;
geButton m_pitchDouble;
geButton m_pitchReset;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <FL/Fl.H>
+#include "rangeTool.h"
#include "core/model/model.h"
#include "core/wave.h"
#include "glue/channel.h"
#include "glue/sampleEditor.h"
+#include "gui/dialogs/sampleEditor.h"
#include "utils/gui.h"
#include "utils/string.h"
-#include "gui/dialogs/sampleEditor.h"
#include "waveTools.h"
-#include "rangeTool.h"
-
+#include <FL/Fl.H>
+#include <cassert>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geRangeTool::geRangeTool(const c::sampleEditor::Data& d, int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_data (nullptr)
+: gePack(x, y, Direction::HORIZONTAL)
+, m_data(nullptr)
, m_label(0, 0, 60, G_GUI_UNIT, "Range", FL_ALIGN_LEFT)
, m_begin(0, 0, 70, G_GUI_UNIT)
-, m_end (0, 0, 70, G_GUI_UNIT)
+, m_end(0, 0, 70, G_GUI_UNIT)
, m_reset(0, 0, 70, G_GUI_UNIT, "Reset")
{
add(&m_label);
m_begin.type(FL_INT_INPUT);
m_begin.when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
m_begin.callback(cb_setChanPos, this);
-
+
m_end.type(FL_INT_INPUT);
m_end.when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
m_end.callback(cb_setChanPos, this);
rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void geRangeTool::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
update(m_data->begin, m_data->end);
}
-
/* -------------------------------------------------------------------------- */
-
void geRangeTool::update(Frame begin, Frame end)
{
m_begin.value(std::to_string(begin).c_str());
/* -------------------------------------------------------------------------- */
-
-void geRangeTool::cb_setChanPos (Fl_Widget* /*w*/, void* p) { ((geRangeTool*)p)->cb_setChanPos(); }
+void geRangeTool::cb_setChanPos(Fl_Widget* /*w*/, void* p) { ((geRangeTool*)p)->cb_setChanPos(); }
void geRangeTool::cb_resetStartEnd(Fl_Widget* /*w*/, void* p) { ((geRangeTool*)p)->cb_resetStartEnd(); }
-
/* -------------------------------------------------------------------------- */
-
void geRangeTool::cb_setChanPos()
{
c::sampleEditor::setBeginEnd(m_data->channelId, atoi(m_begin.value()), atoi(m_end.value()));
}
-
/* -------------------------------------------------------------------------- */
-
void geRangeTool::cb_resetStartEnd()
{
c::sampleEditor::setBeginEnd(m_data->channelId, 0, m_data->waveSize - 1);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_RANGE_TOOL_H
#define GE_RANGE_TOOL_H
-
-#include "gui/elems/basics/pack.h"
+#include "core/types.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/pack.h"
-
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class geRangeTool : public gePack
{
public:
-
geRangeTool(const c::sampleEditor::Data& d, int x, int y);
void rebuild(const c::sampleEditor::Data& d);
void update(Frame begin, Frame end);
-private:
-
- static void cb_setChanPos (Fl_Widget* /*w*/, void* p);
+ private:
+ static void cb_setChanPos(Fl_Widget* /*w*/, void* p);
static void cb_resetStartEnd(Fl_Widget* /*w*/, void* p);
- void cb_setChanPos();
- void cb_resetStartEnd();
+ void cb_setChanPos();
+ void cb_resetStartEnd();
const c::sampleEditor::Data* m_data;
geInput m_end;
geButton m_reset;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <cstdlib>
-#include "core/model/model.h"
+#include "shiftTool.h"
#include "core/const.h"
-#include "utils/gui.h"
-#include "utils/string.h"
+#include "core/model/model.h"
#include "glue/sampleEditor.h"
-#include "gui/dialogs/warnings.h"
#include "gui/dialogs/sampleEditor.h"
-#include "shiftTool.h"
-
+#include "gui/dialogs/warnings.h"
+#include "utils/gui.h"
+#include "utils/string.h"
+#include <cassert>
+#include <cstdlib>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geShiftTool::geShiftTool(const c::sampleEditor::Data& d, int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_data (nullptr)
+: gePack(x, y, Direction::HORIZONTAL)
+, m_data(nullptr)
, m_label(0, 0, 60, G_GUI_UNIT, "Shift", FL_ALIGN_LEFT)
, m_shift(0, 0, 70, G_GUI_UNIT)
, m_reset(0, 0, 70, G_GUI_UNIT, "Reset")
rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::cb_setShift(Fl_Widget* /*w*/, void* p) { ((geShiftTool*)p)->cb_setShift(); }
void geShiftTool::cb_reset(Fl_Widget* /*w*/, void* p) { ((geShiftTool*)p)->cb_reset(); }
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::cb_setShift()
{
shift(atoi(m_shift.value()));
}
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::cb_reset()
{
shift(0);
}
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
update(m_data->shift);
}
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::update(Frame shift)
{
m_shift.value(std::to_string(shift).c_str());
}
-
/* -------------------------------------------------------------------------- */
-
void geShiftTool::shift(int f)
{
- c::sampleEditor::shift(m_data->channelId, m_data->waveId, f);
+ c::sampleEditor::shift(m_data->channelId, f);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SHIFT_TOOL_H
#define GE_SHIFT_TOOL_H
-
-#include "gui/elems/basics/pack.h"
+#include "core/types.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/input.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/pack.h"
-
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class geShiftTool : public gePack
{
public:
-
geShiftTool(const c::sampleEditor::Data& d, int x, int y);
void rebuild(const c::sampleEditor::Data& d);
void update(Frame shift);
-private:
-
+ private:
static void cb_setShift(Fl_Widget* /*w*/, void* p);
static void cb_reset(Fl_Widget* /*w*/, void* p);
- void cb_setShift();
- void cb_reset();
+ void cb_setShift();
+ void cb_reset();
void shift(int f);
const c::sampleEditor::Data* m_data;
-
+
geBox m_label;
geInput m_shift;
geButton m_reset;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <cstdlib>
-#include <FL/Fl_Pack.H>
+#include "volumeTool.h"
#include "core/const.h"
#include "glue/events.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
#include "utils/gui.h"
#include "utils/math.h"
#include "utils/string.h"
-#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "volumeTool.h"
-
+#include <FL/Fl_Pack.H>
+#include <cmath>
+#include <cstdlib>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geVolumeTool::geVolumeTool(const c::sampleEditor::Data& d, int x, int y)
-: gePack (x, y, Direction::HORIZONTAL)
-, m_data (nullptr)
+: gePack(x, y, Direction::HORIZONTAL)
+, m_data(nullptr)
, m_label(0, 0, 60, G_GUI_UNIT, "Volume", FL_ALIGN_LEFT)
-, m_dial (0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, m_dial(0, 0, G_GUI_UNIT, G_GUI_UNIT)
, m_input(0, 0, 70, G_GUI_UNIT)
-{
+{
add(&m_label);
add(&m_dial);
add(&m_input);
rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void geVolumeTool::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
update(m_data->volume, /*isDial=*/false);
}
-
/* -------------------------------------------------------------------------- */
-
void geVolumeTool::update(float v, bool isDial)
{
std::string tmp = "-inf";
- float dB = u::math::linearToDB(v);
- if (dB > -INFINITY)
- tmp = u::string::fToString(dB, 2); // 2 digits
+ float dB = u::math::linearToDB(v);
+ if (dB > -INFINITY)
+ tmp = u::string::fToString(dB, 2); // 2 digits
m_input.value(tmp.c_str());
if (!isDial)
m_dial.value(v);
}
-
/* -------------------------------------------------------------------------- */
-
-void geVolumeTool::cb_setVolume (Fl_Widget* /*w*/, void* p) { ((geVolumeTool*)p)->cb_setVolume(); }
+void geVolumeTool::cb_setVolume(Fl_Widget* /*w*/, void* p) { ((geVolumeTool*)p)->cb_setVolume(); }
void geVolumeTool::cb_setVolumeNum(Fl_Widget* /*w*/, void* p) { ((geVolumeTool*)p)->cb_setVolumeNum(); }
-
/* -------------------------------------------------------------------------- */
-
void geVolumeTool::cb_setVolume()
{
c::events::setChannelVolume(m_data->channelId, m_dial.value(), Thread::MAIN);
}
-
/* -------------------------------------------------------------------------- */
-
void geVolumeTool::cb_setVolumeNum()
{
- c::events::setChannelVolume(m_data->channelId, u::math::dBtoLinear(atof(m_input.value())),
- Thread::MAIN);
+ c::events::setChannelVolume(m_data->channelId, u::math::dBtoLinear(atof(m_input.value())),
+ Thread::MAIN);
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_VOLUME_TOOL_H
#define GE_VOLUME_TOOL_H
-
-#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/pack.h"
-
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class geVolumeTool : public gePack
{
public:
-
geVolumeTool(const c::sampleEditor::Data& d, int x, int y);
void rebuild(const c::sampleEditor::Data& d);
- void update(float v, bool isDial=false);
-
-private:
+ void update(float v, bool isDial = false);
+ private:
static void cb_setVolume(Fl_Widget* /*w*/, void* p);
static void cb_setVolumeNum(Fl_Widget* /*w*/, void* p);
- void cb_setVolume();
- void cb_setVolumeNum();
+ void cb_setVolume();
+ void cb_setVolumeNum();
const c::sampleEditor::Data* m_data;
geBox m_label;
geDial m_dial;
geInput m_input;
-
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cstdint>
-#include <FL/Fl_Menu_Item.H>
-#include <FL/Fl_Menu_Button.H>
+#include "waveTools.h"
+#include "core/const.h"
#include "core/model/model.h"
#include "core/waveFx.h"
-#include "core/const.h"
#include "glue/sampleEditor.h"
-#include "gui/elems/basics/boxtypes.h"
#include "gui/dialogs/sampleEditor.h"
+#include "gui/elems/basics/boxtypes.h"
#include "waveform.h"
-#include "waveTools.h"
-
+#include <FL/Fl_Menu_Button.H>
+#include <FL/Fl_Menu_Item.H>
+#include <cstdint>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
namespace
{
TO_NEW_CHANNEL
};
-
/* -------------------------------------------------------------------------- */
-
void menuCallback_(Fl_Widget* w, void* v)
{
const geWaveTools* wt = static_cast<geWaveTools*>(w);
-
- ID channelId = wt->getChannelData().channelId;
- ID waveId = wt->getChannelData().waveId;
- Menu selectedItem = (Menu) (intptr_t) v;
-
- int a = wt->waveform->getSelectionA();
- int b = wt->waveform->getSelectionB();
-
- switch (selectedItem) {
- case Menu::CUT:
- c::sampleEditor::cut(channelId, waveId, a, b);
- break;
- case Menu::COPY:
- c::sampleEditor::copy(waveId, a, b);
- break;
- case Menu::PASTE:
- c::sampleEditor::paste(channelId, waveId, a);
- break;
- case Menu::TRIM:
- c::sampleEditor::trim(channelId, waveId, a, b);
- break;
- case Menu::SILENCE:
- c::sampleEditor::silence(channelId, waveId, a, b);
- break;
- case Menu::REVERSE:
- c::sampleEditor::reverse(channelId, waveId, a, b);
- break;
- case Menu::NORMALIZE:
- c::sampleEditor::normalize(channelId, waveId, a, b);
- break;
- case Menu::FADE_IN:
- c::sampleEditor::fade(channelId, waveId, a, b, m::wfx::Fade::IN);
- break;
- case Menu::FADE_OUT:
- c::sampleEditor::fade(channelId, waveId, a, b, m::wfx::Fade::OUT);
- break;
- case Menu::SMOOTH_EDGES:
- c::sampleEditor::smoothEdges(channelId, waveId, a, b);
- break;
- case Menu::SET_BEGIN_END:
- c::sampleEditor::setBeginEnd(channelId, a, b);
- break;
- case Menu::TO_NEW_CHANNEL:
- c::sampleEditor::toNewChannel(channelId, waveId, a, b);
- break;
+
+ ID channelId = wt->getChannelData().channelId;
+ Menu selectedItem = (Menu)(intptr_t)v;
+
+ Frame a = wt->waveform->getSelectionA();
+ Frame b = wt->waveform->getSelectionB();
+
+ switch (selectedItem)
+ {
+ case Menu::CUT:
+ c::sampleEditor::cut(channelId, a, b);
+ break;
+ case Menu::COPY:
+ c::sampleEditor::copy(channelId, a, b);
+ break;
+ case Menu::PASTE:
+ c::sampleEditor::paste(channelId, a);
+ break;
+ case Menu::TRIM:
+ c::sampleEditor::trim(channelId, a, b);
+ break;
+ case Menu::SILENCE:
+ c::sampleEditor::silence(channelId, a, b);
+ break;
+ case Menu::REVERSE:
+ c::sampleEditor::reverse(channelId, a, b);
+ break;
+ case Menu::NORMALIZE:
+ c::sampleEditor::normalize(channelId, a, b);
+ break;
+ case Menu::FADE_IN:
+ c::sampleEditor::fade(channelId, a, b, m::wfx::Fade::IN);
+ break;
+ case Menu::FADE_OUT:
+ c::sampleEditor::fade(channelId, a, b, m::wfx::Fade::OUT);
+ break;
+ case Menu::SMOOTH_EDGES:
+ c::sampleEditor::smoothEdges(channelId, a, b);
+ break;
+ case Menu::SET_BEGIN_END:
+ c::sampleEditor::setBeginEnd(channelId, a, b);
+ break;
+ case Menu::TO_NEW_CHANNEL:
+ c::sampleEditor::toNewChannel(channelId, a, b);
+ break;
}
}
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
geWaveTools::geWaveTools(int x, int y, int w, int h)
: Fl_Scroll(x, y, w, h, nullptr)
-, m_data (nullptr)
+, m_data(nullptr)
{
type(Fl_Scroll::HORIZONTAL_ALWAYS);
hscrollbar.color(G_COLOR_GREY_2);
hscrollbar.labelcolor(G_COLOR_LIGHT_1);
hscrollbar.slider(G_CUSTOM_BORDER_BOX);
- waveform = new v::geWaveform(x, y, w, h-24);
+ waveform = new v::geWaveform(x, y, w, h - 24);
}
-
-
/* -------------------------------------------------------------------------- */
-
void geWaveTools::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
waveform->rebuild(d);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveTools::refresh()
{
if (m_data->a_getPreviewStatus() == ChannelStatus::PLAY)
waveform->redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveTools::resize(int x, int y, int w, int h)
{
Fl_Widget::resize(x, y, w, h);
- if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
- waveform->resize(x, y, waveform->w(), h-24);
+ if (this->w() == w || (this->w() != w && this->h() != h))
+ { // vertical or both resize
+ waveform->resize(x, y, waveform->w(), h - 24);
waveform->rebuild(*m_data);
}
int offset = waveform->x() + waveform->w() - this->w() - this->x();
if (offset < 0)
- waveform->position(waveform->x()-offset, this->y());
+ waveform->position(waveform->x() - offset, this->y());
}
-
/* -------------------------------------------------------------------------- */
-
int geWaveTools::handle(int e)
{
- switch (e) {
- case FL_MOUSEWHEEL: {
- waveform->setZoom(Fl::event_dy() == 1 ? geWaveform::Zoom::OUT : geWaveform::Zoom::IN);
- redraw();
+ switch (e)
+ {
+ case FL_MOUSEWHEEL:
+ {
+ waveform->setZoom(Fl::event_dy() == 1 ? geWaveform::Zoom::OUT : geWaveform::Zoom::IN);
+ redraw();
+ return 1;
+ }
+ case FL_PUSH:
+ {
+ if (Fl::event_button3()) // right button
+ {
+ openMenu();
return 1;
}
- case FL_PUSH: {
- if (Fl::event_button3()) { // right button
- openMenu();
- return 1;
- }
- Fl::focus(waveform);
- }
- default:
- return Fl_Group::handle(e);
+ Fl::focus(waveform);
+ return Fl_Group::handle(e);
+ }
+ default:
+ return Fl_Group::handle(e);
}
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveTools::openMenu()
{
Fl_Menu_Item menu[] = {
- {"Cut", 0, menuCallback_, (void*) Menu::CUT},
- {"Copy", 0, menuCallback_, (void*) Menu::COPY},
- {"Paste", 0, menuCallback_, (void*) Menu::PASTE},
- {"Trim", 0, menuCallback_, (void*) Menu::TRIM},
- {"Silence", 0, menuCallback_, (void*) Menu::SILENCE},
- {"Reverse", 0, menuCallback_, (void*) Menu::REVERSE},
- {"Normalize", 0, menuCallback_, (void*) Menu::NORMALIZE},
- {"Fade in", 0, menuCallback_, (void*) Menu::FADE_IN},
- {"Fade out", 0, menuCallback_, (void*) Menu::FADE_OUT},
- {"Smooth edges", 0, menuCallback_, (void*) Menu::SMOOTH_EDGES},
- {"Set begin/end here", 0, menuCallback_, (void*) Menu::SET_BEGIN_END},
- {"Copy to new channel", 0, menuCallback_, (void*) Menu::TO_NEW_CHANNEL},
- {0}
- };
-
- if (!waveform->isSelected()) {
+ {"Cut", 0, menuCallback_, (void*)Menu::CUT, 0, 0, 0, 0, 0},
+ {"Copy", 0, menuCallback_, (void*)Menu::COPY, 0, 0, 0, 0, 0},
+ {"Paste", 0, menuCallback_, (void*)Menu::PASTE, 0, 0, 0, 0, 0},
+ {"Trim", 0, menuCallback_, (void*)Menu::TRIM, 0, 0, 0, 0, 0},
+ {"Silence", 0, menuCallback_, (void*)Menu::SILENCE, 0, 0, 0, 0, 0},
+ {"Reverse", 0, menuCallback_, (void*)Menu::REVERSE, 0, 0, 0, 0, 0},
+ {"Normalize", 0, menuCallback_, (void*)Menu::NORMALIZE, 0, 0, 0, 0, 0},
+ {"Fade in", 0, menuCallback_, (void*)Menu::FADE_IN, 0, 0, 0, 0, 0},
+ {"Fade out", 0, menuCallback_, (void*)Menu::FADE_OUT, 0, 0, 0, 0, 0},
+ {"Smooth edges", 0, menuCallback_, (void*)Menu::SMOOTH_EDGES, 0, 0, 0, 0, 0},
+ {"Set begin/end here", 0, menuCallback_, (void*)Menu::SET_BEGIN_END, 0, 0, 0, 0, 0},
+ {"Copy to new channel", 0, menuCallback_, (void*)Menu::TO_NEW_CHANNEL, 0, 0, 0, 0, 0},
+ {0}};
+
+ if (!waveform->isSelected())
+ {
menu[(int)Menu::CUT].deactivate();
- menu[(int)Menu::COPY].deactivate();
- menu[(int)Menu::TRIM].deactivate();
- menu[(int)Menu::SILENCE].deactivate();
- menu[(int)Menu::REVERSE].deactivate();
- menu[(int)Menu::NORMALIZE].deactivate();
- menu[(int)Menu::FADE_IN].deactivate();
- menu[(int)Menu::FADE_OUT].deactivate();
- menu[(int)Menu::SMOOTH_EDGES].deactivate();
- menu[(int)Menu::SET_BEGIN_END].deactivate();
- menu[(int)Menu::TO_NEW_CHANNEL].deactivate();
+ menu[(int)Menu::COPY].deactivate();
+ menu[(int)Menu::TRIM].deactivate();
+ menu[(int)Menu::SILENCE].deactivate();
+ menu[(int)Menu::REVERSE].deactivate();
+ menu[(int)Menu::NORMALIZE].deactivate();
+ menu[(int)Menu::FADE_IN].deactivate();
+ menu[(int)Menu::FADE_OUT].deactivate();
+ menu[(int)Menu::SMOOTH_EDGES].deactivate();
+ menu[(int)Menu::SET_BEGIN_END].deactivate();
+ menu[(int)Menu::TO_NEW_CHANNEL].deactivate();
}
Fl_Menu_Button b(0, 0, 100, 50);
return;
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_WAVE_TOOLS_H
#define GE_WAVE_TOOLS_H
-
#include <FL/Fl_Scroll.H>
-
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class geWaveform;
class geWaveTools : public Fl_Scroll
{
public:
-
geWaveTools(int x, int y, int w, int h);
void resize(int x, int y, int w, int h) override;
void refresh();
const c::sampleEditor::Data& getChannelData() const { return *m_data; }
-
- v::geWaveform* waveform;
-private:
+ v::geWaveform* waveform;
+ private:
void openMenu();
const c::sampleEditor::Data* m_data;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cassert>
-#include <cmath>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Menu_Button.H>
-#include "core/model/model.h"
-#include "core/wave.h"
+#include "waveform.h"
#include "core/conf.h"
#include "core/const.h"
#include "core/mixer.h"
+#include "core/model/model.h"
+#include "core/wave.h"
#include "core/waveFx.h"
#include "glue/channel.h"
#include "glue/sampleEditor.h"
-#include "utils/log.h"
#include "gui/dialogs/sampleEditor.h"
#include "gui/elems/basics/boxtypes.h"
+#include "utils/log.h"
#include "waveTools.h"
-#include "waveform.h"
-
+#include <FL/Fl_Menu_Button.H>
+#include <FL/fl_draw.H>
+#include <cassert>
+#include <cmath>
-namespace giada {
-namespace v
+namespace giada
+{
+namespace v
{
geWaveform::geWaveform(int x, int y, int w, int h)
-: Fl_Widget (x, y, w, h, nullptr)
-, m_selection {}
-, m_data (nullptr)
-, m_chanStart (0)
+: Fl_Widget(x, y, w, h, nullptr)
+, m_selection{}
+, m_data(nullptr)
+, m_chanStart(0)
, m_chanStartLit(false)
-, m_chanEnd (0)
-, m_chanEndLit (false)
-, m_pushed (false)
-, m_dragged (false)
-, m_resizedA (false)
-, m_resizedB (false)
-, m_ratio (0.0f)
+, m_chanEnd(0)
+, m_chanEndLit(false)
+, m_pushed(false)
+, m_dragged(false)
+, m_resizedA(false)
+, m_resizedB(false)
+, m_ratio(0.0f)
{
m_waveform.size = w;
m_grid.level = m::conf::conf.sampleEditorGridVal;
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::clearData()
{
m_waveform.sup.clear();
m_grid.points.clear();
}
-
/* -------------------------------------------------------------------------- */
-
int geWaveform::alloc(int datasize, bool force)
{
- /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
-
- m::model::WavesLock l(m::model::waves);
- const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
+ const m::Wave& wave = m_data->getWaveRef();
- m_ratio = wave.getSize() / (float) datasize;
+ m_ratio = wave.getBuffer().countFrames() / (float)datasize;
/* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */
- if (m_ratio < 1) {
- datasize = wave.getSize();
- m_ratio = 1;
+ if (m_ratio < 1)
+ {
+ datasize = wave.getBuffer().countFrames();
+ m_ratio = 1;
}
if (datasize == m_waveform.size && !force)
/* Frid frequency: store a grid point every 'gridFreq' frame (if grid is
enabled). TODO - this will cause round off errors, since gridFreq is integer. */
- int gridFreq = m_grid.level != 0 ? wave.getSize() / m_grid.level : 0;
+ int gridFreq = m_grid.level != 0 ? wave.getBuffer().countFrames() / m_grid.level : 0;
/* Resampling the waveform, hardcore way. Many thanks to
http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */
- for (int i = 0; i < m_waveform.size; i++) {
-
+ for (int i = 0; i < m_waveform.size; i++)
+ {
+
/* Scan the original waveform in chunks [pc, pn]. */
- int pc = i * m_ratio; // current point TODO - int until we switch to uint32_t for Wave size...
- int pn = (i+1) * m_ratio; // next point TODO - int until we switch to uint32_t for Wave size...
+ int pc = i * m_ratio; // current point TODO - int until we switch to uint32_t for Wave size...
+ int pn = (i + 1) * m_ratio; // next point TODO - int until we switch to uint32_t for Wave size...
float peaksup = 0.0f;
float peakinf = 0.0f;
- for (int k = pc; k < pn; k++) { // TODO - int until we switch to uint32_t for Wave size...
+ for (int k = pc; k < pn; k++)
+ { // TODO - int until we switch to uint32_t for Wave size...
- if (k >= wave.getSize())
+ if (k >= wave.getBuffer().countFrames())
continue;
/* Compute average of stereo signal. */
- float avg = 0.0f;
- float* frame = wave.getFrame(k);
- for (int j = 0; j < wave.getChannels(); j++)
+ float avg = 0.0f;
+ float* frame = wave.getBuffer()[k];
+ for (int j = 0; j < wave.getBuffer().countChannels(); j++)
avg += frame[j];
- avg /= wave.getChannels();
-
+ avg /= wave.getBuffer().countChannels();
+
/* Find peaks (greater and lower). */
- if (avg > peaksup) peaksup = avg;
- else if (avg <= peakinf) peakinf = avg;
+ if (avg > peaksup)
+ peaksup = avg;
+ else if (avg <= peakinf)
+ peakinf = avg;
/* Fill up grid vector. */
- if (gridFreq != 0 && (int) k % gridFreq == 0 && k != 0)
+ if (gridFreq != 0 && (int)k % gridFreq == 0 && k != 0)
m_grid.points.push_back(k);
}
// avoid window overflow
- if (m_waveform.sup[i] < y()) m_waveform.sup[i] = y();
- if (m_waveform.inf[i] > y()+h()-1) m_waveform.inf[i] = y()+h()-1;
+ if (m_waveform.sup[i] < y())
+ m_waveform.sup[i] = y();
+ if (m_waveform.inf[i] > y() + h() - 1)
+ m_waveform.inf[i] = y() + h() - 1;
}
recalcPoints();
return 1;
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::recalcPoints()
{
m_chanStart = m_data->begin;
m_chanEnd = m_data->end;
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::drawSelection()
{
- if (!isSelected())
+ if (!isSelected())
return;
int a = frameToPixel(m_selection.a) + x();
b = w() + BORDER;
if (a < b)
- fl_rectf(a, y(), b-a, h(), G_COLOR_GREY_4);
+ fl_rectf(a, y(), b - a, h(), G_COLOR_GREY_4);
else
- fl_rectf(b, y(), a-b, h(), G_COLOR_GREY_4);
+ fl_rectf(b, y(), a - b, h(), G_COLOR_GREY_4);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::drawWaveform(int from, int to)
{
int zero = y() + (h() / 2); // zero amplitude (-inf dB)
fl_color(G_COLOR_BLACK);
- for (int i=from; i<to; i++) {
+ for (int i = from; i < to; i++)
+ {
if (i >= m_waveform.size)
break;
- fl_line(i+x(), zero, i+x(), m_waveform.sup[i]);
- fl_line(i+x(), zero, i+x(), m_waveform.inf[i]);
+ fl_line(i + x(), zero, i + x(), m_waveform.sup[i]);
+ fl_line(i + x(), zero, i + x(), m_waveform.inf[i]);
}
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::drawGrid(int from, int to)
{
fl_color(G_COLOR_GREY_3);
fl_line_style(FL_DASH, 1, nullptr);
- for (int pf : m_grid.points) {
+ for (int pf : m_grid.points)
+ {
int pp = frameToPixel(pf);
if (pp > from && pp < to)
- fl_line(pp+x(), y(), pp+x(), y()+h());
+ fl_line(pp + x(), y(), pp + x(), y() + h());
}
fl_line_style(FL_SOLID, 0, nullptr);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::drawStartEndPoints()
{
/* print m_chanStart */
int lineX = frameToPixel(m_chanStart) + x();
- if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2);
- else fl_color(G_COLOR_LIGHT_1);
+ if (m_chanStartLit)
+ fl_color(G_COLOR_LIGHT_2);
+ else
+ fl_color(G_COLOR_LIGHT_1);
/* vertical line */
- fl_line(lineX, y()+1, lineX, y()+h()-2);
+ fl_line(lineX, y() + 1, lineX, y() + h() - 2);
/* print flag and avoid overflow */
- if (lineX+FLAG_WIDTH > w()+x()-2)
- fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
+ if (lineX + FLAG_WIDTH > w() + x() - 2)
+ fl_rectf(lineX, y() + h() - FLAG_HEIGHT - 1, w() - lineX + x() - 1, FLAG_HEIGHT);
else
- fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
+ fl_rectf(lineX, y() + h() - FLAG_HEIGHT - 1, FLAG_WIDTH, FLAG_HEIGHT);
/* print m_chanEnd */
lineX = frameToPixel(m_chanEnd) + x() - 1;
- if (m_chanEndLit) fl_color(G_COLOR_LIGHT_2);
- else fl_color(G_COLOR_LIGHT_1);
+ if (m_chanEndLit)
+ fl_color(G_COLOR_LIGHT_2);
+ else
+ fl_color(G_COLOR_LIGHT_1);
/* vertical line */
- fl_line(lineX, y()+1, lineX, y()+h()-2);
+ fl_line(lineX, y() + 1, lineX, y() + h() - 2);
- if (lineX-FLAG_WIDTH < x())
- fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
+ if (lineX - FLAG_WIDTH < x())
+ fl_rectf(x() + 1, y() + 1, lineX - x(), FLAG_HEIGHT);
else
- fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
+ fl_rectf(lineX - FLAG_WIDTH, y() + 1, FLAG_WIDTH, FLAG_HEIGHT);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::drawPlayHead()
{
int p = frameToPixel(m_data->a_getPreviewTracker()) + x();
fl_line(p, y() + 1, p, y() + h() - 2);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::draw()
{
assert(m_waveform.sup.size() > 0);
assert(m_waveform.inf.size() > 0);
- fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas
+ fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas
/* Draw things from 'from' (offset driven by the scrollbar) to 'to' (width of
parent window). We don't draw the entire waveform, only the visibile part. */
int from = abs(x() - parent()->x());
- int to = from + parent()->w();
+ int to = from + parent()->w();
if (x() + w() < parent()->w())
to = x() + w() - BORDER;
drawGrid(from, to);
drawPlayHead();
- fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box
-
+ fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box
+
drawStartEndPoints();
}
-
/* -------------------------------------------------------------------------- */
-
int geWaveform::handle(int e)
{
- /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
-
- m::model::WavesLock l(m::model::waves);
- const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
+ const m::Wave& wave = m_data->getWaveRef();
m_mouseX = pixelToFrame(Fl::event_x() - x());
m_mouseY = pixelToFrame(Fl::event_y() - y());
- switch (e) {
+ switch (e)
+ {
- case FL_KEYDOWN: {
- if (Fl::event_key() == ' ')
- static_cast<v::gdSampleEditor*>(window())->cb_togglePreview();
- else
- if (Fl::event_key() == FL_BackSpace)
- c::sampleEditor::setPreviewTracker(m_data->begin);
+ case FL_KEYDOWN:
+ {
+ if (Fl::event_key() == ' ')
+ static_cast<v::gdSampleEditor*>(window())->cb_togglePreview();
+ else if (Fl::event_key() == FL_BackSpace)
+ c::sampleEditor::setPreviewTracker(m_data->begin);
+ return 1;
+ }
+
+ case FL_PUSH:
+ {
+
+ if (Fl::event_clicks() > 0)
+ {
+ selectAll();
return 1;
}
- case FL_PUSH: {
+ m_pushed = true;
- if (Fl::event_clicks() > 0) {
- selectAll();
- return 1;
- }
-
- m_pushed = true;
-
- if (!mouseOnEnd() && !mouseOnStart()) {
- if (Fl::event_button3()) // let the parent (waveTools) handle this
- return 0;
- if (mouseOnSelectionA())
- m_resizedA = true;
- else
- if(mouseOnSelectionB())
- m_resizedB = true;
- else {
- m_dragged = true;
- m_selection.a = m_mouseX;
- m_selection.b = m_mouseX;
- }
+ if (!mouseOnEnd() && !mouseOnStart())
+ {
+ if (Fl::event_button3()) // let the parent (waveTools) handle this
+ return 0;
+ if (mouseOnSelectionA())
+ m_resizedA = true;
+ else if (mouseOnSelectionB())
+ m_resizedB = true;
+ else
+ {
+ m_dragged = true;
+ m_selection.a = m_mouseX;
+ m_selection.b = m_mouseX;
}
- return 1;
}
+ return 1;
+ }
- case FL_RELEASE: {
+ case FL_RELEASE:
+ {
- c::sampleEditor::setPreviewTracker(m_mouseX);
+ c::sampleEditor::setPreviewTracker(m_mouseX);
- /* If selection has been done (m_dragged or resized), make sure that point A
+ /* If selection has been done (m_dragged or resized), make sure that point A
is always lower than B. */
- if (m_dragged || m_resizedA || m_resizedB)
- fixSelection();
+ if (m_dragged || m_resizedA || m_resizedB)
+ fixSelection();
- /* Handle begin/end markers interaction. */
+ /* Handle begin/end markers interaction. */
- if (m_chanStartLit || m_chanEndLit)
- c::sampleEditor::setBeginEnd(m_data->channelId, m_chanStart, m_chanEnd);
+ if (m_chanStartLit || m_chanEndLit)
+ c::sampleEditor::setBeginEnd(m_data->channelId, m_chanStart, m_chanEnd);
- m_pushed = false;
- m_dragged = false;
- m_resizedA = false;
- m_resizedB = false;
+ m_pushed = false;
+ m_dragged = false;
+ m_resizedA = false;
+ m_resizedB = false;
- redraw();
- return 1;
- }
+ redraw();
+ return 1;
+ }
- case FL_ENTER: { // enables FL_DRAG
- return 1;
- }
+ case FL_ENTER:
+ { // enables FL_DRAG
+ return 1;
+ }
- case FL_LEAVE: {
- if (m_chanStartLit || m_chanEndLit) {
- m_chanStartLit = false;
- m_chanEndLit = false;
- redraw();
- }
- return 1;
+ case FL_LEAVE:
+ {
+ if (m_chanStartLit || m_chanEndLit)
+ {
+ m_chanStartLit = false;
+ m_chanEndLit = false;
+ redraw();
}
+ return 1;
+ }
- case FL_MOVE: {
+ case FL_MOVE:
+ {
- if (mouseOnStart()) {
- m_chanStartLit = true;
- redraw();
- }
- else
- if (m_chanStartLit) {
- m_chanStartLit = false;
- redraw();
- }
-
- if (mouseOnEnd()) {
- m_chanEndLit = true;
- redraw();
- }
- else
- if (m_chanEndLit) {
- m_chanEndLit = false;
- redraw();
- }
-
- if (mouseOnSelectionA() && isSelected())
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- else
- if (mouseOnSelectionB() && isSelected())
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ if (mouseOnStart())
+ {
+ m_chanStartLit = true;
+ redraw();
+ }
+ else if (m_chanStartLit)
+ {
+ m_chanStartLit = false;
+ redraw();
+ }
- return 1;
+ if (mouseOnEnd())
+ {
+ m_chanEndLit = true;
+ redraw();
+ }
+ else if (m_chanEndLit)
+ {
+ m_chanEndLit = false;
+ redraw();
}
- case FL_DRAG: {
+ if (mouseOnSelectionA() && isSelected())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else if (mouseOnSelectionB() && isSelected())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- /* here the mouse is on the m_chanStart tool */
+ return 1;
+ }
- if (m_chanStartLit && m_pushed) {
- m_chanStart = snap(m_mouseX);
+ case FL_DRAG:
+ {
- if (m_chanStart < 0)
- m_chanStart = 0;
- else
- if (m_chanStart >= m_chanEnd)
- m_chanStart = m_chanEnd - 2;
+ /* here the mouse is on the m_chanStart tool */
- redraw();
- }
- else
- if (m_chanEndLit && m_pushed) {
+ if (m_chanStartLit && m_pushed)
+ {
+ m_chanStart = snap(m_mouseX);
- m_chanEnd = snap(m_mouseX);
+ if (m_chanStart < 0)
+ m_chanStart = 0;
+ else if (m_chanStart >= m_chanEnd)
+ m_chanStart = m_chanEnd - 2;
- if (m_chanEnd > wave.getSize())
- m_chanEnd = wave.getSize();
- else
- if (m_chanEnd <= m_chanStart)
- m_chanEnd = m_chanStart + 2;
+ redraw();
+ }
+ else if (m_chanEndLit && m_pushed)
+ {
- redraw();
- }
+ m_chanEnd = snap(m_mouseX);
- /* Here the mouse is on the waveform, i.e. a new selection has started. */
+ if (m_chanEnd > wave.getBuffer().countFrames())
+ m_chanEnd = wave.getBuffer().countFrames();
+ else if (m_chanEnd <= m_chanStart)
+ m_chanEnd = m_chanStart + 2;
- else
- if (m_dragged) {
- m_selection.b = snap(m_mouseX);
- redraw();
- }
+ redraw();
+ }
- /* here the mouse is on a selection boundary i.e. resize */
+ /* Here the mouse is on the waveform, i.e. a new selection has started. */
- else
- if (m_resizedA || m_resizedB) {
- int pos = snap(m_mouseX);
- m_resizedA ? m_selection.a = pos : m_selection.b = pos;
- redraw();
- }
+ else if (m_dragged)
+ {
+ m_selection.b = snap(m_mouseX);
+ redraw();
+ }
- return 1;
+ /* here the mouse is on a selection boundary i.e. resize */
+
+ else if (m_resizedA || m_resizedB)
+ {
+ int pos = snap(m_mouseX);
+ m_resizedA ? m_selection.a = pos : m_selection.b = pos;
+ redraw();
}
- default:
- return Fl_Widget::handle(e);
+ return 1;
}
-}
+ default:
+ return Fl_Widget::handle(e);
+ }
+}
/* -------------------------------------------------------------------------- */
-
int geWaveform::snap(int pos)
{
// TODO use math::quantize
if (!m_grid.snap)
return pos;
- for (int pf : m_grid.points) {
+ for (int pf : m_grid.points)
+ {
if (pos >= pf - pixelToFrame(SNAPPING) &&
- pos <= pf + pixelToFrame(SNAPPING))
+ pos <= pf + pixelToFrame(SNAPPING))
{
return pf;
}
return pos;
}
-
/* -------------------------------------------------------------------------- */
-
bool geWaveform::mouseOnStart() const
{
int mouseXp = frameToPixel(m_mouseX);
int mouseYp = frameToPixel(m_mouseY);
int chanStartP = frameToPixel(m_chanStart);
- return mouseXp - (FLAG_WIDTH / 2) > chanStartP - BORDER &&
- mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH &&
- mouseYp > h() - FLAG_HEIGHT;
+ return mouseXp - (FLAG_WIDTH / 2) > chanStartP - BORDER &&
+ mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH &&
+ mouseYp > h() - FLAG_HEIGHT;
}
-
/* -------------------------------------------------------------------------- */
-
bool geWaveform::mouseOnEnd() const
{
int mouseXp = frameToPixel(m_mouseX);
int mouseYp = frameToPixel(m_mouseY);
int chanEndP = frameToPixel(m_chanEnd);
return mouseXp - (FLAG_WIDTH / 2) >= chanEndP - BORDER - FLAG_WIDTH &&
- mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER &&
- mouseYp <= FLAG_HEIGHT + 1;
+ mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER &&
+ mouseYp <= FLAG_HEIGHT + 1;
}
-
/* -------------------------------------------------------------------------- */
-
bool geWaveform::mouseOnSelectionA() const
{
int mouseXp = frameToPixel(m_mouseX);
return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2);
}
-
bool geWaveform::mouseOnSelectionB() const
{
int mouseXp = frameToPixel(m_mouseX);
return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2);
}
-
/* -------------------------------------------------------------------------- */
-
int geWaveform::pixelToFrame(int p) const
{
if (p <= 0)
return p * m_ratio;
}
-
/* -------------------------------------------------------------------------- */
-
int geWaveform::frameToPixel(int p) const
{
return ceil(p / m_ratio);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::fixSelection()
{
if (m_selection.a > m_selection.b) // inverted m_selection
c::sampleEditor::setPreviewTracker(m_selection.a);
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::clearSelection()
{
m_selection.a = 0;
- m_selection.b = 0;
+ m_selection.b = 0;
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::setZoom(Zoom z)
{
- if (!alloc(z == Zoom::IN ? m_waveform.size * G_GUI_ZOOM_FACTOR : m_waveform.size / G_GUI_ZOOM_FACTOR))
+ if (!alloc(z == Zoom::IN ? m_waveform.size * G_GUI_ZOOM_FACTOR : m_waveform.size / G_GUI_ZOOM_FACTOR))
return;
size(m_waveform.size, h());
/* Zoom to cursor. */
-
+
int newX = -frameToPixel(m_mouseX) + Fl::event_x();
if (newX > BORDER)
newX = BORDER;
|-------------] | offset < 0, smaller = false */
int parentW = parent()->w();
- int thisW = x() + w() - BORDER; // visible width, not full width
+ int thisW = x() + w() - BORDER; // visible width, not full width
if (thisW < parentW)
position(x() + parentW - thisW, y());
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::stretchToWindow()
{
int s = parent()->w();
size(s, h());
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::rebuild(const c::sampleEditor::Data& d)
{
m_data = &d;
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
bool geWaveform::smaller() const
{
return w() < parent()->w();
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::setGridLevel(int l)
{
m_grid.points.clear();
redraw();
}
-
/* -------------------------------------------------------------------------- */
-
bool geWaveform::isSelected() const
{
return m_selection.a != m_selection.b;
}
-
/* -------------------------------------------------------------------------- */
-
void geWaveform::setSnap(bool v) { m_grid.snap = v; }
bool geWaveform::getSnap() const { return m_grid.snap; }
-int geWaveform::getSize() const { return m_waveform.size; }
-
+int geWaveform::getSize() const { return m_waveform.size; }
/* -------------------------------------------------------------------------- */
-
int geWaveform::getSelectionA() const { return m_selection.a; }
int geWaveform::getSelectionB() const { return m_selection.b; }
-
void geWaveform::selectAll()
{
m_selection.a = 0;
m_selection.b = m_data->waveSize - 1;
redraw();
}
-}} // giada::v::
+} // namespace v
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_WAVEFORM_H
#define GE_WAVEFORM_H
-
-#include <vector>
-#include <FL/Fl_Widget.H>
#include "core/const.h"
+#include "core/types.h"
+#include <FL/Fl_Widget.H>
+#include <vector>
-
-namespace giada {
-namespace v
+namespace giada::c::sampleEditor
+{
+struct Data;
+}
+namespace giada::v
{
class geWaveform : public Fl_Widget
{
public:
-
#ifdef G_OS_WINDOWS
- /* Fuck... */
- #undef IN
- #undef OUT
+/* Fuck... */
+#undef IN
+#undef OUT
#endif
- enum class Zoom { IN, OUT };
+ enum class Zoom
+ {
+ IN,
+ OUT
+ };
geWaveform(int x, int y, int w, int h);
int getSelectionB() const;
bool getSnap() const;
- int getSize() const;
+ int getSize() const;
/* recalcPoints
Recomputes m_chanStart, m_chanEnd, ... */
/* setWaveId
Call this when the Wave ID has changed (e.g. after a reload). */
- void setWaveId(ID /*id*/ ) { /* TODO m_waveId = id;*/};
-
-private:
+ void setWaveId(ID /*id*/){/* TODO m_waveId = id;*/};
+ private:
static const int FLAG_WIDTH = 20;
static const int FLAG_HEIGHT = 20;
- static const int BORDER = 8; // window border <-> widget border
+ static const int BORDER = 8; // window border <-> widget border
static const int SNAPPING = 16;
/* selection
struct
{
- std::vector<int> sup; // upper part of the waveform
- std::vector<int> inf; // lower part of the waveform
- int size; // width of the waveform to draw (in pixel)
+ std::vector<int> sup; // upper part of the waveform
+ std::vector<int> inf; // lower part of the waveform
+ int size; // width of the waveform to draw (in pixel)
} m_waveform;
struct
{
- bool snap;
- int level;
+ bool snap;
+ int level;
std::vector<int> points;
} m_grid;
-
+
/* mouseOnStart/end
Is mouse on start or end flag? */
bool smaller() const;
- int pixelToFrame(int p) const; // TODO - move these to utils::, will be needed in actionEditor
- int frameToPixel(int f) const; // TODO - move these to utils::, will be needed in actionEditor
+ int pixelToFrame(int p) const; // TODO - move these to utils::, will be needed in actionEditor
+ int frameToPixel(int f) const; // TODO - move these to utils::, will be needed in actionEditor
/* fixSelection
Helper function which flattens the selection if it was made from right to left
Allocates memory for the picture. It's smart enough not to reallocate if
datasize hasn't changed, but it can be forced otherwise. */
- int alloc(int datasize, bool force=false);
+ int alloc(int datasize, bool force = false);
const c::sampleEditor::Data* m_data;
int m_mouseX;
int m_mouseY;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
-#include <FL/fl_draw.H>
+#include "soundMeter.h"
#include "core/const.h"
#include "core/kernelAudio.h"
+#include "core/types.h"
#include "utils/math.h"
-#include "soundMeter.h"
-
+#include <FL/fl_draw.H>
+#include <algorithm>
+#include <cmath>
-namespace giada {
-namespace v
+namespace giada::v
{
-geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char* l)
-: Fl_Box (x, y, w, h, l),
- mixerPeak (0.0f),
- m_dbLevelCur(0.0f),
- m_dbLevelOld(0.0f)
+namespace
{
+Pixel dbToPx_(float db, Pixel max)
+{
+ const float maxf = max;
+ return std::clamp(u::math::map(db, -G_MIN_DB_SCALE, 0.0f, 0.0f, maxf), 0.0f, maxf);
}
+} // namespace
-
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char* l)
+: Fl_Box(x, y, w, h, l)
+, mixerPeak(0.0f)
+, m_dbLevelOld(0.0f)
+{
+}
+
+/* -------------------------------------------------------------------------- */
void geSoundMeter::draw()
{
/* Compute peak level on 0.0 -> 1.0 scale. 1.0 is considered clip. */
- bool clip = std::fabs(mixerPeak) >= 1.0f ? true : false;
+ const bool clip = std::fabs(mixerPeak) >= 1.0f ? true : false;
/* dBFS (full scale) calculation, plus decay of -2dB per frame. */
- m_dbLevelCur = u::math::linearToDB(std::fabs(mixerPeak));
+ float dbLevelCur = u::math::linearToDB(std::fabs(mixerPeak));
- if (m_dbLevelCur < m_dbLevelOld && m_dbLevelOld > -G_MIN_DB_SCALE)
- m_dbLevelCur = m_dbLevelOld - 2.0f;
+ if (dbLevelCur < m_dbLevelOld && m_dbLevelOld > -G_MIN_DB_SCALE)
+ dbLevelCur = m_dbLevelOld - 2.0f;
- m_dbLevelOld = m_dbLevelCur;
+ m_dbLevelOld = dbLevelCur;
/* Paint the meter on screen. */
- float pxLevel = ((w()/G_MIN_DB_SCALE) * m_dbLevelCur) + w();
+ const int bodyCol = clip || !m::kernelAudio::isReady() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4;
- fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);
- fl_rectf(x()+1, y()+1, (int) pxLevel, h()-2, clip || !m::kernelAudio::isReady() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4);
+ fl_rectf(x() + 1, y() + 1, w() - 2, h() - 2, G_COLOR_GREY_2);
+ fl_rectf(x() + 1, y() + 1, dbToPx_(dbLevelCur, w()), h() - 2, bodyCol);
}
-}} // giada::v::
+} // namespace giada::v
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef GE_SOUND_METER_H
#define GE_SOUND_METER_H
-
#include <FL/Fl_Box.H>
-
-namespace giada {
-namespace v
+namespace giada::v
{
class geSoundMeter : public Fl_Box
{
public:
-
- geSoundMeter(int x, int y, int w, int h, const char* l=0);
+ geSoundMeter(int x, int y, int w, int h, const char* l = 0);
void draw() override;
- float mixerPeak; // peak from mixer
+ float mixerPeak; // peak from mixer
private:
-
- float m_dbLevelCur;
float m_dbLevelOld;
};
-}} // giada::v::
-
+} // namespace giada::v
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "model.h"
#include "core/patch.h"
-#include "utils/log.h"
#include "gui/dialogs/mainWindow.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/elems/mainWindow/keyboard/column.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "model.h"
-
+#include "gui/elems/mainWindow/keyboard/column.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "utils/log.h"
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace v {
-namespace model
+namespace giada::v::model
{
void store(m::patch::Patch& patch)
{
- G_MainWin->keyboard->forEachColumn([&](const geColumn& c)
- {
- patch.columns.push_back({ c.id, c.w() });
- });
+ G_MainWin->keyboard->forEachColumn([&](const geColumn& c) {
+ patch.columns.push_back({c.id, c.w()});
+ });
}
-
/* -------------------------------------------------------------------------- */
-
void load(const m::patch::Patch& patch)
{
- G_MainWin->keyboard->layout.clear();
- for (const m::patch::Column& col : patch.columns)
- G_MainWin->keyboard->layout.push_back({ col.id, col.width });
+ G_MainWin->keyboard->layout.clear();
+ for (const m::patch::Column& col : patch.columns)
+ G_MainWin->keyboard->layout.push_back({col.id, col.width});
}
-}}} // giada::v::model
+} // namespace giada::v::model
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_V_MODEL_H
#define G_V_MODEL_H
-
-namespace giada {
-namespace m { namespace patch
+namespace giada::m::patch
{
struct Patch;
-}}
-namespace v {
-namespace model
+}
+namespace giada::v::model
{
void store(m::patch::Patch& patch);
void load(const m::patch::Patch& patch);
-}}} // giada::v::model
-
+} // namespace giada::v::model
#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
+#include "updater.h"
#include "core/const.h"
#include "core/model/model.h"
#include "utils/gui.h"
-#include "updater.h"
-
+#include <FL/Fl.H>
-namespace giada {
-namespace v {
-namespace updater
+namespace giada::v::updater
{
-void update(void* /*p*/)
+void init()
{
- if (m::model::waves.changed.load() == true ||
- m::model::actions.changed.load() == true ||
- m::model::channels.changed.load() == true)
- {
- u::gui::rebuild();
- m::model::waves.changed.store(false);
- m::model::actions.changed.store(false);
- m::model::channels.changed.store(false);
- }
- else
- u::gui::refresh();
+ m::model::onSwap([](m::model::SwapType type) {
+ if (type == m::model::SwapType::NONE)
+ return;
+
+ /* This callback is fired by the updater thread, so it requires
+ synchronization with the main one. */
+
+ Fl::lock();
+ type == m::model::SwapType::HARD ? u::gui::rebuild() : u::gui::refresh();
+ Fl::unlock();
+ });
Fl::add_timeout(G_GUI_REFRESH_RATE, update, nullptr);
}
-}}} // giada::v::updater
+
+/* -------------------------------------------------------------------------- */
+
+void update(void* /*p*/)
+{
+ u::gui::refresh();
+ Fl::add_timeout(G_GUI_REFRESH_RATE, update, nullptr);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void close()
+{
+ Fl::remove_timeout(update);
+}
+} // namespace giada::v::updater
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_V_UPDATER_H
#define G_V_UPDATER_H
-
-namespace giada {
-namespace v {
-namespace updater
+namespace giada::v::updater
{
+void init();
void update(void* p);
-}}} // giada::v::updater
-
+void close();
+} // namespace giada::v::updater
#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <FL/Fl.H>
#include "core/init.h"
#include "gui/dialogs/mainWindow.h"
+#include <FL/Fl.H>
#ifdef WITH_TESTS
- #define CATCH_CONFIG_RUNNER
- #include <vector>
- #include <string>
- #include <catch2/catch.hpp>
- #include "tests/audioBuffer.cpp"
- #include "tests/rcuList.cpp"
- #include "tests/recorder.cpp"
- #include "tests/utils.cpp"
- #include "tests/wave.cpp"
- #include "tests/waveFx.cpp"
- #include "tests/waveManager.cpp"
+#define CATCH_CONFIG_RUNNER
+#include "tests/audioBuffer.cpp"
+#include "tests/recorder.cpp"
+#include "tests/utils.cpp"
+#include "tests/wave.cpp"
+#include "tests/waveFx.cpp"
+#include "tests/waveManager.cpp"
+#include <catch2/catch.hpp>
+#include <string>
+#include <vector>
#endif
-
class giada::v::gdMainWindow* G_MainWin = nullptr;
-
int main(int argc, char** argv)
{
#ifdef WITH_TESTS
giada::m::init::startup(argc, argv);
+ Fl::lock(); // Enable multithreading in FLTK
int ret = Fl::run();
giada::m::init::shutdown();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_COCOA_H
#define G_UTILS_COCOA_H
-
/* fl_xid() from FLTK returns a pointer to NSWindow, but plugins on OS X want a
pointer to NSView. The function does the hard conversion. */
//void cocoa_setWindowSize(void *p, int w, int h);
-
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#include <filesystem>
-#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
- #include <direct.h>
- #include <windows.h>
+#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
+#include <direct.h>
+#include <windows.h>
#else
- #include <unistd.h>
+#include <unistd.h>
#endif
+#include <climits>
#include <cstdarg>
-#include <sys/stat.h> // stat (fs::dirExists)
-#include <errno.h>
-#include <cstdlib>
#include <cstdint>
-#include <string>
+#include <cstdlib>
#include <cstring>
-#include <climits>
+#include <errno.h>
+#include <string>
+#include <sys/stat.h> // stat (fs::dirExists)
#ifdef __APPLE__
- #include <libgen.h> // basename unix
- #include <pwd.h> // getpwuid
+#include <libgen.h> // basename unix
+#include <pwd.h> // getpwuid
#endif
#include "core/const.h"
-#include "utils/string.h"
-#include "utils/log.h"
#include "utils/fs.h"
+#include "utils/log.h"
+#include "utils/string.h"
+namespace stdfs = std::filesystem;
-namespace giada {
-namespace u {
-namespace fs
+namespace giada::u::fs
{
bool fileExists(const std::string& s)
{
- return std::filesystem::exists(s);
+ return stdfs::exists(s);
}
-
/* -------------------------------------------------------------------------- */
-
bool isDir(const std::string& s)
{
- return std::filesystem::is_directory(s) && !isProject(s);
+ return stdfs::is_directory(s) && !isProject(s);
}
-
/* -------------------------------------------------------------------------- */
-
bool dirExists(const std::string& s)
-{
- return std::filesystem::exists(s);
+{
+ return stdfs::exists(s);
}
-
/* -------------------------------------------------------------------------- */
-
bool mkdir(const std::string& s)
{
- return dirExists(s) ? true : std::filesystem::create_directory(s);
+ return dirExists(s) ? true : stdfs::create_directory(s);
}
-
/* -------------------------------------------------------------------------- */
-
std::string getRealPath(const std::string& s)
{
- return s.empty() ? "" : std::filesystem::canonical(s).string();
+ return s.empty() || !stdfs::exists(s) ? "" : stdfs::canonical(s).string();
}
-
/* -------------------------------------------------------------------------- */
-
std::string basename(const std::string& s)
{
- return std::filesystem::path(s).filename().string();
+ return stdfs::path(s).filename().string();
}
-
/* -------------------------------------------------------------------------- */
-
std::string dirname(const std::string& s)
{
- return std::filesystem::path(s).parent_path().string();
+ return stdfs::path(s).parent_path().string();
}
-
/* -------------------------------------------------------------------------- */
-
std::string getCurrentPath()
{
- return std::filesystem::current_path().string();
+ return stdfs::current_path().string();
}
-
/* -------------------------------------------------------------------------- */
-
std::string getExt(const std::string& s)
{
- return std::filesystem::path(s).extension().string();
+ return stdfs::path(s).extension().string();
}
-
/* -------------------------------------------------------------------------- */
-
std::string stripExt(const std::string& s)
{
- return std::filesystem::path(s).replace_extension("").string();
+ return stdfs::path(s).replace_extension("").string();
}
-
/* -------------------------------------------------------------------------- */
-
bool isProject(const std::string& s)
{
/** TODO - checks too weak. */
return getExt(s) == ".gprj";
}
-
/* -------------------------------------------------------------------------- */
-
std::string stripFileUrl(const std::string& s)
{
std::string out = s;
- out = u::string::replace(out, "file://", "");
- out = u::string::replace(out, "%20", " ");
+ out = u::string::replace(out, "file://", "");
+ out = u::string::replace(out, "%20", " ");
return out;
}
-
/* -------------------------------------------------------------------------- */
-
std::string getHomePath()
{
#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
#elif defined(G_OS_MAC)
- char buf[PATH_MAX];
+ char buf[PATH_MAX];
struct passwd* pwd = getpwuid(getuid());
- if (pwd == nullptr) {
+ if (pwd == nullptr)
+ {
log::print("[getHomePath] unable to fetch user infos\n");
return "";
}
#endif
- return std::filesystem::path(buf).string();
+ return stdfs::path(buf).string();
}
-
/* -------------------------------------------------------------------------- */
-
bool isRootDir(const std::string& s)
{
- return std::filesystem::current_path().root_directory() == s;
+ return stdfs::current_path().root_directory() == s;
}
-
/* -------------------------------------------------------------------------- */
-
std::string getUpDir(const std::string& s)
{
#ifdef G_OS_WINDOWS
- // If root, let the user browse the drives list by returning "".
+ // If root, let the user browse the drives list by returning "".
if (isRootDir(s))
return "";
#endif
- return std::filesystem::path(s).parent_path().string();
+ return stdfs::path(s).parent_path().string();
}
-}}} // giada::u::fs::
\ No newline at end of file
+} // namespace giada::u::fs
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_FS_H
#define G_UTILS_FS_H
-
#include <string>
-
-namespace giada {
-namespace u {
-namespace fs
+namespace giada::u::fs
{
bool fileExists(const std::string& s);
bool dirExists(const std::string& s);
bool isRootDir(const std::string& s);
-bool isProject(const std::string& s);
-bool mkdir(const std::string& s);
+bool isProject(const std::string& s);
+bool mkdir(const std::string& s);
std::string getCurrentPath();
std::string getHomePath();
/path/to/my/directory -> /path/to/my/ */
std::string getUpDir(const std::string& s);
-}}} // giada::u::fs::
-
+} // namespace giada::u::fs
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <string>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
+#include <string>
#if defined(_WIN32)
- #include "../ext/resource.h"
+#include "../ext/resource.h"
#elif defined(__linux__) || defined(__FreeBSD__)
- #include <X11/xpm.h>
+#include <X11/xpm.h>
#endif
-#include "core/mixer.h"
-#include "core/mixerHandler.h"
#include "core/clock.h"
-#include "core/plugins/pluginHost.h"
#include "core/conf.h"
#include "core/graphics.h"
-#include "gui/dialogs/warnings.h"
-#include "gui/dialogs/mainWindow.h"
+#include "core/mixer.h"
+#include "core/mixerHandler.h"
+#include "core/plugins/pluginHost.h"
+#include "gui.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
-#include "gui/dialogs/window.h"
+#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/dialogs/window.h"
+#include "gui/elems/mainWindow/sequencer.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/mainIO.h"
#include "gui/elems/mainWindow/mainTimer.h"
#include "gui/elems/mainWindow/mainTransport.h"
-#include "gui/elems/mainWindow/beatMeter.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/sampleEditor/waveTools.h"
#include "log.h"
#include "string.h"
-#include "gui.h"
-
extern giada::v::gdMainWindow* G_MainWin;
-
-namespace giada {
-namespace u {
-namespace gui
+namespace giada
+{
+namespace u
+{
+namespace gui
{
namespace
{
int blinker_ = 0;
-} // {anonymous}
-
+} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-
void rebuildSubWindow(int wid)
{
v::gdWindow* w = getSubwindow(G_MainWin, wid);
- if(w != nullptr) // If its open
- w->rebuild();
+ if (w != nullptr) // If its open
+ w->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
void refreshSubWindow(int wid)
{
v::gdWindow* w = getSubwindow(G_MainWin, wid);
- if(w != nullptr) // If its open
- w->refresh();
+ if (w != nullptr) // If its open
+ w->refresh();
}
-
/* -------------------------------------------------------------------------- */
-
void refresh()
{
/* Update dynamic elements inside main window: in and out meters, beat meter
refreshSubWindow(WID_SAMPLE_EDITOR);
}
-
/* -------------------------------------------------------------------------- */
-
void rebuild()
{
G_MainWin->rebuild();
rebuildSubWindow(WID_ACTION_EDITOR);
}
-
/* -------------------------------------------------------------------------- */
-
bool shouldBlink()
{
return blinker_ > 6;
}
-
/* -------------------------------------------------------------------------- */
-
void updateStaticWidgets()
{
using namespace giada::m;
#ifdef WITH_VST
-// G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::StackType::MASTER_OUT).plugins.size() > 0);
-// G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::StackType::MASTER_IN).plugins.size() > 0);
-
+ // G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::StackType::MASTER_OUT).plugins.size() > 0);
+ // G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::StackType::MASTER_IN).plugins.size() > 0);
+
#endif
G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
G_MainWin->mainTimer->setQuantizer(clock::getQuantizerValue());
}
-
/* -------------------------------------------------------------------------- */
-
void updateMainWinLabel(const std::string& s)
{
std::string out = std::string(G_APP_NAME) + " - " + s;
G_MainWin->copy_label(out.c_str());
}
-
/* -------------------------------------------------------------------------- */
-
void setFavicon(v::gdWindow* w)
{
#if defined(__linux__) || defined(__FreeBSD__)
fl_open_display();
Pixmap p, mask;
XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
- (char **) giada_icon, &p, &mask, nullptr);
- w->icon((char *)p);
+ (char**)giada_icon, &p, &mask, nullptr);
+ w->icon((char*)p);
#elif defined(_WIN32)
- w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1)));
+ w->icon((char*)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1)));
#endif
}
-
/* -------------------------------------------------------------------------- */
-
void openSubWindow(v::gdWindow* parent, v::gdWindow* child, int id)
{
- if (parent->hasWindow(id)) {
+ if (parent->hasWindow(id))
+ {
u::log::print("[GU] parent has subwindow with id=%d, deleting\n", id);
parent->delSubWindow(id);
}
parent->addSubWindow(child);
}
-
/* -------------------------------------------------------------------------- */
-
void refreshActionEditor()
{
v::gdBaseActionEditor* ae = static_cast<v::gdBaseActionEditor*>(G_MainWin->getChild(WID_ACTION_EDITOR));
ae->rebuild();
}
-
/* -------------------------------------------------------------------------- */
-
v::gdWindow* getSubwindow(v::gdWindow* parent, int id)
{
if (parent->hasWindow(id))
return nullptr;
}
-
/* -------------------------------------------------------------------------- */
-
void closeAllSubwindows()
{
/* don't close WID_FILE_BROWSER, because it's the caller of this
G_MainWin->delSubWindow(WID_FX);
}
-
/* -------------------------------------------------------------------------- */
-
int getStringWidth(const std::string& s)
{
int w = 0;
return w;
}
-
/* -------------------------------------------------------------------------- */
-
std::string removeFltkChars(const std::string& s)
{
std::string out = u::string::replace(s, "/", "-");
- out = u::string::replace(out, "|", "-");
- out = u::string::replace(out, "&", "-");
- out = u::string::replace(out, "_", "-");
+ out = u::string::replace(out, "|", "-");
+ out = u::string::replace(out, "&", "-");
+ out = u::string::replace(out, "_", "-");
return out;
}
-
/* -------------------------------------------------------------------------- */
-
std::string truncate(const std::string& s, Pixel width)
{
- if (s.empty() || getStringWidth(s) <= width)
+ if (s.empty() || getStringWidth(s) <= width)
return s;
-
+
std::string tmp = s;
std::size_t size = tmp.size();
- while (getStringWidth(tmp + "...") > width) {
+ while (getStringWidth(tmp + "...") > width)
+ {
if (size == 0)
return "";
tmp.resize(--size);
/* -------------------------------------------------------------------------- */
-
int centerWindowX(int w)
{
return (Fl::w() / 2) - (w / 2);
}
-
int centerWindowY(int h)
{
return (Fl::h() / 2) - (h / 2);
}
-}}} // giada::u::gui::
+} // namespace gui
+} // namespace u
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_GUI_H
#define G_UTILS_GUI_H
-
-#include <string>
#include "core/types.h"
-
+#include <string>
namespace giada
{
{
class gdWindow;
}
-namespace u {
-namespace gui
+namespace u
+{
+namespace gui
{
/* refresh
Repaints some dynamic GUI elements. */
int centerWindowX(int w);
int centerWindowY(int h);
-}}} // giada::u::gui::
-
+} // namespace gui
+} // namespace u
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "log.h"
#include <cstdio>
#include <string>
-#include "log.h"
-
namespace giada::u::log
{
{
mode = m;
stat = true;
- if (mode == LOG_MODE_FILE) {
+ if (mode == LOG_MODE_FILE)
+ {
std::string fpath = fs::getHomePath() + G_SLASH + "giada.log";
- f = std::fopen(fpath.c_str(), "a");
- if (!f) {
+ f = std::fopen(fpath.c_str(), "a");
+ if (!f)
+ {
stat = false;
return 0;
}
return 1;
}
-
/* -------------------------------------------------------------------------- */
-
void close()
{
if (mode == LOG_MODE_FILE)
std::fclose(f);
}
-} // giada::u::log
+} // namespace giada::u::log
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_LOG_H
#define G_UTILS_LOG_H
-
+#include "core/const.h"
+#include "utils/fs.h"
#include <cstdio>
#include <string>
#include <type_traits>
#include <utility>
-#include "utils/fs.h"
-#include "core/const.h"
-
-namespace giada::u::log
+namespace giada::u::log
{
inline FILE* f;
inline int mode;
Internal utility function for string transformation. Uses forwarding references
(&&) to avoid useless string copy. */
-static constexpr auto string_to_c_str = [] (auto&& s)
-{
+static constexpr auto string_to_c_str = [](auto&& s) {
/* Remove any reference and const-ness, since the function can handle
l-value and r-value, const or not. TODO - Use std::remove_cvref instead,
when switching to C++20. */
if constexpr (std::is_same_v<std::remove_const_t<std::remove_reference_t<
- decltype(s)>>,
- std::string>)
+ decltype(s)>>,
+ std::string>)
// If the argument is a std::string return an old-style C-string
return s.c_str();
else
automatically transformed into a C-string. */
template <typename... Args>
-static void print(const char* format, Args&&... args)
+static void print(const char* format, Args&&... args)
{
if (mode == LOG_MODE_MUTE)
return;
- if (mode == LOG_MODE_FILE && stat == true) {
+ if (mode == LOG_MODE_FILE && stat == true)
+ {
// Replace any std::string in the arguments by its C-string
std::fprintf(f, format, string_to_c_str(std::forward<Args>(args))...);
#ifdef _WIN32
else
std::printf(format, string_to_c_str(std::forward<Args>(args))...);
}
-} // giada::u::log
-
+} // namespace giada::u::log
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <cmath>
#include "math.h"
+#include <cmath>
-
-namespace giada {
-namespace u {
-namespace math
+namespace giada
+{
+namespace u
+{
+namespace math
{
float linearToDB(float f)
{
return 20 * std::log10(f);
}
-
/* -------------------------------------------------------------------------- */
-
int quantize(int x, int step)
{
/* Source:
https://en.wikipedia.org/wiki/Quantization_(signal_processing)#Rounding_example */
- return step * std::floor((x / (float) step) + 0.5f);
+ return step * std::floor((x / (float)step) + 0.5f);
}
-
/* -------------------------------------------------------------------------- */
-
float dBtoLinear(float f)
{
- return std::pow(10, f/20.0f);
+ return std::pow(10, f / 20.0f);
}
-}}} // giada::u::math::
\ No newline at end of file
+} // namespace math
+} // namespace u
+} // namespace giada
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_MATH_H
#define G_UTILS_MATH_H
-
#include <type_traits>
-
-namespace giada {
-namespace u {
-namespace math
+namespace giada::u::math
{
float linearToDB(float f);
float dBtoLinear(float f);
-int quantize(int x, int step);
-
+int quantize(int x, int step);
/* -------------------------------------------------------------------------- */
{
static_assert(std::is_arithmetic_v<TI>);
static_assert(std::is_arithmetic_v<TO>);
-
- return (((x - a) / (double) (b - a)) * (z - w)) + w;
-}
+ return (((x - a) / (double)(b - a)) * (z - w)) + w;
+}
/* map (2)
Maps 'x' in range [0, b) to a new range [0, z]. */
template <typename TI, typename TO>
TO map(TI x, TI b, TO z)
{
- static_assert(std::is_arithmetic_v<TI>);
- static_assert(std::is_arithmetic_v<TO>);
-
- return static_cast<TO>((x / (double) b) * z);
+ return map(x, static_cast<TI>(0), b, static_cast<TO>(0), z);
}
-}}} // giada::u::math::
-
+} // namespace giada::u::math
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <iomanip>
-#include <cstdarg>
-#include <climits>
-#include "core/const.h"
#include "string.h"
+#include "core/const.h"
+#include <climits>
+#include <cstdarg>
+#include <iomanip>
-
-namespace giada {
-namespace u {
-namespace string
+namespace giada
+{
+namespace u
+{
+namespace string
{
/* TODO - use std::to_string() */
return out.str();
}
-
/* -------------------------------------------------------------------------- */
-
std::string trim(const std::string& s)
{
std::size_t first = s.find_first_not_of(" \n\t");
std::size_t last = s.find_last_not_of(" \n\t");
- return s.substr(first, last-first+1);
+ return s.substr(first, last - first + 1);
}
-
/* -------------------------------------------------------------------------- */
-
std::string replace(std::string in, const std::string& search, const std::string& replace)
{
std::size_t pos = 0;
- while ((pos = in.find(search, pos)) != std::string::npos) {
+ while ((pos = in.find(search, pos)) != std::string::npos)
+ {
in.replace(pos, search.length(), replace);
pos += replace.length();
}
return in;
}
-
/* -------------------------------------------------------------------------- */
-
std::string format(const char* format, ...)
{
va_list args;
va_start(args, format);
std::size_t size = vsnprintf(nullptr, 0, format, args) + 1;
va_end(args);
-
+
/* Create a new temporary char array to hold the new expanded std::string. */
std::unique_ptr<char[]> tmp(new char[size]);
return std::string(tmp.get(), tmp.get() + size - 1);
}
-
/* -------------------------------------------------------------------------- */
-
std::vector<std::string> split(std::string in, std::string sep)
{
std::vector<std::string> out;
- std::string full = in;
- std::string token = "";
- std::size_t curr = 0;
- std::size_t next = -1;
- do {
+ std::string full = in;
+ std::string token = "";
+ std::size_t curr = 0;
+ std::size_t next = -1;
+ do
+ {
curr = next + 1;
next = full.find_first_of(sep, curr);
token = full.substr(curr, next - curr);
if (token != "")
out.push_back(token);
- }
- while (next != std::string::npos);
+ } while (next != std::string::npos);
return out;
}
-}}} // giada::u::string
+} // namespace string
+} // namespace u
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_STRING_H
#define G_UTILS_STRING_H
-
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
-
-namespace giada {
-namespace u {
-namespace string
+namespace giada
+{
+namespace u
+{
+namespace string
{
template <typename T>
-std::string iToString(T t, bool hex=false)
+std::string iToString(T t, bool hex = false)
{
std::stringstream out;
if (hex)
out << std::hex << std::uppercase << t;
else
out << t;
- return out.str();
+ return out.str();
}
std::string replace(std::string in, const std::string& search,
- const std::string& replace);
+ const std::string& replace);
std::string trim(const std::string& s);
std::string format(const char* format, ...);
-}}} // giada::u::string
+} // namespace string
+} // namespace u
+} // namespace giada
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
+#include "time.h"
#include <chrono>
#include <thread>
-#include "time.h"
-
-namespace giada {
-namespace u {
-namespace time
+namespace giada
+{
+namespace u
+{
+namespace time
{
void sleep(int millisecs)
{
std::this_thread::sleep_for(std::chrono::milliseconds(millisecs));
}
-}}} // giada::u::time::
+} // namespace time
+} // namespace u
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_TIME_H
#define G_UTILS_TIME_H
-
-namespace giada {
-namespace u {
-namespace time
+namespace giada
+{
+namespace u
+{
+namespace time
{
void sleep(int millisecs);
-}}}
-
+}
+} // namespace u
+} // namespace giada
#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_VECTOR_H
#define G_UTILS_VECTOR_H
-
-#include <vector>
#include <algorithm>
#include <functional>
+#include <vector>
-
-namespace giada {
-namespace u {
-namespace vector
+namespace giada::u::vector
{
template <typename T, typename P>
std::size_t indexOf(T& v, const P& p)
return std::distance(v.begin(), std::find(v.begin(), v.end(), p));
}
+/* -------------------------------------------------------------------------- */
+
+template <typename T, typename F>
+auto findIf(T& v, F&& func)
+{
+ return std::find_if(v.begin(), v.end(), func);
+}
/* -------------------------------------------------------------------------- */
+template <typename T, typename F>
+bool has(T& v, F&& func)
+{
+ return findIf(v, func) != v.end();
+}
+
+/* -------------------------------------------------------------------------- */
template <typename T, typename F>
void removeIf(T& v, F&& func)
{
- v.erase(std::remove_if(v.begin(), v.end(), func), v.end());
+ v.erase(std::remove_if(v.begin(), v.end(), func), v.end());
}
-
template <typename T, typename V>
-void remove(T& v, V val)
+void remove(T& v, const V& o)
+{
+ v.erase(std::remove(v.begin(), v.end(), o), v.end());
+}
+
+/* -------------------------------------------------------------------------- */
+
+template <typename T, typename I>
+std::vector<T> cast(const I& i)
{
- v.erase(std::remove(v.begin(), v.end(), val), v.end());
+ return {i.begin(), i.end()};
}
-}}} // giada::u::vector::
+} // namespace giada::u::vector
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
-#include <RtMidi.h>
+#include "ver.h"
#include "core/const.h"
-#include <sndfile.h>
#include "deps/rtaudio/RtAudio.h"
-#include "ver.h"
-
+#include <RtMidi.h>
+#include <sndfile.h>
-namespace giada {
-namespace u {
-namespace ver
+namespace giada
+{
+namespace u
+{
+namespace ver
{
std::string getLibsndfileVersion()
{
return std::string(buffer);
}
-
/* -------------------------------------------------------------------------- */
-
std::string getRtAudioVersion()
{
#ifdef TESTS
#endif
}
-
/* -------------------------------------------------------------------------- */
-
std::string getRtMidiVersion()
{
#ifdef TESTS
return RtMidi::getVersion();
#endif
}
-}}} // giada::u::ver::
+} // namespace ver
+} // namespace u
+} // namespace giada
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2021 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-
#ifndef G_UTILS_VER_H
#define G_UTILS_VER_H
-
#include <string>
-
-namespace giada {
-namespace u {
-namespace ver
+namespace giada
+{
+namespace u
+{
+namespace ver
{
std::string getLibsndfileVersion();
std::string getRtAudioVersion();
std::string getRtMidiVersion();
-}}} // giada::u::ver::
-
+} // namespace ver
+} // namespace u
+} // namespace giada
#endif
-#include <memory>
#include "../src/core/audioBuffer.h"
#include <catch2/catch.hpp>
-
+#include <memory>
TEST_CASE("AudioBuffer")
{
AudioBuffer buffer;
buffer.alloc(BUFFER_SIZE, 2);
-
+
SECTION("test allocation")
{
SECTION("test mono")
REQUIRE(buffer.countFrames() == BUFFER_SIZE);
REQUIRE(buffer.countSamples() == BUFFER_SIZE);
REQUIRE(buffer.countChannels() == 1);
- }
+ }
SECTION("test stereo")
{
REQUIRE(buffer.countFrames() == BUFFER_SIZE);
REQUIRE(buffer.countSamples() == BUFFER_SIZE * 2);
REQUIRE(buffer.countChannels() == 2);
- }
+ }
buffer.free();
}
SECTION("test clear all")
- {
+ {
buffer.clear();
- for (int i=0; i<buffer.countFrames(); i++)
- for (int k=0; k<buffer.countChannels(); k++)
+ for (int i = 0; i < buffer.countFrames(); i++)
+ for (int k = 0; k < buffer.countChannels(); k++)
REQUIRE(buffer[i][k] == 0.0f);
buffer.free();
}
SECTION("test clear range")
- {
- for (int i=0; i<buffer.countFrames(); i++)
- for (int k=0; k<buffer.countChannels(); k++)
- buffer[i][k] = 1.0f;
+ {
+ for (int i = 0; i < buffer.countFrames(); i++)
+ for (int k = 0; k < buffer.countChannels(); k++)
+ buffer[i][k] = 1.0f;
buffer.clear(5, 6);
- for (int k=0; k<buffer.countChannels(); k++)
+ for (int k = 0; k < buffer.countChannels(); k++)
REQUIRE(buffer[5][k] == 0.0f);
buffer.free();
}
SECTION("test copy")
- {
- int size = BUFFER_SIZE * 2;
- float* data = new float[size];
- for (int i=0; i<size; i++)
- data[i] = (float) i;
+ {
+ AudioBuffer other(BUFFER_SIZE, 2);
+
+ for (int i = 0; i < other.countFrames(); i++)
+ for (int k = 0; k < other.countChannels(); k++)
+ other[i][k] = (float)i;
SECTION("test full copy")
- {
- buffer.copyData(data, BUFFER_SIZE);
+ {
+ buffer.set(other, 1.0f);
- REQUIRE(buffer[0][0] == 0.0f);
- REQUIRE(buffer[16][0] == 32.0f);
- REQUIRE(buffer[32][0] == 64.0f);
- REQUIRE(buffer[1024][0] == 2048.0f);
+ REQUIRE(buffer[0][0] == 0.0f);
+ REQUIRE(buffer[16][0] == 16.0f);
+ REQUIRE(buffer[128][0] == 128.0f);
+ REQUIRE(buffer[1024][0] == 1024.0f);
+ REQUIRE(buffer[BUFFER_SIZE - 1][0] == (float)BUFFER_SIZE - 1);
}
-
- delete[] data;
}
}
+++ /dev/null
-#include "../src/core/rcuList.h"
-#include "../src/core/types.h"
-#include <catch2/catch.hpp>
-
-
-using namespace giada;
-using namespace giada::m;
-
-
-TEST_CASE("RCUList")
-{
- struct Object
- {
- Object(ID id) : id(id) {}
- ID id;
- };
-
- RCUList<Object> list;
-
- REQUIRE(list.size() == 0);
- REQUIRE(list.changed == false);
-
- SECTION("test push")
- {
- list.push(std::make_unique<Object>(1));
- list.push(std::make_unique<Object>(2));
- list.push(std::make_unique<Object>(3));
-
- REQUIRE(list.size() == 3);
- REQUIRE(list.changed == true);
-
- SECTION("test retrieval")
- {
- RCUList<Object>::Lock l(list);
-
- REQUIRE(list.get(0)->id == 1);
- REQUIRE(list.get(1)->id == 2);
- REQUIRE(list.get(2)->id == 3);
- REQUIRE(list[0]->id == 1);
- REQUIRE(list[1]->id == 2);
- REQUIRE(list[2]->id == 3);
- REQUIRE(list.back()->id == 3);
- }
-
- SECTION("test iterator")
- {
- RCUList<Object>::Lock l(list);
-
- ID id = 1;
- for (Object* o : list)
- REQUIRE(o->id == id++);
- }
-
- SECTION("test const iterator")
- {
- RCUList<Object>::Lock l(list);
-
- ID id = 1;
- for (const Object* o : list)
- REQUIRE(o->id == id++);
- }
-
- SECTION("test pop")
- {
- list.pop(0);
-
- REQUIRE(list.size() == 2);
- REQUIRE(list.changed == true);
- }
-
- SECTION("test clear")
- {
- list.clear();
-
- REQUIRE(list.size() == 0);
- REQUIRE(list.changed == true);
- }
- }
-
- SECTION("test swap")
- {
- list.push(std::make_unique<Object>(1));
-
- list.swap(std::make_unique<Object>(16));
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.changed == true);
-
- RCUList<Object>::Lock l(list);
-
- REQUIRE(list.get(0)->id == 16);
- }
-}
#include "../src/core/recorder.h"
+#include "../src/core/action.h"
#include "../src/core/const.h"
#include "../src/core/types.h"
-#include "../src/core/action.h"
#include <catch2/catch.hpp>
-
TEST_CASE("recorder")
{
using namespace giada;
recorder::rec(ch, f2, e2);
recorder::clearChannel(/*channel=*/0);
-
+
REQUIRE(recorder::hasActions(/*channel=*/0) == false);
REQUIRE(recorder::hasActions(/*channel=*/1) == true);
}
{
recorder::clearActions(/*channel=*/0, MidiEvent::NOTE_ON);
recorder::clearActions(/*channel=*/0, MidiEvent::NOTE_OFF);
-
+
REQUIRE(recorder::hasActions(/*channel=*/0) == false);
}
-
SECTION("Test clear all")
{
recorder::clearAll();
#include "../src/utils/fs.h"
-#include "../src/utils/string.h"
#include "../src/utils/math.h"
+#include "../src/utils/string.h"
#include <catch2/catch.hpp>
-
TEST_CASE("u::fs")
{
using namespace giada::u;
REQUIRE(fs::getExt("tests/utils.cpp") == ".cpp");
REQUIRE(fs::stripExt("tests/utils.cpp") == "tests/utils");
#if defined(_WIN32)
- REQUIRE(fs::isRootDir("C:\\") == true);
+ REQUIRE(fs::isRootDir("\\") == true);
REQUIRE(fs::isRootDir("C:\\path\\to\\something") == false);
- REQUIRE(fs::getUpDir("C:\\path\\to\\something") == "C:\\path\\to\\");
+ REQUIRE(fs::getUpDir("C:\\path\\to\\something") == "C:\\path\\to");
REQUIRE(fs::getUpDir("C:\\path") == "C:\\");
- REQUIRE(fs::getUpDir("C:\\") == "");
+ REQUIRE(fs::getUpDir("C:\\") == "C:\\");
#else
REQUIRE(fs::isRootDir("/") == true);
REQUIRE(fs::isRootDir("/path/to/something") == false);
#endif
}
-
TEST_CASE("u::string")
{
using namespace giada::u;
REQUIRE(v.at(2) == "cool");
}
-
TEST_CASE("::math")
{
using namespace giada::u;
- REQUIRE(math::map( 0.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 0.0f);
+ REQUIRE(math::map(0.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 0.0f);
REQUIRE(math::map(30.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 1.0f);
REQUIRE(math::map(15.0f, 0.0f, 30.0f, 0.0f, 1.0f) == Approx(0.5f));
+
+ REQUIRE(math::map(0.0f, 30.0f, 1.0f) == 0.0f);
+ REQUIRE(math::map(30.0f, 30.0f, 1.0f) == 1.0f);
+ REQUIRE(math::map(15.0f, 30.0f, 1.0f) == Approx(0.5f));
}
-#include <memory>
#include "../src/core/wave.h"
#include <catch2/catch.hpp>
-
+#include <memory>
TEST_CASE("Wave")
{
static const int SAMPLE_RATE = 44100;
static const int BUFFER_SIZE = 4096;
- static const int CHANNELS = 2;
- static const int BIT_DEPTH = 32;
+ static const int CHANNELS = 2;
+ static const int BIT_DEPTH = 32;
/* Each SECTION the TEST_CASE is executed from the start. Any code between
this comment and the first SECTION macro is executed before each SECTION. */
-
SECTION("test allocation")
{
m::Wave wave(1);
wave.setPath("path/is/now/different.mp3", 5);
REQUIRE(wave.getPath() == "path/is/now/different-5.mp3");
- }
+ }
SECTION("test change name")
{
-#include <memory>
-#include "../src/core/model/model.h"
-#include "../src/core/const.h"
-#include "../src/core/wave.h"
#include "../src/core/waveFx.h"
+#include "../src/core/const.h"
#include "../src/core/types.h"
+#include "../src/core/wave.h"
#include <catch2/catch.hpp>
-
+#include <memory>
using namespace giada;
using namespace giada::m;
-
-Wave& getWave(ID id)
+TEST_CASE("waveFx")
{
- model::WavesLock l(model::waves);
- return model::get(model::waves, id);
-}
+ static const int SAMPLE_RATE = 44100;
+ static const int BUFFER_SIZE = 4000;
+ static const int BIT_DEPTH = 32;
+ Wave waveMono(0);
+ Wave waveStereo(0);
-TEST_CASE("waveFx")
-{
- static const ID WAVE_MONO_ID = 1;
- static const ID WAVE_STEREO_ID = 2;
- static const int SAMPLE_RATE = 44100;
- static const int BUFFER_SIZE = 4000;
- static const int BIT_DEPTH = 32;
-
- std::unique_ptr<Wave> waveMono = std::make_unique<Wave>(WAVE_MONO_ID);
- std::unique_ptr<Wave> waveStereo = std::make_unique<Wave>(WAVE_STEREO_ID);
-
- waveMono->alloc(BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-mono.wav");
- waveStereo->alloc(BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-stereo.wav");
-
- model::waves.clear();
- model::waves.push(std::move(waveMono));
- model::waves.push(std::move(waveStereo));
+ waveMono.alloc(BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-mono.wav");
+ waveStereo.alloc(BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample-stereo.wav");
SECTION("test mono->stereo conversion")
{
- int prevSize = getWave(WAVE_MONO_ID).getSize();
+ int prevSize = waveMono.getBuffer().countFrames();
- REQUIRE(wfx::monoToStereo(getWave(WAVE_MONO_ID)) == G_RES_OK);
- REQUIRE(getWave(WAVE_MONO_ID).getSize() == prevSize); // size does not change, channels do
- REQUIRE(getWave(WAVE_MONO_ID).getChannels() == 2);
+ REQUIRE(wfx::monoToStereo(waveMono) == G_RES_OK);
+ REQUIRE(waveMono.getBuffer().countFrames() == prevSize); // size does not change, channels do
+ REQUIRE(waveMono.getBuffer().countChannels() == 2);
SECTION("test mono->stereo conversion for already stereo wave")
{
/* Should do nothing. */
- int prevSize = getWave(WAVE_STEREO_ID).getSize();
+ int prevSize = waveStereo.getBuffer().countFrames();
- REQUIRE(wfx::monoToStereo(getWave(WAVE_STEREO_ID)) == G_RES_OK);
- REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize);
- REQUIRE(getWave(WAVE_STEREO_ID).getChannels() == 2);
+ REQUIRE(wfx::monoToStereo(waveStereo) == G_RES_OK);
+ REQUIRE(waveStereo.getBuffer().countFrames() == prevSize);
+ REQUIRE(waveStereo.getBuffer().countChannels() == 2);
}
}
{
int a = 20;
int b = 57;
- wfx::silence(getWave(WAVE_STEREO_ID).id, a, b);
+ wfx::silence(waveStereo, a, b);
- for (int i=a; i<b; i++)
- for (int k=0; k<getWave(WAVE_STEREO_ID).getChannels(); k++)
- REQUIRE(getWave(WAVE_STEREO_ID)[i][k] == 0.0f);
+ for (int i = a; i < b; i++)
+ for (int k = 0; k < waveStereo.getBuffer().countChannels(); k++)
+ REQUIRE(waveStereo.getBuffer()[i][k] == 0.0f);
}
SECTION("test cut")
{
- int a = 47;
- int b = 210;
- int range = b - a;
- int prevSize = getWave(WAVE_STEREO_ID).getSize();
+ int a = 47;
+ int b = 210;
+ int range = b - a;
+ int prevSize = waveStereo.getBuffer().countFrames();
- wfx::cut(getWave(WAVE_STEREO_ID).id, a, b);
+ wfx::cut(waveStereo, a, b);
- REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize - range);
+ REQUIRE(waveStereo.getBuffer().countFrames() == prevSize - range);
}
SECTION("test trim")
{
- int a = 47;
- int b = 210;
+ int a = 47;
+ int b = 210;
int area = b - a;
- wfx::trim(getWave(WAVE_STEREO_ID).id, a, b);
+ wfx::trim(waveStereo, a, b);
- REQUIRE(getWave(WAVE_STEREO_ID).getSize() == area);
+ REQUIRE(waveStereo.getBuffer().countFrames() == area);
}
SECTION("test fade")
int a = 47;
int b = 500;
- wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::Fade::IN);
- wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::Fade::OUT);
+ wfx::fade(waveStereo, a, b, wfx::Fade::IN);
+ wfx::fade(waveStereo, a, b, wfx::Fade::OUT);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[0] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[a][0] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[a][1] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[b][0] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[b][1] == 0.0f);
}
SECTION("test smooth")
int a = 11;
int b = 79;
- wfx::smooth(getWave(WAVE_STEREO_ID).id, a, b);
+ wfx::smooth(waveStereo, a, b);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[0] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
- REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[a][0] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[a][1] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[b][0] == 0.0f);
+ REQUIRE(waveStereo.getBuffer()[b][1] == 0.0f);
}
}
-#include <memory>
-#include <samplerate.h>
#include "../src/core/waveManager.h"
-#include "../src/core/wave.h"
#include "../src/core/const.h"
+#include "../src/core/wave.h"
#include <catch2/catch.hpp>
-
+#include <memory>
+#include <samplerate.h>
using std::string;
using namespace giada::m;
-
#define G_SAMPLE_RATE 44100
#define G_BUFFER_SIZE 4096
#define G_CHANNELS 2
-
TEST_CASE("waveManager")
{
/* Each SECTION the TEST_CASE is executed from the start. Any code between
SECTION("test creation")
{
waveManager::Result res = waveManager::createFromFile(TEST_RESOURCES_DIR "test.wav",
- /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR);
+ /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR);
REQUIRE(res.status == G_RES_OK);
REQUIRE(res.wave->getRate() == G_SAMPLE_RATE);
- REQUIRE(res.wave->getChannels() == G_CHANNELS);
+ REQUIRE(res.wave->getBuffer().countChannels() == G_CHANNELS);
REQUIRE(res.wave->isLogical() == false);
REQUIRE(res.wave->isEdited() == false);
}
SECTION("test recording")
{
- std::unique_ptr<Wave> wave = waveManager::createEmpty(G_BUFFER_SIZE,
- G_MAX_IO_CHANS, G_SAMPLE_RATE, "test.wav");
+ std::unique_ptr<Wave> wave = waveManager::createEmpty(G_BUFFER_SIZE,
+ G_MAX_IO_CHANS, G_SAMPLE_RATE, "test.wav");
REQUIRE(wave->getRate() == G_SAMPLE_RATE);
- REQUIRE(wave->getSize() == G_BUFFER_SIZE);
- REQUIRE(wave->getChannels() == G_CHANNELS);
+ REQUIRE(wave->getBuffer().countFrames() == G_BUFFER_SIZE);
+ REQUIRE(wave->getBuffer().countChannels() == G_CHANNELS);
REQUIRE(wave->isLogical() == true);
REQUIRE(wave->isEdited() == false);
}
SECTION("test resampling")
{
waveManager::Result res = waveManager::createFromFile(TEST_RESOURCES_DIR "test.wav",
- /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR);
+ /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR);
- int oldSize = res.wave->getSize();
+ int oldSize = res.wave->getBuffer().countFrames();
waveManager::resample(*res.wave.get(), 1, G_SAMPLE_RATE * 2);
-
+
REQUIRE(res.wave->getRate() == G_SAMPLE_RATE * 2);
- REQUIRE(res.wave->getSize() == oldSize * 2);
- REQUIRE(res.wave->getChannels() == G_CHANNELS);
+ REQUIRE(res.wave->getBuffer().countFrames() == oldSize * 2);
+ REQUIRE(res.wave->getBuffer().countChannels() == G_CHANNELS);
REQUIRE(res.wave->isLogical() == false);
REQUIRE(res.wave->isEdited() == false);
}